mount_msdosfs.c revision 152731
1160814Ssimon/*	$NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $	*/
2296341Sdelphij
3160814Ssimon/*
4296341Sdelphij * Copyright (c) 1994 Christopher G. Demetriou
5160814Ssimon * All rights reserved.
6160814Ssimon *
7160814Ssimon * Redistribution and use in source and binary forms, with or without
8160814Ssimon * modification, are permitted provided that the following conditions
9160814Ssimon * are met:
10160814Ssimon * 1. Redistributions of source code must retain the above copyright
11160814Ssimon *    notice, this list of conditions and the following disclaimer.
12160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright
13160814Ssimon *    notice, this list of conditions and the following disclaimer in the
14296341Sdelphij *    documentation and/or other materials provided with the distribution.
15160814Ssimon * 3. All advertising materials mentioning features or use of this software
16160814Ssimon *    must display the following acknowledgement:
17160814Ssimon *      This product includes software developed by Christopher G. Demetriou.
18160814Ssimon * 4. The name of the author may not be used to endorse or promote products
19160814Ssimon *    derived from this software without specific prior written permission
20160814Ssimon *
21160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22160814Ssimon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23160814Ssimon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24160814Ssimon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25160814Ssimon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26160814Ssimon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27160814Ssimon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28160814Ssimon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29160814Ssimon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30160814Ssimon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31160814Ssimon */
32160814Ssimon
33160814Ssimon#ifndef lint
34160814Ssimonstatic const char rcsid[] =
35160814Ssimon  "$FreeBSD: head/sbin/mount_msdosfs/mount_msdosfs.c 152731 2005-11-23 19:52:14Z avatar $";
36160814Ssimon#endif /* not lint */
37160814Ssimon
38160814Ssimon#include <sys/param.h>
39160814Ssimon#include <sys/mount.h>
40160814Ssimon#include <sys/stat.h>
41160814Ssimon#include <sys/iconv.h>
42160814Ssimon#include <sys/linker.h>
43160814Ssimon#include <sys/module.h>
44160814Ssimon
45160814Ssimon#include <ctype.h>
46160814Ssimon#include <err.h>
47160814Ssimon#include <grp.h>
48160814Ssimon#include <locale.h>
49160814Ssimon#include <pwd.h>
50160814Ssimon#include <stdio.h>
51160814Ssimon/* must be after stdio to declare fparseln */
52160814Ssimon#include <libutil.h>
53160814Ssimon#include <stdlib.h>
54160814Ssimon#include <string.h>
55160814Ssimon#include <sysexits.h>
56160814Ssimon#include <unistd.h>
57160814Ssimon
58160814Ssimon#include "mntopts.h"
59160814Ssimon
60160814Ssimonstatic struct mntopt mopts[] = {
61205128Ssimon	MOPT_STDOPTS,
62160814Ssimon	MOPT_FORCE,
63160814Ssimon	MOPT_SYNC,
64160814Ssimon	MOPT_UPDATE,
65205128Ssimon	MOPT_END
66296341Sdelphij};
67205128Ssimon
68205128Ssimonstatic gid_t	a_gid(char *);
69205128Ssimonstatic uid_t	a_uid(char *);
70296341Sdelphijstatic mode_t	a_mask(char *);
71205128Ssimonstatic void	usage(void) __dead2;
72160814Ssimonstatic int	set_charset(struct iovec *iov, int *iovlen, const char *, const char *);
73296341Sdelphij
74160814Ssimonint
75296341Sdelphijmain(int argc, char **argv)
76296341Sdelphij{
77296341Sdelphij	struct iovec *iov = NULL;
78296341Sdelphij	int iovlen = 0;
79296341Sdelphij	struct stat sb;
80296341Sdelphij	int c, mntflags, set_gid, set_uid, set_mask, set_dirmask;
81296341Sdelphij	int optflags = 0;
82296341Sdelphij	char *dev, *dir, mntpath[MAXPATHLEN], *csp;
83296341Sdelphij	char fstype[] = "msdosfs";
84296341Sdelphij	char *cs_dos = NULL;
85296341Sdelphij	char *cs_local = NULL;
86296341Sdelphij	mode_t mask = 0, dirmask = 0;
87160814Ssimon	uid_t uid = 0;
88160814Ssimon	gid_t gid = 0;
89296341Sdelphij	getmnt_silent = 1;
90296341Sdelphij
91296341Sdelphij	mntflags = set_gid = set_uid = set_mask = set_dirmask = 0;
92296341Sdelphij
93296341Sdelphij	while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) {
94296341Sdelphij		switch (c) {
95296341Sdelphij		case 's':
96160814Ssimon			build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1);
97160814Ssimon			break;
98296341Sdelphij		case 'l':
99296341Sdelphij			build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1);
100160814Ssimon			break;
101296341Sdelphij		case '9':
102296341Sdelphij			build_iovec_argf(&iov, &iovlen, "nowin95", "", (size_t)-1);
103296341Sdelphij			break;
104296341Sdelphij		case 'u':
105296341Sdelphij			uid = a_uid(optarg);
106160814Ssimon			set_uid = 1;
107296341Sdelphij			break;
108160814Ssimon		case 'g':
109296341Sdelphij			gid = a_gid(optarg);
110296341Sdelphij			set_gid = 1;
111296341Sdelphij			break;
112296341Sdelphij		case 'm':
113296341Sdelphij			mask = a_mask(optarg);
114160814Ssimon			set_mask = 1;
115296341Sdelphij			break;
116296341Sdelphij		case 'M':
117296341Sdelphij			dirmask = a_mask(optarg);
118160814Ssimon			set_dirmask = 1;
119296341Sdelphij			break;
120296341Sdelphij		case 'L': {
121279264Sdelphij			const char *quirk = NULL;
122296341Sdelphij			if (setlocale(LC_CTYPE, optarg) == NULL)
123296341Sdelphij				err(EX_CONFIG, "%s", optarg);
124296341Sdelphij			csp = strchr(optarg,'.');
125296341Sdelphij			if (!csp)
126296341Sdelphij				err(EX_CONFIG, "%s", optarg);
127296341Sdelphij			quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT);
128296341Sdelphij			build_iovec_argf(&iov, &iovlen, "cs_local", quirk);
129296341Sdelphij			}
130296341Sdelphij			break;
131296341Sdelphij		case 'D':
132296341Sdelphij			cs_dos = strdup(optarg);
133296341Sdelphij			build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
134296341Sdelphij			break;
135296341Sdelphij		case 'o': {
136296341Sdelphij			char *p = NULL;
137296341Sdelphij			char *val = strdup("");
138160814Ssimon			getmntopts(optarg, mopts, &mntflags, &optflags);
139296341Sdelphij			p = strchr(optarg, '=');
140296341Sdelphij			if (p != NULL) {
141296341Sdelphij				free(val);
142296341Sdelphij				*p = '\0';
143160814Ssimon				val = p + 1;
144237657Sjkim			}
145296341Sdelphij			build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
146160814Ssimon			}
147296341Sdelphij			break;
148160814Ssimon		case 'W':
149296341Sdelphij			if (strcmp(optarg, "iso22dos") == 0) {
150296341Sdelphij				cs_local = strdup("ISO8859-2");
151296341Sdelphij				cs_dos = strdup("CP852");
152296341Sdelphij			} else if (strcmp(optarg, "iso72dos") == 0) {
153296341Sdelphij				cs_local = strdup("ISO8859-7");
154160814Ssimon				cs_dos = strdup("CP737");
155160814Ssimon			} else if (strcmp(optarg, "koi2dos") == 0) {
156296341Sdelphij				cs_local = strdup("KOI8-R");
157296341Sdelphij				cs_dos = strdup("CP866");
158296341Sdelphij			} else if (strcmp(optarg, "koi8u2dos") == 0) {
159296341Sdelphij				cs_local = strdup("KOI8-U");
160296341Sdelphij				cs_dos = strdup("CP866");
161296341Sdelphij			} else {
162160814Ssimon				err(EX_NOINPUT, "%s", optarg);
163160814Ssimon			}
164160814Ssimon			build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1);
165296341Sdelphij			build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1);
166160814Ssimon			break;
167306230Sdelphij		case '?':
168306230Sdelphij		default:
169306230Sdelphij			usage();
170306230Sdelphij			break;
171306230Sdelphij		}
172306230Sdelphij	}
173306230Sdelphij
174306230Sdelphij	if (optind + 2 != argc)
175306230Sdelphij		usage();
176306230Sdelphij
177306230Sdelphij	if (set_mask && !set_dirmask) {
178306230Sdelphij		dirmask = mask;
179306230Sdelphij		set_dirmask = 1;
180306230Sdelphij	}
181306230Sdelphij	else if (set_dirmask && !set_mask) {
182306230Sdelphij		mask = dirmask;
183306230Sdelphij		set_mask = 1;
184306230Sdelphij	}
185296341Sdelphij
186160814Ssimon	dev = argv[optind];
187279264Sdelphij	dir = argv[optind + 1];
188160814Ssimon
189296341Sdelphij	if (cs_local != NULL) {
190306230Sdelphij		if (set_charset(iov, &iovlen, cs_local, cs_dos) == -1)
191160814Ssimon			err(EX_OSERR, "msdosfs_iconv");
192306230Sdelphij		build_iovec_argf(&iov, &iovlen, "kiconv", "");
193306230Sdelphij	} else if (cs_dos != NULL) {
194306230Sdelphij		build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1");
195306230Sdelphij		if (set_charset(iov, &iovlen, "ISO8859-1", cs_dos) == -1)
196306230Sdelphij			err(EX_OSERR, "msdosfs_iconv");
197296341Sdelphij		build_iovec_argf(&iov, &iovlen, "kiconv", "");
198160814Ssimon	}
199279264Sdelphij
200160814Ssimon	/*
201296341Sdelphij	 * Resolve the mountpoint with realpath(3) and remove unnecessary
202296341Sdelphij	 * slashes from the devicename if there are any.
203160814Ssimon	 */
204306230Sdelphij	(void)checkpath(dir, mntpath);
205237657Sjkim	(void)rmslashes(dev, dev);
206296341Sdelphij
207296341Sdelphij	if (!set_gid || !set_uid || !set_mask) {
208237657Sjkim		if (stat(mntpath, &sb) == -1)
209296341Sdelphij			err(EX_OSERR, "stat %s", mntpath);
210237657Sjkim
211237657Sjkim		if (!set_uid)
212237657Sjkim			uid = sb.st_uid;
213237657Sjkim		if (!set_gid)
214296341Sdelphij			gid = sb.st_gid;
215296341Sdelphij		if (!set_mask)
216160814Ssimon			mask = dirmask =
217296341Sdelphij				sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
218296341Sdelphij	}
219296341Sdelphij
220160814Ssimon	build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
221160814Ssimon	build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1);
222296341Sdelphij	build_iovec(&iov, &iovlen, "from", dev, (size_t)-1);
223237657Sjkim	build_iovec_argf(&iov, &iovlen, "uid", "%d", uid);
224237657Sjkim	build_iovec_argf(&iov, &iovlen, "gid", "%u", gid);
225237657Sjkim	build_iovec_argf(&iov, &iovlen, "mask", "%u", mask);
226296341Sdelphij	build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask);
227296341Sdelphij
228296341Sdelphij	if (nmount(iov, iovlen, mntflags) < 0)
229296341Sdelphij		err(1, "%s", dev);
230237657Sjkim
231296341Sdelphij	exit (0);
232296341Sdelphij}
233296341Sdelphij
234296341Sdelphijgid_t
235296341Sdelphija_gid(s)
236296341Sdelphij	char *s;
237296341Sdelphij{
238296341Sdelphij	struct group *gr;
239237657Sjkim	char *gname;
240296341Sdelphij	gid_t gid;
241237657Sjkim
242296341Sdelphij	if ((gr = getgrnam(s)) != NULL)
243237657Sjkim		gid = gr->gr_gid;
244296341Sdelphij	else {
245296341Sdelphij		for (gname = s; *s && isdigit(*s); ++s);
246296341Sdelphij		if (!*s)
247237657Sjkim			gid = atoi(gname);
248296341Sdelphij		else
249296341Sdelphij			errx(EX_NOUSER, "unknown group id: %s", gname);
250296341Sdelphij	}
251296341Sdelphij	return (gid);
252237657Sjkim}
253296341Sdelphij
254296341Sdelphijuid_t
255296341Sdelphija_uid(s)
256296341Sdelphij	char *s;
257296341Sdelphij{
258296341Sdelphij	struct passwd *pw;
259237657Sjkim	char *uname;
260296341Sdelphij	uid_t uid;
261296341Sdelphij
262296341Sdelphij	if ((pw = getpwnam(s)) != NULL)
263296341Sdelphij		uid = pw->pw_uid;
264296341Sdelphij	else {
265296341Sdelphij		for (uname = s; *s && isdigit(*s); ++s);
266194206Ssimon		if (!*s)
267205128Ssimon			uid = atoi(uname);
268296341Sdelphij		else
269296341Sdelphij			errx(EX_NOUSER, "unknown user id: %s", uname);
270205128Ssimon	}
271296341Sdelphij	return (uid);
272296341Sdelphij}
273296341Sdelphij
274296341Sdelphijmode_t
275296341Sdelphija_mask(s)
276296341Sdelphij	char *s;
277296341Sdelphij{
278296341Sdelphij	int done, rv;
279296341Sdelphij	char *ep;
280296341Sdelphij
281296341Sdelphij	done = 0;
282296341Sdelphij	rv = -1;
283296341Sdelphij	if (*s >= '0' && *s <= '7') {
284296341Sdelphij		done = 1;
285296341Sdelphij		rv = strtol(optarg, &ep, 8);
286296341Sdelphij	}
287296341Sdelphij	if (!done || rv < 0 || *ep)
288296341Sdelphij		errx(EX_USAGE, "invalid file mode: %s", s);
289273399Sdelphij	return (rv);
290296341Sdelphij}
291273399Sdelphij
292296341Sdelphijvoid
293296341Sdelphijusage()
294296341Sdelphij{
295296341Sdelphij	fprintf(stderr, "%s\n%s\n%s\n",
296296341Sdelphij	"usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]",
297296341Sdelphij	"                     [-M mask] [-m mask] [-o options] [-u uid]",
298296341Sdelphij	"		      [-W table] special node");
299296341Sdelphij	exit(EX_USAGE);
300296341Sdelphij}
301296341Sdelphij
302296341Sdelphijint
303296341Sdelphijset_charset(struct iovec *iov, int *iovlen, const char *cs_local, const char *cs_dos)
304296341Sdelphij{
305296341Sdelphij	int error;
306296341Sdelphij
307296341Sdelphij	if (modfind("msdosfs_iconv") < 0)
308296341Sdelphij		if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) {
309296341Sdelphij			warnx("cannot find or load \"msdosfs_iconv\" kernel module");
310296341Sdelphij			return (-1);
311296341Sdelphij		}
312296341Sdelphij
313296341Sdelphij	build_iovec_argf(&iov, iovlen, "cs_win", ENCODING_UNICODE);
314296341Sdelphij	error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local);
315296341Sdelphij	if (error)
316296341Sdelphij		return (-1);
317296341Sdelphij	if (cs_dos != NULL) {
318296341Sdelphij		error = kiconv_add_xlat16_cspairs(cs_dos, cs_local);
319205128Ssimon		if (error)
320194206Ssimon			return (-1);
321194206Ssimon	} else {
322194206Ssimon		build_iovec_argf(&iov, iovlen, "cs_dos", cs_local);
323194206Ssimon		error = kiconv_add_xlat16_cspair(cs_local, cs_local,
324194206Ssimon				KICONV_FROM_UPPER | KICONV_LOWER);
325194206Ssimon		if (error)
326194206Ssimon			return (-1);
327238405Sjkim	}
328296341Sdelphij
329296341Sdelphij	return (0);
330194206Ssimon}
331296341Sdelphij