xref: /dflybsd-src/sbin/mount_cd9660/mount_cd9660.c (revision 44d26fef007fd5282db87e8bea6faec2b48d2803)
1 /*
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley
6  * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
7  * Support code is derived from software contributed to Berkeley
8  * by Atsushi Murai (amurai@spec.co.jp).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * @(#)mount_cd9660.c	8.7 (Berkeley) 5/1/95
35  * $FreeBSD: src/sbin/mount_cd9660/mount_cd9660.c,v 1.15.2.3 2001/03/14 12:05:01 bp Exp $
36  */
37 
38 #include <sys/cdio.h>
39 #include <sys/param.h>
40 #include <sys/mount.h>
41 #include <sys/iconv.h>
42 #include <sys/linker.h>
43 #include <sys/module.h>
44 #include <vfs/isofs/cd9660/cd9660_mount.h>
45 
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <grp.h>
51 #include <mntopts.h>
52 #include <pwd.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <locale.h>
57 #include <sysexits.h>
58 #include <unistd.h>
59 
60 struct mntopt mopts[] = {
61 	MOPT_STDOPTS,
62 	MOPT_UPDATE,
63 	{ "extatt", 0, ISOFSMNT_EXTATT, 1 },
64 	{ "gens", 0, ISOFSMNT_GENS, 1 },
65 	{ "rrip", 1, ISOFSMNT_NORRIP, 1 },
66 	{ "joliet", 1, ISOFSMNT_NOJOLIET, 1 },
67 	{ "strictjoliet", 1, ISOFSMNT_BROKENJOLIET, 1 },
68 	MOPT_NULL
69 };
70 
71 static gid_t	a_gid(const char *);
72 static uid_t	a_uid(const char *);
73 static mode_t	a_mask(const char *);
74 static int	set_charset(struct iso_args *args, const char *cs_local);
75 static int	get_ssector(const char *dev);
76 static void	usage(void);
77 
78 int
main(int argc,char ** argv)79 main(int argc, char **argv)
80 {
81 	struct iso_args args;
82 	int ch, mntflags, opts, set_mask, set_dirmask;
83 	char *dev, *dir, mntpath[MAXPATHLEN];
84 	struct vfsconf vfc;
85 	int error, verbose;
86 	const char *cs_local;
87 
88 	mntflags = opts = set_mask = set_dirmask = verbose = 0;
89 	memset(&args, 0, sizeof args);
90 	args.ssector = -1;
91 	while ((ch = getopt(argc, argv, "bC:egG:jm:M:o:rs:U:v")) != -1)
92 		switch (ch) {
93 		case 'b':
94 			opts |= ISOFSMNT_BROKENJOLIET;
95 			break;
96 		case 'C':
97 			cs_local = kiconv_quirkcs(optarg, KICONV_VENDOR_MICSFT);
98 			if (set_charset(&args, cs_local) == -1)
99 				err(EX_OSERR, "cd9660_iconv");
100 			opts |= ISOFSMNT_KICONV;
101 			break;
102 		case 'e':
103 			opts |= ISOFSMNT_EXTATT;
104 			break;
105 		case 'g':
106 			opts |= ISOFSMNT_GENS;
107 			break;
108 		case 'G':
109 			opts |= ISOFSMNT_GID;
110 			args.gid = a_gid(optarg);
111 			break;
112 		case 'j':
113 			opts |= ISOFSMNT_NOJOLIET;
114 			break;
115 		case 'm':
116 			args.fmask = a_mask(optarg);
117 			opts |= ISOFSMNT_MODEMASK;
118 			set_mask = 1;
119 			break;
120 		case 'M':
121 			args.dmask = a_mask(optarg);
122 			opts |= ISOFSMNT_MODEMASK;
123 			set_dirmask = 1;
124 			break;
125 		case 'o':
126 			getmntopts(optarg, mopts, &mntflags, &opts);
127 			break;
128 		case 'r':
129 			opts |= ISOFSMNT_NORRIP;
130 			break;
131 		case 's':
132 			args.ssector = atoi(optarg);
133 			break;
134 		case 'U':
135 			opts |= ISOFSMNT_UID;
136 			args.uid = a_uid(optarg);
137 			break;
138 		case 'v':
139 			verbose++;
140 			break;
141 		case '?':
142 		default:
143 			usage();
144 		}
145 	argc -= optind;
146 	argv += optind;
147 
148 	if (argc != 2)
149 		usage();
150 
151 	if (set_mask && !set_dirmask) {
152 		args.dmask = args.fmask;
153 		set_dirmask = 1;
154 	} else if (set_dirmask && !set_mask) {
155 		args.fmask = args.dmask;
156 		set_mask = 1;
157 	}
158 
159 	dev = argv[0];
160 	dir = argv[1];
161 
162 	/*
163 	 * Resolve the mountpoint with realpath(3) and remove unnecessary
164 	 * slashes from the devicename if there are any.
165 	 */
166 	checkpath(dir, mntpath);
167 	rmslashes(dev, dev);
168 
169 #define DEFAULT_ROOTUID	-2
170 	/*
171 	 * ISO 9660 filesystems are not writeable.
172 	 */
173 	mntflags |= MNT_RDONLY;
174 	args.export.ex_flags = MNT_EXRDONLY;
175 	args.fspec = dev;
176 	args.export.ex_root = DEFAULT_ROOTUID;
177 	args.flags = opts;
178 
179 	if (args.ssector == -1) {
180 		/*
181 		 * The start of the session has not been specified on
182 		 * the command line.  If we can successfully read the
183 		 * TOC of a CD-ROM, use the last data track we find.
184 		 * Otherwise, just use 0, in order to mount the very
185 		 * first session.  This is compatible with the
186 		 * historic behaviour of mount_cd9660(8).  If the user
187 		 * has specified -s <ssector> above, we don't get here
188 		 * and leave the user's will.
189 		 */
190 		if ((args.ssector = get_ssector(dev)) == -1) {
191 			if (verbose)
192 				printf("could not determine starting sector, "
193 				       "using very first session\n");
194 			args.ssector = 0;
195 		} else if (verbose)
196 			printf("using starting sector %d\n", args.ssector);
197 	}
198 
199 	/*
200 	 * Don't set the mount flag, so newer kernels will just ignore
201 	 * the fmask and dmask fields when the options are not used.
202 	 * Older kernels will still use them so set a backward-compatible
203 	 * default.  Do not use the underlying mount point's permsissions
204 	 * as a basis.
205 	 */
206 	if (!set_mask) {
207 #if 0
208 		struct stat sb;
209 		if (stat(mntpath, &sb) == -1)
210 			err(EX_OSERR, "stat %s", mntpath);
211 #endif
212 		args.fmask = args.dmask = ACCESSPERMS;
213 #if 0
214 		args.fmask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
215 #endif
216 	}
217 
218 	error = getvfsbyname("cd9660", &vfc);
219 	if (error && vfsisloadable("cd9660")) {
220 		if (vfsload("cd9660"))
221 			err(EX_OSERR, "vfsload(cd9660)");
222 		endvfsent();	/* flush cache */
223 		error = getvfsbyname("cd9660", &vfc);
224 	}
225 	if (error)
226 		errx(1, "cd9660 filesystem is not available");
227 
228 	if (mount(vfc.vfc_name, mntpath, mntflags, &args) < 0)
229 		err(1, "%s", args.fspec);
230 	exit(0);
231 }
232 
233 static int
set_charset(struct iso_args * args,const char * cs_local)234 set_charset(struct iso_args *args, const char *cs_local)
235 {
236 	if (modfind("cd9660_iconv") < 0) {
237 		if (kldload("cd9660_iconv") < 0 ||
238 		    modfind("cd9660_iconv") < 0) {
239 			warnx("cannot find or load \"cd9660_iconv\" "
240 			      "kernel module");
241 			return (-1);
242 		}
243 	}
244 
245 	snprintf(args->cs_disk, sizeof(args->cs_disk), "%s", ENCODING_UNICODE);
246 	snprintf(args->cs_local, sizeof(args->cs_local), "%s", cs_local);
247 	if (kiconv_add_xlat16_cspairs(args->cs_disk, args->cs_local) != 0)
248 		return (-1);
249 
250 	return (0);
251 }
252 
253 static void
usage(void)254 usage(void)
255 {
256 	fprintf(stderr,
257 	    "usage: mount_cd9660 [-begjrv] [-C charset] [-G gid] [-m mask]\n"
258 	    "                    [-M mask] [-o options] [-s startsector]\n"
259 	    "                    [-U uid] special_node\n");
260 	exit(EX_USAGE);
261 }
262 
263 static int
get_ssector(const char * dev)264 get_ssector(const char *dev)
265 {
266 	struct ioc_toc_header h;
267 	struct ioc_read_toc_entry t;
268 	struct cd_toc_entry toc_buffer[100];
269 	int fd, ntocentries, i;
270 
271 	if ((fd = open(dev, O_RDONLY)) == -1)
272 		return -1;
273 	if (ioctl(fd, CDIOREADTOCHEADER, &h) == -1) {
274 		close(fd);
275 		return -1;
276 	}
277 
278 	ntocentries = h.ending_track - h.starting_track + 1;
279 	if (ntocentries > 100) {
280 		/* unreasonable, only 100 allowed */
281 		close(fd);
282 		return -1;
283 	}
284 	t.address_format = CD_LBA_FORMAT;
285 	t.starting_track = 0;
286 	t.data_len = ntocentries * sizeof(struct cd_toc_entry);
287 	t.data = toc_buffer;
288 
289 	if (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t) == -1) {
290 		close(fd);
291 		return -1;
292 	}
293 	close(fd);
294 
295 	for (i = ntocentries - 1; i >= 0; i--)
296 		if ((toc_buffer[i].control & 4) != 0)
297 			/* found a data track */
298 			break;
299 	if (i < 0)
300 		return -1;
301 
302 	return ntohl(toc_buffer[i].addr.lba);
303 }
304 
305 static gid_t
a_gid(const char * s)306 a_gid(const char *s)
307 {
308 	struct group *gr;
309 	const char *gname;
310 	gid_t gid;
311 
312 	if ((gr = getgrnam(s)) != NULL) {
313 		gid = gr->gr_gid;
314 	} else {
315 		for (gname = s; *s && isdigit(*s); ++s)
316 			;
317 		if (!*s)
318 			gid = atoi(gname);
319 		else
320 			errx(EX_NOUSER, "unknown group id: %s", gname);
321 	}
322 	return (gid);
323 }
324 
325 static uid_t
a_uid(const char * s)326 a_uid(const char *s)
327 {
328 	struct passwd *pw;
329 	const char *uname;
330 	uid_t uid;
331 
332 	if ((pw = getpwnam(s)) != NULL) {
333 		uid = pw->pw_uid;
334 	} else {
335 		for (uname = s; *s && isdigit(*s); ++s)
336 			;
337 		if (!*s)
338 			uid = atoi(uname);
339 		else
340 			errx(EX_NOUSER, "unknown user id: %s", uname);
341 	}
342 	return (uid);
343 }
344 
345 static mode_t
a_mask(const char * s)346 a_mask(const char *s)
347 {
348 	int done, rv;
349 	char *ep;
350 
351 	done = 0;
352 	rv = -1;
353 	if (*s >= '0' && *s <= '7') {
354 		done = 1;
355 		rv = strtol(optarg, &ep, 8);
356 	}
357 	if (!done || rv < 0 || *ep)
358 		errx(EX_USAGE, "invalid file mode: %s", s);
359 	return (rv);
360 }
361