mount_msdosfs.c revision 152974
1191983Sweongyo/*	$NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $	*/
2191983Sweongyo
3191983Sweongyo/*
4191983Sweongyo * Copyright (c) 1994 Christopher G. Demetriou
5191983Sweongyo * All rights reserved.
6191983Sweongyo *
7191983Sweongyo * Redistribution and use in source and binary forms, with or without
8191983Sweongyo * modification, are permitted provided that the following conditions
9191983Sweongyo * are met:
10191983Sweongyo * 1. Redistributions of source code must retain the above copyright
11191983Sweongyo *    notice, this list of conditions and the following disclaimer.
12191983Sweongyo * 2. Redistributions in binary form must reproduce the above copyright
13191983Sweongyo *    notice, this list of conditions and the following disclaimer in the
14191983Sweongyo *    documentation and/or other materials provided with the distribution.
15191983Sweongyo * 3. All advertising materials mentioning features or use of this software
16191983Sweongyo *    must display the following acknowledgement:
17191983Sweongyo *      This product includes software developed by Christopher G. Demetriou.
18191983Sweongyo * 4. The name of the author may not be used to endorse or promote products
19191983Sweongyo *    derived from this software without specific prior written permission
20191983Sweongyo *
21191983Sweongyo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22191983Sweongyo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23191983Sweongyo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24191983Sweongyo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25191983Sweongyo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26191983Sweongyo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27191983Sweongyo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28191983Sweongyo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29191983Sweongyo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30191983Sweongyo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31191983Sweongyo */
32191983Sweongyo
33191983Sweongyo#ifndef lint
34191983Sweongyostatic const char rcsid[] =
35191983Sweongyo  "$FreeBSD: head/sbin/mount_msdosfs/mount_msdosfs.c 152974 2005-12-01 00:18:48Z avatar $";
36191983Sweongyo#endif /* not lint */
37191983Sweongyo
38191983Sweongyo#include <sys/param.h>
39191983Sweongyo#include <sys/mount.h>
40191983Sweongyo#include <sys/stat.h>
41191983Sweongyo#include <sys/iconv.h>
42191983Sweongyo#include <sys/linker.h>
43191983Sweongyo#include <sys/module.h>
44191983Sweongyo
45191983Sweongyo#include <ctype.h>
46191983Sweongyo#include <err.h>
47191983Sweongyo#include <grp.h>
48191983Sweongyo#include <locale.h>
49191983Sweongyo#include <pwd.h>
50191983Sweongyo#include <stdio.h>
51194677Sthompsa/* must be after stdio to declare fparseln */
52191983Sweongyo#include <libutil.h>
53191983Sweongyo#include <stdlib.h>
54191983Sweongyo#include <string.h>
55191983Sweongyo#include <sysexits.h>
56191983Sweongyo#include <unistd.h>
57191983Sweongyo
58191983Sweongyo#include "mntopts.h"
59191983Sweongyo
60191983Sweongyostatic struct mntopt mopts[] = {
61191983Sweongyo	MOPT_STDOPTS,
62191983Sweongyo	MOPT_FORCE,
63191983Sweongyo	MOPT_SYNC,
64191983Sweongyo	MOPT_UPDATE,
65191983Sweongyo	MOPT_END
66191983Sweongyo};
67191983Sweongyo
68191983Sweongyostatic gid_t	a_gid(char *);
69191983Sweongyostatic uid_t	a_uid(char *);
70191983Sweongyostatic mode_t	a_mask(char *);
71191983Sweongyostatic void	usage(void) __dead2;
72191983Sweongyostatic int	set_charset(struct iovec **iov, int *iovlen, const char *, const char *);
73248085Smarius
74191983Sweongyoint
75191983Sweongyomain(int argc, char **argv)
76191983Sweongyo{
77191983Sweongyo	struct iovec *iov = NULL;
78242775Shselasky	int iovlen = 0;
79191983Sweongyo	struct stat sb;
80191983Sweongyo	int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
81191983Sweongyo	int optflags = 0;
82191983Sweongyo	char *dev, *dir, mntpath[MAXPATHLEN], *csp;
83191983Sweongyo	char fstype[] = "msdosfs";
84191983Sweongyo	char *cs_dos = NULL;
85191983Sweongyo	char *cs_local = NULL;
86191983Sweongyo	mode_t mask = 0, dirmask = 0;
87191983Sweongyo	uid_t uid = 0;
88191983Sweongyo	gid_t gid = 0;
89191983Sweongyo	getmnt_silent = 1;
90191983Sweongyo
91191983Sweongyo	mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
92191983Sweongyo
93191983Sweongyo	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
94191983Sweongyo		switch (c) {
95191983Sweongyo		case 's':
96191983Sweongyo			build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1);
97191983Sweongyo			break;
98191983Sweongyo		case 'l':
99191983Sweongyo			build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1);
100191983Sweongyo			break;
101191983Sweongyo		case '9':
102191983Sweongyo			build_iovec_argf(&iov, &iovlen, "nowin95", "", (size_t)-1);
103191983Sweongyo			break;
104191983Sweongyo		case 'u':
105191983Sweongyo			uid = a_uid(optarg);
106191983Sweongyo			set_uid = 1;
107191983Sweongyo			break;
108191983Sweongyo		case 'g':
109191983Sweongyo			gid = a_gid(optarg);
110191983Sweongyo			set_gid = 1;
111191983Sweongyo			break;
112191983Sweongyo		case 'm':
113191983Sweongyo			mask = a_mask(optarg);
114191983Sweongyo			set_mask = 1;
115191983Sweongyo			break;
116191983Sweongyo		case 'M':
117191983Sweongyo			dirmask = a_mask(optarg);
118191983Sweongyo			set_dirmask = 1;
119192984Sthompsa			break;
120191983Sweongyo		case 'L': {
121191983Sweongyo			const char *quirk = NULL;
122192984Sthompsa			if (setlocale(LC_CTYPE, optarg) == NULL)
123191983Sweongyo				err(EX_CONFIG, "%s", optarg);
124191983Sweongyo			csp = strchr(optarg,'.');
125191983Sweongyo			if (!csp)
126191983Sweongyo				err(EX_CONFIG, "%s", optarg);
127191983Sweongyo			quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT);
128191983Sweongyo			build_iovec_argf(&iov, &iovlen, "cs_local", quirk);
129191983Sweongyo			cs_local = strdup(quirk);
130191983Sweongyo			}
131191983Sweongyo			break;
132191983Sweongyo		case 'D':
133191983Sweongyo			cs_dos = strdup(optarg);
134191983Sweongyo			build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
135191983Sweongyo			break;
136191983Sweongyo		case 'o': {
137191983Sweongyo			char *p = NULL;
138191983Sweongyo			char *val = strdup("");
139191983Sweongyo			getmntopts(optarg, mopts, &mntflags, &optflags);
140191983Sweongyo			p = strchr(optarg, '=');
141234753Sdim			if (p != NULL) {
142234753Sdim				free(val);
143234753Sdim				*p = '\0';
144191983Sweongyo				val = p + 1;
145191983Sweongyo			}
146191983Sweongyo			build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
147191983Sweongyo			}
148191983Sweongyo			break;
149191983Sweongyo		case 'W':
150191983Sweongyo			if (strcmp(optarg, "iso22dos") == 0) {
151191983Sweongyo				cs_local = strdup("ISO8859-2");
152191983Sweongyo				cs_dos = strdup("CP852");
153191983Sweongyo			} else if (strcmp(optarg, "iso72dos") == 0) {
154191983Sweongyo				cs_local = strdup("ISO8859-7");
155191983Sweongyo				cs_dos = strdup("CP737");
156191983Sweongyo			} else if (strcmp(optarg, "koi2dos") == 0) {
157191983Sweongyo				cs_local = strdup("KOI8-R");
158191983Sweongyo				cs_dos = strdup("CP866");
159191983Sweongyo			} else if (strcmp(optarg, "koi8u2dos") == 0) {
160191983Sweongyo				cs_local = strdup("KOI8-U");
161191983Sweongyo				cs_dos = strdup("CP866");
162191983Sweongyo			} else {
163191983Sweongyo				err(EX_NOINPUT, "%s", optarg);
164191983Sweongyo			}
165191983Sweongyo			build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1);
166191983Sweongyo			build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
167191983Sweongyo			break;
168191983Sweongyo		case '?':
169191983Sweongyo		default:
170191983Sweongyo			usage();
171191983Sweongyo			break;
172191983Sweongyo		}
173223486Shselasky	}
174191983Sweongyo
175191983Sweongyo	if (optind + 2 != argc)
176191983Sweongyo		usage();
177191983Sweongyo
178191983Sweongyo	if (set_mask && !set_dirmask) {
179191983Sweongyo		dirmask = mask;
180191983Sweongyo		set_dirmask = 1;
181191983Sweongyo	}
182191983Sweongyo	else if (set_dirmask && !set_mask) {
183191983Sweongyo		mask = dirmask;
184191983Sweongyo		set_mask = 1;
185213804Shselasky	}
186191983Sweongyo
187191983Sweongyo	dev = argv[optind];
188209447Sthompsa	dir = argv[optind + 1];
189191983Sweongyo
190191983Sweongyo	if (cs_local != NULL) {
191191983Sweongyo		if (set_charset(&iov, &iovlen, cs_local, cs_dos) == -1)
192191983Sweongyo			err(EX_OSERR, "msdosfs_iconv");
193191983Sweongyo		build_iovec_argf(&iov, &iovlen, "kiconv", "");
194191983Sweongyo	} else if (cs_dos != NULL) {
195191983Sweongyo		build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1");
196193045Sthompsa		if (set_charset(&iov, &iovlen, "ISO8859-1", cs_dos) == -1)
197193045Sthompsa			err(EX_OSERR, "msdosfs_iconv");
198191983Sweongyo		build_iovec_argf(&iov, &iovlen, "kiconv", "");
199192984Sthompsa	}
200191983Sweongyo
201191983Sweongyo	/*
202191983Sweongyo	 * Resolve the mountpoint with realpath(3) and remove unnecessary
203191983Sweongyo	 * slashes from the devicename if there are any.
204259454Shselasky	 */
205191983Sweongyo	(void)checkpath(dir, mntpath);
206191983Sweongyo	(void)rmslashes(dev, dev);
207191983Sweongyo
208191983Sweongyo	if (!set_gid || !set_uid || !set_mask) {
209191983Sweongyo		if (stat(mntpath, &sb) == -1)
210191983Sweongyo			err(EX_OSERR, "stat %s", mntpath);
211191983Sweongyo
212191983Sweongyo		if (!set_uid)
213191983Sweongyo			uid = sb.st_uid;
214191983Sweongyo		if (!set_gid)
215191983Sweongyo			gid = sb.st_gid;
216259454Shselasky		if (!set_mask)
217191983Sweongyo			mask = dirmask =
218191983Sweongyo				sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
219191983Sweongyo	}
220191983Sweongyo
221191983Sweongyo	build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
222191983Sweongyo	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
223191983Sweongyo	build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
224191983Sweongyo	build_iovec_argf(&iov, &iovlen, "uid", "%d", uid);
225191983Sweongyo	build_iovec_argf(&iov, &iovlen, "gid", "%u", gid);
226191983Sweongyo	build_iovec_argf(&iov, &iovlen, "mask", "%u", mask);
227191983Sweongyo	build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask);
228192984Sthompsa
229191983Sweongyo	if (nmount(iov, iovlen, mntflags) < 0)
230192499Sthompsa		err(1, "%s", dev);
231191983Sweongyo
232191983Sweongyo	exit (0);
233191983Sweongyo}
234191983Sweongyo
235191983Sweongyogid_t
236191983Sweongyoa_gid(s)
237223486Shselasky	char *s;
238191983Sweongyo{
239191983Sweongyo	struct group *gr;
240191983Sweongyo	char *gname;
241191983Sweongyo	gid_t gid;
242191983Sweongyo
243191983Sweongyo	if ((gr = getgrnam(s)) != NULL)
244191983Sweongyo		gid = gr->gr_gid;
245191983Sweongyo	else {
246191983Sweongyo		for (gname = s; *s && isdigit(*s); ++s);
247192984Sthompsa		if (!*s)
248191983Sweongyo			gid = atoi(gname);
249191983Sweongyo		else
250191983Sweongyo			errx(EX_NOUSER, "unknown group id: %s", gname);
251191983Sweongyo	}
252191983Sweongyo	return (gid);
253191983Sweongyo}
254191983Sweongyo
255194228Sthompsauid_t
256191983Sweongyoa_uid(s)
257191983Sweongyo	char *s;
258191983Sweongyo{
259191983Sweongyo	struct passwd *pw;
260191983Sweongyo	char *uname;
261191983Sweongyo	uid_t uid;
262194228Sthompsa
263191983Sweongyo	if ((pw = getpwnam(s)) != NULL)
264191983Sweongyo		uid = pw->pw_uid;
265191983Sweongyo	else {
266194228Sthompsa		for (uname = s; *s && isdigit(*s); ++s);
267259454Shselasky		if (!*s)
268191983Sweongyo			uid = atoi(uname);
269191983Sweongyo		else
270259454Shselasky			errx(EX_NOUSER, "unknown user id: %s", uname);
271259454Shselasky	}
272259454Shselasky	return (uid);
273259454Shselasky}
274259454Shselasky
275259454Shselaskymode_t
276259454Shselaskya_mask(s)
277259454Shselasky	char *s;
278259454Shselasky{
279259454Shselasky	int done, rv;
280259454Shselasky	char *ep;
281259454Shselasky
282259454Shselasky	done = 0;
283191983Sweongyo	rv = -1;
284191983Sweongyo	if (*s >= '0' && *s <= '7') {
285191983Sweongyo		done = 1;
286191983Sweongyo		rv = strtol(optarg, &ep, 8);
287191983Sweongyo	}
288191983Sweongyo	if (!done || rv < 0 || *ep)
289191983Sweongyo		errx(EX_USAGE, "invalid file mode: %s", s);
290191983Sweongyo	return (rv);
291191983Sweongyo}
292191983Sweongyo
293191983Sweongyovoid
294191983Sweongyousage()
295191983Sweongyo{
296191983Sweongyo	fprintf(stderr, "%s\n%s\n%s\n",
297191983Sweongyo	"usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]",
298191983Sweongyo	"                     [-M mask] [-m mask] [-o options] [-u uid]",
299191983Sweongyo	"		      [-W table] special node");
300199816Sthompsa	exit(EX_USAGE);
301191983Sweongyo}
302191983Sweongyo
303191983Sweongyoint
304191983Sweongyoset_charset(struct iovec **iov, int *iovlen, const char *cs_local, const char *cs_dos)
305191983Sweongyo{
306191983Sweongyo	int error;
307191983Sweongyo
308191983Sweongyo	if (modfind("msdosfs_iconv") < 0)
309191983Sweongyo		if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) {
310191983Sweongyo			warnx("cannot find or load \"msdosfs_iconv\" kernel module");
311191983Sweongyo			return (-1);
312191983Sweongyo		}
313191983Sweongyo
314191983Sweongyo	build_iovec_argf(iov, iovlen, "cs_win", ENCODING_UNICODE);
315191983Sweongyo	error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local);
316191983Sweongyo	if (error)
317191983Sweongyo		return (-1);
318191983Sweongyo	if (cs_dos != NULL) {
319191983Sweongyo		error = kiconv_add_xlat16_cspairs(cs_dos, cs_local);
320191983Sweongyo		if (error)
321191983Sweongyo			return (-1);
322191983Sweongyo	} else {
323191983Sweongyo		build_iovec_argf(iov, iovlen, "cs_dos", cs_local);
324191983Sweongyo		error = kiconv_add_xlat16_cspair(cs_local, cs_local,
325191983Sweongyo				KICONV_FROM_UPPER | KICONV_LOWER);
326191983Sweongyo		if (error)
327191983Sweongyo			return (-1);
328191983Sweongyo	}
329191983Sweongyo
330191983Sweongyo	return (0);
331191983Sweongyo}
332191983Sweongyo