mount_msdosfs.c revision 121429
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 121429 2003-10-23 16:09:20Z trhodes $";
36#endif /* not lint */
37
38#include <sys/param.h>
39#include <sys/mount.h>
40#include <sys/stat.h>
41#include <sys/iconv.h>
42#include <sys/linker.h>
43#include <sys/module.h>
44
45#include <fs/msdosfs/msdosfsmount.h>
46
47#include <ctype.h>
48#include <err.h>
49#include <grp.h>
50#include <locale.h>
51#include <pwd.h>
52#include <stdio.h>
53/* must be after stdio to declare fparseln */
54#include <libutil.h>
55#include <stdlib.h>
56#include <string.h>
57#include <sysexits.h>
58#include <unistd.h>
59
60#include "mntopts.h"
61
62#define TRANSITION_PERIOD_HACK
63
64/*
65 * XXX - no way to specify "foo=<bar>"-type options; that's what we'd
66 * want for "-u", "-g", "-m", "-M", "-L", "-D", and "-W".
67 */
68static struct mntopt mopts[] = {
69	MOPT_STDOPTS,
70	MOPT_FORCE,
71	MOPT_SYNC,
72	MOPT_UPDATE,
73#ifdef MSDOSFSMNT_GEMDOSFS
74	{ "gemdosfs", 0, MSDOSFSMNT_GEMDOSFS, 1 },
75#endif
76	{ "shortnames", 0, MSDOSFSMNT_SHORTNAME, 1 },
77	{ "longnames", 0, MSDOSFSMNT_LONGNAME, 1 },
78	{ "nowin95", 0, MSDOSFSMNT_NOWIN95, 1 },
79	{ NULL }
80};
81
82static gid_t	a_gid(char *);
83static uid_t	a_uid(char *);
84static mode_t	a_mask(char *);
85static void	usage(void) __dead2;
86static int	set_charset(struct msdosfs_args *);
87
88int
89main(int argc, char **argv)
90{
91	struct msdosfs_args args;
92	struct stat sb;
93	int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
94	char *dev, *dir, mntpath[MAXPATHLEN], *csp;
95
96	mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
97	(void)memset(&args, '\0', sizeof(args));
98	args.magic = MSDOSFS_ARGSMAGIC;
99
100	args.cs_win = NULL;
101	args.cs_dos = NULL;
102	args.cs_local = NULL;
103#ifdef TRANSITION_PERIOD_HACK
104	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
105#else
106	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:")) != -1) {
107#endif
108		switch (c) {
109#ifdef MSDOSFSMNT_GEMDOSFS
110		case 'G':
111			args.flags |= MSDOSFSMNT_GEMDOSFS;
112			break;
113#endif
114		case 's':
115			args.flags |= MSDOSFSMNT_SHORTNAME;
116			break;
117		case 'l':
118			args.flags |= MSDOSFSMNT_LONGNAME;
119			break;
120		case '9':
121			args.flags |= MSDOSFSMNT_NOWIN95;
122			break;
123		case 'u':
124			args.uid = a_uid(optarg);
125			set_uid = 1;
126			break;
127		case 'g':
128			args.gid = a_gid(optarg);
129			set_gid = 1;
130			break;
131		case 'm':
132			args.mask = a_mask(optarg);
133			set_mask = 1;
134			break;
135		case 'M':
136			args.dirmask = a_mask(optarg);
137			set_dirmask = 1;
138			break;
139		case 'L':
140			if (setlocale(LC_CTYPE, optarg) == NULL)
141				err(EX_CONFIG, "%s", optarg);
142			csp = strchr(optarg,'.');
143			if (!csp)
144				err(EX_CONFIG, "%s", optarg);
145			args.cs_local = malloc(ICONV_CSNMAXLEN);
146			if (args.cs_local == NULL)
147				err(EX_OSERR, "malloc()");
148			strncpy(args.cs_local,
149			    kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT),
150			    ICONV_CSNMAXLEN);
151			break;
152		case 'D':
153			args.cs_dos = malloc(ICONV_CSNMAXLEN);
154			if (args.cs_dos == NULL)
155				err(EX_OSERR, "malloc()");
156			strncpy(args.cs_dos, optarg, ICONV_CSNMAXLEN);
157			break;
158		case 'o':
159			getmntopts(optarg, mopts, &mntflags, &args.flags);
160			break;
161#ifdef TRANSITION_PERIOD_HACK
162		case 'W':
163			args.cs_local = malloc(ICONV_CSNMAXLEN);
164			if (args.cs_local == NULL)
165				err(EX_OSERR, "malloc()");
166			args.cs_dos = malloc(ICONV_CSNMAXLEN);
167			if (args.cs_dos == NULL)
168				err(EX_OSERR, "malloc()");
169			if (strcmp(optarg, "iso22dos") == 0) {
170				strcpy(args.cs_local, "ISO8859-2");
171				strcpy(args.cs_dos, "CP852");
172			} else if (strcmp(optarg, "iso72dos") == 0) {
173				strcpy(args.cs_local, "ISO8859-7");
174				strcpy(args.cs_dos, "CP737");
175			} else if (strcmp(optarg, "koi2dos") == 0) {
176				strcpy(args.cs_local, "KOI8-R");
177				strcpy(args.cs_dos, "CP866");
178			} else if (strcmp(optarg, "koi8u2dos") == 0) {
179				strcpy(args.cs_local, "KOI8-U");
180				strcpy(args.cs_dos, "CP866");
181			} else {
182				err(EX_NOINPUT, "%s", optarg);
183			}
184			break;
185#endif /* TRANSITION_PERIOD_HACK */
186		case '?':
187		default:
188			usage();
189			break;
190		}
191	}
192
193	if (optind + 2 != argc)
194		usage();
195
196	if (set_mask && !set_dirmask) {
197		args.dirmask = args.mask;
198		set_dirmask = 1;
199	}
200	else if (set_dirmask && !set_mask) {
201		args.mask = args.dirmask;
202		set_mask = 1;
203	}
204
205	dev = argv[optind];
206	dir = argv[optind + 1];
207
208	if (args.cs_local) {
209		if (set_charset(&args) == -1)
210			err(EX_OSERR, "msdosfs_iconv");
211		args.flags |= MSDOSFSMNT_KICONV;
212	} else if (args.cs_dos) {
213		if ((args.cs_local = malloc(ICONV_CSNMAXLEN)) == NULL)
214			err(EX_OSERR, "malloc()");
215		strcpy(args.cs_local, "ISO8859-1");
216		if (set_charset(&args) == -1)
217			err(EX_OSERR, "msdosfs_iconv");
218		args.flags |= MSDOSFSMNT_KICONV;
219	}
220
221	/*
222	 * Resolve the mountpoint with realpath(3) and remove unnecessary
223	 * slashes from the devicename if there are any.
224	 */
225	(void)checkpath(dir, mntpath);
226	(void)rmslashes(dev, dev);
227
228	args.fspec = dev;
229	args.export.ex_root = -2;	/* unchecked anyway on DOS fs */
230	if (mntflags & MNT_RDONLY)
231		args.export.ex_flags = MNT_EXRDONLY;
232	else
233		args.export.ex_flags = 0;
234	if (!set_gid || !set_uid || !set_mask) {
235		if (stat(mntpath, &sb) == -1)
236			err(EX_OSERR, "stat %s", mntpath);
237
238		if (!set_uid)
239			args.uid = sb.st_uid;
240		if (!set_gid)
241			args.gid = sb.st_gid;
242		if (!set_mask)
243			args.mask = args.dirmask =
244				sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
245	}
246
247	if (mount("msdosfs", mntpath, mntflags, &args) < 0)
248		err(EX_OSERR, "%s", dev);
249
250	exit (0);
251}
252
253gid_t
254a_gid(s)
255	char *s;
256{
257	struct group *gr;
258	char *gname;
259	gid_t gid;
260
261	if ((gr = getgrnam(s)) != NULL)
262		gid = gr->gr_gid;
263	else {
264		for (gname = s; *s && isdigit(*s); ++s);
265		if (!*s)
266			gid = atoi(gname);
267		else
268			errx(EX_NOUSER, "unknown group id: %s", gname);
269	}
270	return (gid);
271}
272
273uid_t
274a_uid(s)
275	char *s;
276{
277	struct passwd *pw;
278	char *uname;
279	uid_t uid;
280
281	if ((pw = getpwnam(s)) != NULL)
282		uid = pw->pw_uid;
283	else {
284		for (uname = s; *s && isdigit(*s); ++s);
285		if (!*s)
286			uid = atoi(uname);
287		else
288			errx(EX_NOUSER, "unknown user id: %s", uname);
289	}
290	return (uid);
291}
292
293mode_t
294a_mask(s)
295	char *s;
296{
297	int done, rv;
298	char *ep;
299
300	done = 0;
301	rv = -1;
302	if (*s >= '0' && *s <= '7') {
303		done = 1;
304		rv = strtol(optarg, &ep, 8);
305	}
306	if (!done || rv < 0 || *ep)
307		errx(EX_USAGE, "invalid file mode: %s", s);
308	return (rv);
309}
310
311void
312usage()
313{
314#ifdef TRANSITION_PERIOD_HACK
315	fprintf(stderr, "%s\n%s\n%s\n",
316	"usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]",
317	"                     [-M mask] [-m mask] [-o options] [-u uid]",
318	"		      [-W table] special node");
319#else
320	fprintf(stderr, "%s\n%s\n%s\n",
321	"usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]",
322	"                     [-M mask] [-m mask] [-o options] [-u uid]",
323	"		      special node");
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