mount_msdosfs.c revision 120492
137535Sdes/*	$NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $	*/
2135546Sdes
337535Sdes/*
437535Sdes * Copyright (c) 1994 Christopher G. Demetriou
537535Sdes * All rights reserved.
637535Sdes *
737535Sdes * Redistribution and use in source and binary forms, with or without
837535Sdes * modification, are permitted provided that the following conditions
937535Sdes * are met:
1037535Sdes * 1. Redistributions of source code must retain the above copyright
1137535Sdes *    notice, this list of conditions and the following disclaimer.
1237535Sdes * 2. Redistributions in binary form must reproduce the above copyright
1337535Sdes *    notice, this list of conditions and the following disclaimer in the
1437535Sdes *    documentation and/or other materials provided with the distribution.
1563012Sdes * 3. All advertising materials mentioning features or use of this software
1637535Sdes *    must display the following acknowledgement:
1737535Sdes *      This product includes software developed by Christopher G. Demetriou.
1837535Sdes * 4. The name of the author may not be used to endorse or promote products
1937535Sdes *    derived from this software without specific prior written permission
2037535Sdes *
2137535Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2237535Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2337535Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2437535Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2537535Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2637535Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2737535Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2837535Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2984203Sdillon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3084203Sdillon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3184203Sdillon */
3263236Sdes
3363236Sdes#ifndef lint
3463236Sdesstatic const char rcsid[] =
3563236Sdes  "$FreeBSD: head/sbin/mount_msdosfs/mount_msdosfs.c 120492 2003-09-26 20:26:25Z fjoe $";
3663236Sdes#endif /* not lint */
3763236Sdes
3863236Sdes#include <sys/param.h>
3963236Sdes#include <sys/mount.h>
4063236Sdes#include <sys/stat.h>
4163236Sdes#include <sys/module.h>
4263236Sdes#include <sys/iconv.h>
4363236Sdes
4463236Sdes#include <fs/msdosfs/msdosfsmount.h>
4563236Sdes
4663236Sdes#include <ctype.h>
4763236Sdes#include <err.h>
4863236Sdes#include <grp.h>
4990267Sdes#include <locale.h>
5063236Sdes#include <pwd.h>
5163236Sdes#include <stdio.h>
5263236Sdes/* must be after stdio to declare fparseln */
5363236Sdes#include <libutil.h>
5463236Sdes#include <stdlib.h>
5563236Sdes#include <string.h>
5663236Sdes#include <sysexits.h>
5763236Sdes#include <unistd.h>
5863236Sdes
5963236Sdes#include "mntopts.h"
6063236Sdes
6163236Sdes#define TRANSITION_PERIOD_HACK
6263236Sdes
6363236Sdes/*
6437535Sdes * XXX - no way to specify "foo=<bar>"-type options; that's what we'd
6560737Sume * want for "-u", "-g", "-m", "-L", "-D", and "-W".
66186124Smurray */
6737535Sdesstatic struct mntopt mopts[] = {
6863012Sdes	MOPT_STDOPTS,
6937535Sdes	MOPT_FORCE,
7063012Sdes	MOPT_SYNC,
7160376Sdes	MOPT_UPDATE,
7260189Sdes#ifdef MSDOSFSMNT_GEMDOSFS
7337608Sdes	{ "gemdosfs", 0, MSDOSFSMNT_GEMDOSFS, 1 },
7437535Sdes#endif
7537535Sdes	{ "shortnames", 0, MSDOSFSMNT_SHORTNAME, 1 },
7637535Sdes	{ "longnames", 0, MSDOSFSMNT_LONGNAME, 1 },
7760376Sdes	{ "nowin95", 0, MSDOSFSMNT_NOWIN95, 1 },
7837535Sdes	{ NULL }
79202613Sdes};
8037535Sdes
81141958Skbyancstatic gid_t	a_gid(char *);
82141958Skbyancstatic uid_t	a_uid(char *);
83141958Skbyancstatic mode_t	a_mask(char *);
8437535Sdesstatic void	usage(void) __dead2;
8540939Sdesstatic int	set_charset(struct msdosfs_args *);
8641862Sdes
8737535Sdesint
8863012Sdesmain(argc, argv)
8963012Sdes	int argc;
9037535Sdes	char **argv;
9163012Sdes{
9263012Sdes	struct msdosfs_args args;
9363012Sdes	struct stat sb;
9463012Sdes	int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
9563012Sdes	char *dev, *dir, mntpath[MAXPATHLEN], *csp;
9663012Sdes
97186124Smurray	mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
98169386Sdes	(void)memset(&args, '\0', sizeof(args));
9963012Sdes	args.magic = MSDOSFS_ARGSMAGIC;
10087317Sdes
101125696Sdes	args.cs_win = NULL;
10263012Sdes	args.cs_dos = NULL;
10360196Sdes	args.cs_local = NULL;
10463012Sdes#ifdef TRANSITION_PERIOD_HACK
10590267Sdes	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
106169386Sdes#else
10790267Sdes	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:")) != -1) {
10863012Sdes#endif
10988771Sdes		switch (c) {
11063012Sdes#ifdef MSDOSFSMNT_GEMDOSFS
11190267Sdes		case 'G':
11263012Sdes			args.flags |= MSDOSFSMNT_GEMDOSFS;
11363012Sdes			break;
11463012Sdes#endif
11563012Sdes		case 's':
11697859Sdes			args.flags |= MSDOSFSMNT_SHORTNAME;
11737535Sdes			break;
11897858Sdes		case 'l':
11997866Sdes			args.flags |= MSDOSFSMNT_LONGNAME;
12097858Sdes			break;
12197866Sdes		case '9':
12297866Sdes			args.flags |= MSDOSFSMNT_NOWIN95;
12397866Sdes			break;
12497858Sdes		case 'u':
12597858Sdes			args.uid = a_uid(optarg);
12697858Sdes			set_uid = 1;
12763281Sdes			break;
12890267Sdes		case 'g':
12963012Sdes			args.gid = a_gid(optarg);
13037535Sdes			set_gid = 1;
13137535Sdes			break;
13237608Sdes		case 'm':
13363012Sdes			args.mask = a_mask(optarg);
13437608Sdes			set_mask = 1;
13537608Sdes			break;
136174588Sdes		case 'M':
13737608Sdes			args.dirmask = a_mask(optarg);
13890267Sdes			set_dirmask = 1;
13990267Sdes			break;
140174588Sdes		case 'L':
14190267Sdes			if (setlocale(LC_CTYPE, optarg) == NULL)
14290267Sdes				err(EX_CONFIG, "%s", optarg);
143174761Sdes			csp = strchr(optarg,'.');
14490267Sdes			if (!csp)
14590267Sdes				err(EX_CONFIG, "%s", optarg);
146174761Sdes			args.cs_local = malloc(ICONV_CSNMAXLEN);
14790267Sdes			if (args.cs_local == NULL)
14890267Sdes				err(EX_OSERR, "malloc()");
149174761Sdes			strncpy(args.cs_local,
15090267Sdes			    kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT),
151174761Sdes			    ICONV_CSNMAXLEN);
15297859Sdes			break;
15390267Sdes		case 'D':
15490267Sdes			args.cs_dos = malloc(ICONV_CSNMAXLEN);
15597859Sdes			if (args.cs_dos == NULL)
156176036Sdes				err(EX_OSERR, "malloc()");
15790267Sdes			strncpy(args.cs_dos, optarg, ICONV_CSNMAXLEN);
15890267Sdes			break;
15990267Sdes		case 'o':
16063281Sdes			getmntopts(optarg, mopts, &mntflags, &args.flags);
16190267Sdes			break;
16297859Sdes#ifdef TRANSITION_PERIOD_HACK
16397859Sdes		case 'W':
164106207Sdes			args.cs_local = malloc(ICONV_CSNMAXLEN);
16590267Sdes			if (args.cs_local == NULL)
166106207Sdes				err(EX_OSERR, "malloc()");
167106207Sdes			args.cs_dos = malloc(ICONV_CSNMAXLEN);
168106207Sdes			if (args.cs_dos == NULL)
16990267Sdes				err(EX_OSERR, "malloc()");
17063012Sdes			if (strcmp(optarg, "iso22dos") == 0) {
17190267Sdes				strcpy(args.cs_local, "ISO8859-2");
17297859Sdes				strcpy(args.cs_dos, "CP852");
17337608Sdes			} else if (strcmp(optarg, "iso72dos") == 0) {
17437608Sdes				strcpy(args.cs_local, "ISO8859-7");
17537608Sdes				strcpy(args.cs_dos, "CP737");
17697866Sdes			} else if (strcmp(optarg, "koi2dos") == 0) {
17797866Sdes				strcpy(args.cs_local, "KOI8-R");
17897866Sdes				strcpy(args.cs_dos, "CP866");
179174588Sdes			} else if (strcmp(optarg, "koi8u2dos") == 0) {
18097866Sdes				strcpy(args.cs_local, "KOI8-U");
18197866Sdes				strcpy(args.cs_dos, "CP866");
18297866Sdes			} else {
18397866Sdes				err(EX_NOINPUT, "%s", optarg);
18497866Sdes			}
18597866Sdes			break;
18697866Sdes#endif /* TRANSITION_PERIOD_HACK */
18797866Sdes		case '?':
18897866Sdes		default:
18997866Sdes			usage();
190106044Sdes			break;
19197866Sdes		}
19297866Sdes	}
19397866Sdes
19437608Sdes	if (optind + 2 != argc)
19537608Sdes		usage();
19663012Sdes
197174588Sdes	if (set_mask && !set_dirmask) {
19837535Sdes		args.dirmask = args.mask;
19997859Sdes		set_dirmask = 1;
20090267Sdes	}
20197859Sdes	else if (set_dirmask && !set_mask) {
20290267Sdes		args.mask = args.dirmask;
20390267Sdes		set_mask = 1;
20497866Sdes	}
205174588Sdes
20697866Sdes	dev = argv[optind];
207174588Sdes	dir = argv[optind + 1];
208106185Sdes
20997866Sdes	if (args.cs_local) {
210106185Sdes		if (set_charset(&args) == -1)
21197866Sdes			err(EX_OSERR, "msdosfs_iconv");
21297866Sdes		args.flags |= MSDOSFSMNT_KICONV;
21397866Sdes	} else if (args.cs_dos) {
21497866Sdes		if ((args.cs_local = malloc(ICONV_CSNMAXLEN)) == NULL)
21597859Sdes			err(EX_OSERR, "malloc()");
216174588Sdes		strcpy(args.cs_local, "ISO8859-1");
21790267Sdes		if (set_charset(&args) == -1)
21897859Sdes			err(EX_OSERR, "msdosfs_iconv");
21990267Sdes		args.flags |= MSDOSFSMNT_KICONV;
22090267Sdes	}
22197859Sdes
22290267Sdes	/*
22390267Sdes	 * Resolve the mountpoint with realpath(3) and remove unnecessary
22437535Sdes	 * slashes from the devicename if there are any.
22563012Sdes	 */
22697866Sdes	(void)checkpath(dir, mntpath);
22797866Sdes	(void)rmslashes(dev, dev);
228174588Sdes
22990267Sdes	args.fspec = dev;
230174588Sdes	args.export.ex_root = -2;	/* unchecked anyway on DOS fs */
231106185Sdes	if (mntflags & MNT_RDONLY)
23297866Sdes		args.export.ex_flags = MNT_EXRDONLY;
233106185Sdes	else
23497866Sdes		args.export.ex_flags = 0;
23590267Sdes	if (!set_gid || !set_uid || !set_mask) {
23697859Sdes		if (stat(mntpath, &sb) == -1)
23797856Sdes			err(EX_OSERR, "stat %s", mntpath);
23897856Sdes
239174588Sdes		if (!set_uid)
24097856Sdes			args.uid = sb.st_uid;
24190267Sdes		if (!set_gid)
24290267Sdes			args.gid = sb.st_gid;
24390267Sdes		if (!set_mask)
24497866Sdes			args.mask = args.dirmask =
24590267Sdes				sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
24697866Sdes	}
24737535Sdes
24837535Sdes	if (mount("msdosfs", mntpath, mntflags, &args) < 0)
24937608Sdes		err(EX_OSERR, "%s", dev);
25037608Sdes
25137608Sdes	exit (0);
25237535Sdes}
253174588Sdes
25437535Sdesgid_t
25597859Sdesa_gid(s)
25690267Sdes	char *s;
25763012Sdes{
25897859Sdes	struct group *gr;
25990267Sdes	char *gname;
26097859Sdes	gid_t gid;
26190267Sdes
26263012Sdes	if ((gr = getgrnam(s)) != NULL)
26390267Sdes		gid = gr->gr_gid;
26490267Sdes	else {
26597866Sdes		for (gname = s; *s && isdigit(*s); ++s);
266174588Sdes		if (!*s)
26790267Sdes			gid = atoi(gname);
26897866Sdes		else
26990267Sdes			errx(EX_NOUSER, "unknown group id: %s", gname);
27090267Sdes	}
271176105Sdes	return (gid);
27297866Sdes}
27390267Sdes
27437535Sdesuid_t
27597859Sdesa_uid(s)
27690267Sdes	char *s;
27790267Sdes{
27837535Sdes	struct passwd *pw;
27937535Sdes	char *uname;
28037608Sdes	uid_t uid;
28137608Sdes
28237608Sdes	if ((pw = getpwnam(s)) != NULL)
28337535Sdes		uid = pw->pw_uid;
284174588Sdes	else {
28537535Sdes		for (uname = s; *s && isdigit(*s); ++s);
28697859Sdes		if (!*s)
28790267Sdes			uid = atoi(uname);
288174588Sdes		else
28937535Sdes			errx(EX_NOUSER, "unknown user id: %s", uname);
29037535Sdes	}
29137608Sdes	return (uid);
29237608Sdes}
29337608Sdes
29437535Sdesmode_t
295174588Sdesa_mask(s)
29637535Sdes	char *s;
29797859Sdes{
29890267Sdes	int done, rv;
29963012Sdes	char *ep;
300174588Sdes
30197859Sdes	done = 0;
30297859Sdes	rv = -1;
30397859Sdes	if (*s >= '0' && *s <= '7') {
30490267Sdes		done = 1;
30537535Sdes		rv = strtol(optarg, &ep, 8);
30637535Sdes	}
30737608Sdes	if (!done || rv < 0 || *ep)
30863012Sdes		errx(EX_USAGE, "invalid file mode: %s", s);
30937608Sdes	return (rv);
31063012Sdes}
311174588Sdes
31237535Sdesvoid
31397859Sdesusage()
31490267Sdes{
31563012Sdes	fprintf(stderr, "%s\n%s\n",
316109967Sdes#ifdef TRANSITION_PERIOD_HACK
317174588Sdes	"usage: mount_msdosfs [-o options] [-u user] [-g group] [-m mask] [-s] [-l]",
31890267Sdes	"                     [-9] [-L locale] [-D dos-codepage] [-W table] bdev dir");
31990267Sdes#else
32097859Sdes	"usage: mount_msdosfs [-o options] [-u user] [-g group] [-m mask]",
32197866Sdes	"                     [-s] [-l] [-9] [-L locale] [-D dos-codepage] bdev dir");
322174588Sdes#endif
32390267Sdes	exit(EX_USAGE);
324174588Sdes}
32597859Sdes
32690267Sdesint
32790267Sdesset_charset(struct msdosfs_args *args)
32890267Sdes{
32963012Sdes	int error;
33063012Sdes
33190267Sdes	if (modfind("msdosfs_iconv") < 0)
33263012Sdes		if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) {
33363012Sdes			warnx( "cannot find or load \"msdosfs_iconv\" kernel module");
33463012Sdes			return (-1);
33563012Sdes		}
33663012Sdes
33763012Sdes	if ((args->cs_win = malloc(ICONV_CSNMAXLEN)) == NULL)
33890267Sdes		return (-1);
33990267Sdes	strncpy(args->cs_win, ENCODING_UNICODE, ICONV_CSNMAXLEN);
34090267Sdes	error = kiconv_add_xlat16_cspair(args->cs_win, args->cs_local, 0);
34190267Sdes	if (error)
34290267Sdes		return (-1);
34390267Sdes	error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_win, 0);
34490267Sdes	if (error)
34590267Sdes		return (-1);
34690267Sdes	if (args->cs_dos) {
347202613Sdes		error = kiconv_add_xlat16_cspair(args->cs_dos, args->cs_local, KICONV_FROM_UPPER);
348202613Sdes		if (error)
34985093Sdes			return (-1);
35063012Sdes		error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_dos, KICONV_LOWER);
35163012Sdes		if (error)
35263012Sdes			return (-1);
35390267Sdes	} else {
35490267Sdes		if ((args->cs_dos = malloc(ICONV_CSNMAXLEN)) == NULL)
35563012Sdes			return (-1);
35690267Sdes		strcpy(args->cs_dos, args->cs_local);
35790267Sdes		error = kiconv_add_xlat16_cspair(args->cs_local, args->cs_local,
35890267Sdes				KICONV_FROM_UPPER | KICONV_LOWER);
35990267Sdes		if (error)
36090267Sdes			return (-1);
36190267Sdes	}
362202613Sdes
36390267Sdes	return (0);
36463012Sdes}
36563012Sdes