mount_msdosfs.c revision 121360
1/*	$NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $	*/
2
3/*
4 * Copyright (c) 1994 Christopher G. Demetriou
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by Christopher G. Demetriou.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char rcsid[] =
35  "$FreeBSD: head/sbin/mount_msdosfs/mount_msdosfs.c 121360 2003-10-22 19:50:57Z trhodes $";
36#endif /* not lint */
37
38#include <sys/param.h>
39#include <sys/mount.h>
40#include <sys/stat.h>
41#include <sys/module.h>
42#include <sys/iconv.h>
43
44#include <fs/msdosfs/msdosfsmount.h>
45
46#include <ctype.h>
47#include <err.h>
48#include <grp.h>
49#include <locale.h>
50#include <pwd.h>
51#include <stdio.h>
52/* must be after stdio to declare fparseln */
53#include <libutil.h>
54#include <stdlib.h>
55#include <string.h>
56#include <sysexits.h>
57#include <unistd.h>
58
59#include "mntopts.h"
60
61#define TRANSITION_PERIOD_HACK
62
63/*
64 * XXX - no way to specify "foo=<bar>"-type options; that's what we'd
65 * want for "-u", "-g", "-m", "-M", "-L", "-D", and "-W".
66 */
67static struct mntopt mopts[] = {
68	MOPT_STDOPTS,
69	MOPT_FORCE,
70	MOPT_SYNC,
71	MOPT_UPDATE,
72#ifdef MSDOSFSMNT_GEMDOSFS
73	{ "gemdosfs", 0, MSDOSFSMNT_GEMDOSFS, 1 },
74#endif
75	{ "shortnames", 0, MSDOSFSMNT_SHORTNAME, 1 },
76	{ "longnames", 0, MSDOSFSMNT_LONGNAME, 1 },
77	{ "nowin95", 0, MSDOSFSMNT_NOWIN95, 1 },
78	{ NULL }
79};
80
81static gid_t	a_gid(char *);
82static uid_t	a_uid(char *);
83static mode_t	a_mask(char *);
84static void	usage(void) __dead2;
85static int	set_charset(struct msdosfs_args *);
86
87int
88main(argc, argv)
89	int argc;
90	char **argv;
91{
92	struct msdosfs_args args;
93	struct stat sb;
94	int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
95	char *dev, *dir, mntpath[MAXPATHLEN], *csp;
96
97	mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
98	(void)memset(&args, '\0', sizeof(args));
99	args.magic = MSDOSFS_ARGSMAGIC;
100
101	args.cs_win = NULL;
102	args.cs_dos = NULL;
103	args.cs_local = NULL;
104#ifdef TRANSITION_PERIOD_HACK
105	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
106#else
107	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:")) != -1) {
108#endif
109		switch (c) {
110#ifdef MSDOSFSMNT_GEMDOSFS
111		case 'G':
112			args.flags |= MSDOSFSMNT_GEMDOSFS;
113			break;
114#endif
115		case 's':
116			args.flags |= MSDOSFSMNT_SHORTNAME;
117			break;
118		case 'l':
119			args.flags |= MSDOSFSMNT_LONGNAME;
120			break;
121		case '9':
122			args.flags |= MSDOSFSMNT_NOWIN95;
123			break;
124		case 'u':
125			args.uid = a_uid(optarg);
126			set_uid = 1;
127			break;
128		case 'g':
129			args.gid = a_gid(optarg);
130			set_gid = 1;
131			break;
132		case 'm':
133			args.mask = a_mask(optarg);
134			set_mask = 1;
135			break;
136		case 'M':
137			args.dirmask = a_mask(optarg);
138			set_dirmask = 1;
139			break;
140		case 'L':
141			if (setlocale(LC_CTYPE, optarg) == NULL)
142				err(EX_CONFIG, "%s", optarg);
143			csp = strchr(optarg,'.');
144			if (!csp)
145				err(EX_CONFIG, "%s", optarg);
146			args.cs_local = malloc(ICONV_CSNMAXLEN);
147			if (args.cs_local == NULL)
148				err(EX_OSERR, "malloc()");
149			strncpy(args.cs_local,
150			    kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT),
151			    ICONV_CSNMAXLEN);
152			break;
153		case 'D':
154			args.cs_dos = malloc(ICONV_CSNMAXLEN);
155			if (args.cs_dos == NULL)
156				err(EX_OSERR, "malloc()");
157			strncpy(args.cs_dos, optarg, ICONV_CSNMAXLEN);
158			break;
159		case 'o':
160			getmntopts(optarg, mopts, &mntflags, &args.flags);
161			break;
162#ifdef TRANSITION_PERIOD_HACK
163		case 'W':
164			args.cs_local = malloc(ICONV_CSNMAXLEN);
165			if (args.cs_local == NULL)
166				err(EX_OSERR, "malloc()");
167			args.cs_dos = malloc(ICONV_CSNMAXLEN);
168			if (args.cs_dos == NULL)
169				err(EX_OSERR, "malloc()");
170			if (strcmp(optarg, "iso22dos") == 0) {
171				strcpy(args.cs_local, "ISO8859-2");
172				strcpy(args.cs_dos, "CP852");
173			} else if (strcmp(optarg, "iso72dos") == 0) {
174				strcpy(args.cs_local, "ISO8859-7");
175				strcpy(args.cs_dos, "CP737");
176			} else if (strcmp(optarg, "koi2dos") == 0) {
177				strcpy(args.cs_local, "KOI8-R");
178				strcpy(args.cs_dos, "CP866");
179			} else if (strcmp(optarg, "koi8u2dos") == 0) {
180				strcpy(args.cs_local, "KOI8-U");
181				strcpy(args.cs_dos, "CP866");
182			} else {
183				err(EX_NOINPUT, "%s", optarg);
184			}
185			break;
186#endif /* TRANSITION_PERIOD_HACK */
187		case '?':
188		default:
189			usage();
190			break;
191		}
192	}
193
194	if (optind + 2 != argc)
195		usage();
196
197	if (set_mask && !set_dirmask) {
198		args.dirmask = args.mask;
199		set_dirmask = 1;
200	}
201	else if (set_dirmask && !set_mask) {
202		args.mask = args.dirmask;
203		set_mask = 1;
204	}
205
206	dev = argv[optind];
207	dir = argv[optind + 1];
208
209	if (args.cs_local) {
210		if (set_charset(&args) == -1)
211			err(EX_OSERR, "msdosfs_iconv");
212		args.flags |= MSDOSFSMNT_KICONV;
213	} else if (args.cs_dos) {
214		if ((args.cs_local = malloc(ICONV_CSNMAXLEN)) == NULL)
215			err(EX_OSERR, "malloc()");
216		strcpy(args.cs_local, "ISO8859-1");
217		if (set_charset(&args) == -1)
218			err(EX_OSERR, "msdosfs_iconv");
219		args.flags |= MSDOSFSMNT_KICONV;
220	}
221
222	/*
223	 * Resolve the mountpoint with realpath(3) and remove unnecessary
224	 * slashes from the devicename if there are any.
225	 */
226	(void)checkpath(dir, mntpath);
227	(void)rmslashes(dev, dev);
228
229	args.fspec = dev;
230	args.export.ex_root = -2;	/* unchecked anyway on DOS fs */
231	if (mntflags & MNT_RDONLY)
232		args.export.ex_flags = MNT_EXRDONLY;
233	else
234		args.export.ex_flags = 0;
235	if (!set_gid || !set_uid || !set_mask) {
236		if (stat(mntpath, &sb) == -1)
237			err(EX_OSERR, "stat %s", mntpath);
238
239		if (!set_uid)
240			args.uid = sb.st_uid;
241		if (!set_gid)
242			args.gid = sb.st_gid;
243		if (!set_mask)
244			args.mask = args.dirmask =
245				sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
246	}
247
248	if (mount("msdosfs", mntpath, mntflags, &args) < 0)
249		err(EX_OSERR, "%s", dev);
250
251	exit (0);
252}
253
254gid_t
255a_gid(s)
256	char *s;
257{
258	struct group *gr;
259	char *gname;
260	gid_t gid;
261
262	if ((gr = getgrnam(s)) != NULL)
263		gid = gr->gr_gid;
264	else {
265		for (gname = s; *s && isdigit(*s); ++s);
266		if (!*s)
267			gid = atoi(gname);
268		else
269			errx(EX_NOUSER, "unknown group id: %s", gname);
270	}
271	return (gid);
272}
273
274uid_t
275a_uid(s)
276	char *s;
277{
278	struct passwd *pw;
279	char *uname;
280	uid_t uid;
281
282	if ((pw = getpwnam(s)) != NULL)
283		uid = pw->pw_uid;
284	else {
285		for (uname = s; *s && isdigit(*s); ++s);
286		if (!*s)
287			uid = atoi(uname);
288		else
289			errx(EX_NOUSER, "unknown user id: %s", uname);
290	}
291	return (uid);
292}
293
294mode_t
295a_mask(s)
296	char *s;
297{
298	int done, rv;
299	char *ep;
300
301	done = 0;
302	rv = -1;
303	if (*s >= '0' && *s <= '7') {
304		done = 1;
305		rv = strtol(optarg, &ep, 8);
306	}
307	if (!done || rv < 0 || *ep)
308		errx(EX_USAGE, "invalid file mode: %s", s);
309	return (rv);
310}
311
312void
313usage()
314{
315#ifdef TRANSITION_PERIOD_HACK
316	fprintf(stderr, "%s\n%s\n%s\n",
317	"usage: mount_msdosfs [-o options] [-u user] [-g group] [-m mask] [-M mask]",
318	"                     [-s] [-l] [-9] [-L locale] [-D dos-codepage] [-W table]",
319	"                     bdev dir");
320#else
321	fprintf(stderr, "%s\n%s\n",
322	"usage: mount_msdosfs [-o options] [-u user] [-g group] [-m mask] [-M mask]",
323	"                     [-s] [-l] [-9] [-L locale] [-D dos-codepage] bdev dir");
324#endif
325	exit(EX_USAGE);
326}
327
328int
329set_charset(struct msdosfs_args *args)
330{
331	int error;
332
333	if (modfind("msdosfs_iconv") < 0)
334		if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) {
335			warnx( "cannot find or load \"msdosfs_iconv\" kernel module");
336			return (-1);
337		}
338
339	if ((args->cs_win = malloc(ICONV_CSNMAXLEN)) == NULL)
340		return (-1);
341	strncpy(args->cs_win, ENCODING_UNICODE, ICONV_CSNMAXLEN);
342	error = kiconv_add_xlat16_cspair(args->cs_win, args->cs_local, 0);
343	if (error)
344		return (-1);
345	error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_win, 0);
346	if (error)
347		return (-1);
348	if (args->cs_dos) {
349		error = kiconv_add_xlat16_cspair(args->cs_dos, args->cs_local, KICONV_FROM_UPPER);
350		if (error)
351			return (-1);
352		error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_dos, KICONV_LOWER);
353		if (error)
354			return (-1);
355	} else {
356		if ((args->cs_dos = malloc(ICONV_CSNMAXLEN)) == NULL)
357			return (-1);
358		strcpy(args->cs_dos, args->cs_local);
359		error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_local,
360				KICONV_FROM_UPPER | KICONV_LOWER);
361		if (error)
362			return (-1);
363	}
364
365	return (0);
366}
367