1/*	$NetBSD: mount.c,v 1.107 2021/12/07 14:31:13 christos Exp $	*/
2
3/*
4 * Copyright (c) 1980, 1989, 1993, 1994
5 *	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__COPYRIGHT("@(#) Copyright (c) 1980, 1989, 1993, 1994\
35 The Regents of the University of California.  All rights reserved.");
36#endif /* not lint */
37
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)mount.c	8.25 (Berkeley) 5/8/95";
41#else
42__RCSID("$NetBSD: mount.c,v 1.107 2021/12/07 14:31:13 christos Exp $");
43#endif
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/mount.h>
48#include <sys/wait.h>
49
50#include <fs/puffs/puffs_msgif.h>
51
52#include <err.h>
53#include <errno.h>
54#include <fstab.h>
55#include <pwd.h>
56#include <signal.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61#include <util.h>
62
63#define MOUNTNAMES
64#include <fcntl.h>
65#include <sys/disk.h>
66#include <sys/disklabel.h>
67#include <sys/ioctl.h>
68
69#include "pathnames.h"
70#include "mountprog.h"
71
72static int	debug, verbose;
73
74static void	catopt(char **, const char *);
75static const char *
76		getfslab(const char *str);
77static struct statvfs *
78		getmntpt(const char *);
79static int 	getmntargs(struct statvfs *, char *, size_t);
80static int	hasopt(const char *, const char *);
81static void	mangle(char *, int *, const char ** volatile *, int *);
82static int	mountfs(const char *, const char *, const char *,
83		    int, const char *, const char *, int, char *, size_t);
84static void	prmount(struct statvfs *);
85__dead static void	usage(void);
86
87
88/* Map from mount options to printable formats. */
89static const struct opt {
90	int o_opt;
91	int o_silent;
92	const char *o_name;
93} optnames[] = {
94	__MNT_FLAGS
95};
96
97#define FLG_UPDATE	1
98#define FLG_RDONLY	2
99#define FLG_RDWRITE	4
100#define FLG_FORCE	8
101
102static const char ffs_fstype[] = "ffs";
103
104int
105main(int argc, char *argv[])
106{
107	const char *mntfromname, *mntonname, **vfslist, *vfstype;
108	struct fstab *fs;
109	struct statvfs *mntbuf;
110#if 0
111	FILE *mountdfp;
112#endif
113	int all, ch, forceall, i, init_flags, mntsize, rval;
114	char *options;
115	const char *mountopts, *fstypename;
116	char canonical_path_buf[MAXPATHLEN], buf[MAXPATHLEN];
117	char *canonical_path;
118
119	/* started as "mount" */
120	all = forceall = init_flags = 0;
121	options = NULL;
122	vfslist = NULL;
123	vfstype = ffs_fstype;
124	while ((ch = getopt(argc, argv, "Aadfo:rwt:uv")) != -1)
125		switch (ch) {
126		case 'A':
127			all = forceall = 1;
128			break;
129		case 'a':
130			all = 1;
131			break;
132		case 'd':
133			debug = 1;
134			break;
135		case 'f':
136			init_flags |= FLG_FORCE;
137			break;
138		case 'o':
139			if (*optarg)
140				catopt(&options, optarg);
141			break;
142		case 'r':
143			init_flags |= FLG_RDONLY;
144			break;
145		case 't':
146			if (vfslist != NULL)
147				errx(EXIT_FAILURE,
148				    "Only one -t option may be specified.");
149			vfslist = makevfslist(optarg);
150			vfstype = optarg;
151			break;
152		case 'u':
153			init_flags |= FLG_UPDATE;
154			break;
155		case 'v':
156			verbose++;
157			break;
158		case 'w':
159			init_flags |= FLG_RDWRITE;
160			break;
161		case '?':
162		default:
163			usage();
164			/* NOTREACHED */
165		}
166	argc -= optind;
167	argv += optind;
168
169#define	BADTYPE(type)							\
170	(strcmp(type, FSTAB_RO) &&					\
171	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
172
173	rval = 0;
174	switch (argc) {
175	case 0:
176		if (all) {
177			while ((fs = getfsent()) != NULL) {
178				if (BADTYPE(fs->fs_type))
179					continue;
180				if (checkvfsname(fs->fs_vfstype, vfslist))
181					continue;
182				if (hasopt(fs->fs_mntops, "noauto"))
183					continue;
184				if (strcmp(fs->fs_spec, "from_mount") == 0) {
185					if ((mntbuf = getmntpt(fs->fs_file))
186					    == NULL)
187						errx(EXIT_FAILURE,
188						    "Unknown file system %s",
189						    fs->fs_file);
190					mntfromname = mntbuf->f_mntfromname;
191				} else
192					mntfromname = fs->fs_spec;
193				mntfromname = getfsspecname(buf, sizeof(buf),
194				    mntfromname);
195				if (mntfromname == NULL)
196					err(EXIT_FAILURE, "%s", buf);
197				if (mountfs(fs->fs_vfstype, mntfromname,
198				    fs->fs_file, init_flags, options,
199				    fs->fs_mntops, !forceall, NULL, 0))
200					rval = 1;
201			}
202		} else {
203			if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
204				err(EXIT_FAILURE, "getmntinfo");
205			for (i = 0; i < mntsize; i++) {
206				if (checkvfsname(mntbuf[i].f_fstypename,
207				    vfslist))
208					continue;
209				prmount(&mntbuf[i]);
210			}
211		}
212		return rval;
213	case 1:
214		if (vfslist != NULL) {
215			usage();
216			/* NOTREACHED */
217		}
218
219		/*
220		 * Create a canonical version of the device or mount path
221		 * passed to us.  It's ok for this to fail.  It's also ok
222		 * for the result to be exactly the same as the original.
223		 */
224		canonical_path = realpath(*argv, canonical_path_buf);
225
226		if (init_flags & FLG_UPDATE) {
227			/*
228			 * Try looking up the canonical path first,
229			 * then try exactly what the user entered.
230			 */
231			if ((canonical_path == NULL ||
232			    (mntbuf = getmntpt(canonical_path)) == NULL) &&
233			    (mntbuf = getmntpt(*argv)) == NULL) {
234out:
235				errx(EXIT_FAILURE,
236				    "Unknown special file or file system `%s'",
237				    *argv);
238			}
239			mntfromname = mntbuf->f_mntfromname;
240			if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
241				if (strcmp(fs->fs_spec, "from_mount") != 0)
242					mntfromname = fs->fs_spec;
243				/* ignore the fstab file options.  */
244				fs->fs_mntops = NULL;
245			}
246			mntonname  = mntbuf->f_mntonname;
247			fstypename = mntbuf->f_fstypename;
248			mountopts  = NULL;
249		} else {
250			/*
251			 * Try looking up the canonical path first,
252			 * then try exactly what the user entered.
253			 */
254			if (canonical_path == NULL ||
255			    ((fs = getfsfile(canonical_path)) == NULL &&
256			     (fs = getfsspec(canonical_path)) == NULL))
257			{
258				if ((fs = getfsfile(*argv)) == NULL &&
259				    (fs = getfsspec(*argv)) == NULL) {
260					goto out;
261				}
262			}
263			if (BADTYPE(fs->fs_type))
264				errx(EXIT_FAILURE,
265				    "Unknown file system type for `%s'",
266				    *argv);
267			if (strcmp(fs->fs_spec, "from_mount") == 0) {
268				if ((canonical_path == NULL ||
269				    (mntbuf = getmntpt(canonical_path))
270				     == NULL) &&
271				    (mntbuf = getmntpt(*argv)) == NULL)
272					goto out;
273				mntfromname = mntbuf->f_mntfromname;
274			} else
275				mntfromname = fs->fs_spec;
276			mntonname   = fs->fs_file;
277			fstypename  = fs->fs_vfstype;
278			mountopts   = fs->fs_mntops;
279		}
280		mntfromname = getfsspecname(buf, sizeof(buf), mntfromname);
281		if (mntfromname == NULL)
282			err(EXIT_FAILURE, "%s", buf);
283		rval = mountfs(fstypename, mntfromname,
284		    mntonname, init_flags, options, mountopts, 0, NULL, 0);
285		break;
286	case 2:
287		/*
288		 * If -t flag has not been specified, and spec contains either
289		 * a ':' or a '@' then assume that an NFS filesystem is being
290		 * specified ala Sun.
291		 */
292		mntfromname = getfsspecname(buf, sizeof(buf), argv[0]);
293		if (mntfromname == NULL)
294			err(EXIT_FAILURE, "%s", buf);
295		if (vfslist == NULL) {
296			if (strpbrk(argv[0], ":@") != NULL) {
297				vfstype = "nfs";
298			} else {
299				vfstype = getfslab(mntfromname);
300				if (vfstype == NULL)
301					vfstype = ffs_fstype;
302			}
303		}
304		rval = mountfs(vfstype, mntfromname, argv[1], init_flags,
305		    options, NULL, 0, NULL, 0);
306		break;
307	default:
308		usage();
309	}
310
311#if 0	/* disabled because it interferes the service. */
312	/*
313	 * If the mount was successfully, and done by root, tell mountd the
314	 * good news.  Pid checks are probably unnecessary, but don't hurt.
315	 */
316	if (rval == 0 && getuid() == 0 &&
317	    (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
318		int pid;
319
320		if (fscanf(mountdfp, "%d", &pid) == 1 &&
321		    pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
322			err(EXIT_FAILURE, "signal mountd");
323		(void)fclose(mountdfp);
324	}
325#endif
326
327	return rval;
328}
329
330int
331hasopt(const char *mntopts, const char *option)
332{
333	int negative, found;
334	char *opt, *optbuf;
335
336	if (option[0] == 'n' && option[1] == 'o') {
337		negative = 1;
338		option += 2;
339	} else
340		negative = 0;
341	optbuf = estrdup(mntopts);
342	found = 0;
343	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
344		if (opt[0] == 'n' && opt[1] == 'o') {
345			if (!strcasecmp(opt + 2, option))
346				found = negative;
347		} else if (!strcasecmp(opt, option))
348			found = !negative;
349	}
350	free(optbuf);
351	return found;
352}
353
354static int
355mountfs(const char *vfstype, const char *spec, const char *name,
356    int flags, const char *options, const char *mntopts,
357    int skipmounted, char *buf, size_t buflen)
358{
359	/* List of directories containing mount_xxx subcommands. */
360	static const char *edirs[] = {
361#ifdef RESCUEDIR
362		RESCUEDIR,
363#endif
364		_PATH_SBIN,
365		_PATH_USRSBIN,
366		NULL
367	};
368	const char ** volatile argv, **edir;
369	struct statvfs *sfp, sf;
370	pid_t pid;
371	int pfd[2];
372	int argc, numfs, i, status, maxargc;
373	char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN],
374	    mntpath[MAXPATHLEN];
375	volatile int getargs;
376
377	if (realpath(name, mntpath) == NULL) {
378		warn("realpath `%s'", name);
379		return 1;
380	}
381
382	name = mntpath;
383
384	optbuf = NULL;
385	if (mntopts)
386		catopt(&optbuf, mntopts);
387
388	if (options) {
389		catopt(&optbuf, options);
390		getargs = strstr(options, "getargs") != NULL;
391	} else
392		getargs = 0;
393
394	if (!mntopts && !options)
395		catopt(&optbuf, "rw");
396
397	if (getargs == 0 && strcmp(name, "/") == 0 && !hasopt(optbuf, "union"))
398		flags |= FLG_UPDATE;
399	else if (skipmounted) {
400		if ((numfs = getmntinfo(&sfp, MNT_WAIT)) == 0) {
401			warn("getmntinfo");
402			return 1;
403		}
404		for(i = 0; i < numfs; i++) {
405			const char *mountedtype = sfp[i].f_fstypename;
406			size_t cmplen = sizeof(sfp[i].f_fstypename);
407
408			/* remove "puffs|" from comparisons, if present */
409#define TYPESIZE (sizeof(PUFFS_TYPEPREFIX)-1)
410			if (strncmp(mountedtype,
411			    PUFFS_TYPEPREFIX, TYPESIZE) == 0) {
412				mountedtype += TYPESIZE;
413				cmplen -= TYPESIZE;
414			}
415
416			/*
417			 * XXX can't check f_mntfromname,
418			 * thanks to mfs, union, etc.
419			 */
420			if (strncmp(name, sfp[i].f_mntonname, MNAMELEN) == 0 &&
421			    strncmp(vfstype, mountedtype, cmplen) == 0) {
422				if (verbose)
423					(void)printf("%s on %s type %.*s: "
424					    "%s\n",
425					    sfp[i].f_mntfromname,
426					    sfp[i].f_mntonname,
427					    (int)sizeof(sfp[i].f_fstypename),
428					    sfp[i].f_fstypename,
429					    "already mounted");
430				return 0;
431			}
432		}
433	}
434	if (flags & FLG_FORCE)
435		catopt(&optbuf, "force");
436	if (flags & FLG_RDONLY)
437		catopt(&optbuf, "ro");
438	/* make -w override -r */
439	if (flags & FLG_RDWRITE)
440		catopt(&optbuf, "rw");
441
442	if (flags & FLG_UPDATE) {
443		catopt(&optbuf, "update");
444		/* Figure out the fstype only if we defaulted to ffs */
445		if (vfstype == ffs_fstype && statvfs(name, &sf) != -1)
446			vfstype = sf.f_fstypename;
447	}
448
449	maxargc = 64;
450	argv = ecalloc(maxargc, sizeof(*argv));
451
452	if (getargs &&
453	    strncmp(vfstype, PUFFS_TYPEPREFIX, sizeof(PUFFS_TYPEPREFIX)-1) == 0)
454		(void)snprintf(execbase, sizeof(execbase), "mount_puffs");
455	else if (hasopt(optbuf, "rump"))
456		(void)snprintf(execbase, sizeof(execbase), "rump_%s", vfstype);
457	else
458		(void)snprintf(execbase, sizeof(execbase), "mount_%s", vfstype);
459	argc = 0;
460	argv[argc++] = execbase;
461	if (optbuf)
462		mangle(optbuf, &argc, &argv, &maxargc);
463	argv[argc++] = spec;
464	argv[argc++] = name;
465	argv[argc] = NULL;
466
467	if ((verbose && buf == NULL) || debug) {
468		(void)printf("exec:");
469		for (i = 0; i < argc; i++)
470			(void)printf(" %s", argv[i]);
471		(void)printf("\n");
472	}
473
474	if (buf) {
475		if (pipe(pfd) == -1)
476			warn("Cannot create pipe");
477	}
478
479	switch (pid = fork()) {
480	case -1:				/* Error. */
481		warn("fork");
482		if (optbuf)
483			free(optbuf);
484		free(argv);
485		return 1;
486
487	case 0:					/* Child. */
488		if (debug)
489			_exit(0);
490
491		if (buf) {
492			(void)close(pfd[0]);
493			(void)close(STDOUT_FILENO);
494			if (dup2(pfd[1], STDOUT_FILENO) == -1)
495				warn("Cannot open fd to mount program");
496		}
497
498		/* Go find an executable. */
499		edir = edirs;
500		do {
501			(void)snprintf(execname,
502			    sizeof(execname), "%s/%s", *edir, execbase);
503			(void)execv(execname, __UNCONST(argv));
504			if (errno != ENOENT)
505				warn("exec %s for %s", execname, name);
506		} while (*++edir != NULL);
507
508		if (errno == ENOENT)
509			warn("exec %s for %s: %s", execbase, name, execbase);
510		_exit(1);
511		/* NOTREACHED */
512
513	default:				/* Parent. */
514		if (optbuf)
515			free(optbuf);
516		free(argv);
517
518		if (buf || getargs) {
519			char tbuf[1024], *ptr;
520			int nread;
521
522			if (buf == NULL) {
523				ptr = tbuf;
524				buflen = sizeof(tbuf) - 1;
525			} else {
526				ptr = buf;
527				buflen--;
528			}
529			(void)close(pfd[1]);
530			(void)signal(SIGPIPE, SIG_IGN);
531			while ((nread = read(pfd[0], ptr, buflen)) > 0) {
532				buflen -= nread;
533				ptr += nread;
534			}
535			*ptr = '\0';
536			if (buflen == 0) {
537				while (read(pfd[0], &nread, sizeof(nread)) > 0)
538					continue;
539			}
540			if (buf == NULL)
541				(void)fprintf(stdout, "%s", tbuf);
542		}
543
544		if (waitpid(pid, &status, 0) == -1) {
545			warn("waitpid");
546			return 1;
547		}
548
549		if (WIFEXITED(status)) {
550			if (WEXITSTATUS(status) != 0)
551				return WEXITSTATUS(status);
552		} else if (WIFSIGNALED(status)) {
553			warnx("%s: %s", name, strsignal(WTERMSIG(status)));
554			return 1;
555		}
556
557		if (buf == NULL) {
558			if (verbose) {
559				if (statvfs(name, &sf) == -1) {
560					warn("statvfs %s", name);
561					return 1;
562				}
563				prmount(&sf);
564			}
565		}
566		break;
567	}
568
569	return 0;
570}
571
572static void
573prmount(struct statvfs *sfp)
574{
575	int flags;
576	const struct opt *o;
577	struct passwd *pw;
578	int f;
579
580	(void)printf("%s on %s type %.*s", sfp->f_mntfromname,
581	    sfp->f_mntonname, (int)sizeof(sfp->f_fstypename),
582	    sfp->f_fstypename);
583
584	flags = sfp->f_flag & MNT_VISFLAGMASK;
585	for (f = 0, o = optnames; flags && o <
586	    &optnames[sizeof(optnames)/sizeof(optnames[0])]; o++)
587		if (flags & o->o_opt) {
588			if (!o->o_silent || verbose)
589				(void)printf("%s%s", !f++ ? " (" : ", ",
590				    o->o_name);
591			flags &= ~o->o_opt;
592		}
593	if (flags)
594		(void)printf("%sunknown flag%s %#x", !f++ ? " (" : ", ",
595		    flags & (flags - 1) ? "s" : "", flags);
596	if (sfp->f_owner) {
597		(void)printf("%smounted by ", !f++ ? " (" : ", ");
598		if ((pw = getpwuid(sfp->f_owner)) != NULL)
599			(void)printf("%s", pw->pw_name);
600		else
601			(void)printf("%d", sfp->f_owner);
602	}
603	if (verbose)
604		(void)printf("%sfsid: 0x%x/0x%x",
605		    !f++ ? " (" /* ) */: ", ",
606		    sfp->f_fsidx.__fsid_val[0], sfp->f_fsidx.__fsid_val[1]);
607
608	if (verbose) {
609		(void)printf("%s", !f++ ? " (" : ", ");
610		(void)printf("reads: sync %" PRIu64 " async %" PRIu64 "",
611		    sfp->f_syncreads, sfp->f_asyncreads);
612		(void)printf(", writes: sync %" PRIu64 " async %" PRIu64 "",
613		    sfp->f_syncwrites, sfp->f_asyncwrites);
614		if (verbose > 1) {
615			char buf[2048];
616
617			if (getmntargs(sfp, buf, sizeof(buf)))
618				printf(", [%s: %s]", sfp->f_fstypename, buf);
619		}
620		printf(")\n");
621	} else
622		(void)printf("%s", f ? ")\n" : "\n");
623}
624
625static int
626getmntargs(struct statvfs *sfs, char *buf, size_t buflen)
627{
628
629	if (mountfs(sfs->f_fstypename, sfs->f_mntfromname, sfs->f_mntonname, 0,
630	    "getargs", NULL, 0, buf, buflen))
631		return 0;
632	else {
633		if (*buf == '\0')
634			return 0;
635		if ((buf = strchr(buf, '\n')) != NULL)
636			*buf = '\0';
637		return 1;
638	}
639}
640
641static struct statvfs *
642getmntpt(const char *name)
643{
644	struct statvfs *mntbuf;
645	int i, mntsize;
646
647	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
648	for (i = 0; i < mntsize; i++)
649		if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
650		    strcmp(mntbuf[i].f_mntonname, name) == 0)
651			return &mntbuf[i];
652	return NULL;
653}
654
655static void
656catopt(char **sp, const char *o)
657{
658	char *s, *n;
659
660	s = *sp;
661	if (s) {
662		easprintf(&n, "%s,%s", s, o);
663		free(s);
664		s = n;
665	} else
666		s = estrdup(o);
667	*sp = s;
668}
669
670static void
671mangle(char *options, int *argcp, const char ** volatile *argvp, int *maxargcp)
672{
673	char *p, *s;
674	int argc, maxargc;
675	const char **argv, **nargv;
676
677	argc = *argcp;
678	argv = *argvp;
679	maxargc = *maxargcp;
680
681	for (s = options; (p = strsep(&s, ",")) != NULL;) {
682		/* Always leave space for one more argument and the NULL. */
683		if (argc >= maxargc - 4) {
684			nargv = erealloc(argv, (maxargc << 1) * sizeof(nargv));
685			argv = nargv;
686			maxargc <<= 1;
687		}
688		if (*p != '\0') {
689			if (*p == '-') {
690				argv[argc++] = p;
691				p = strchr(p, '=');
692				if (p) {
693					*p = '\0';
694					argv[argc++] = p+1;
695				}
696			} else {
697				argv[argc++] = "-o";
698				argv[argc++] = p;
699			}
700		}
701	}
702
703	*argcp = argc;
704	*argvp = argv;
705	*maxargcp = maxargc;
706}
707
708/* Deduce the filesystem type from the disk label. */
709static const char *
710getfslab(const char *str)
711{
712	static struct dkwedge_info dkw;
713	struct disklabel dl;
714	int fd;
715	int part;
716	const char *vfstype;
717	u_char fstype;
718	char buf[MAXPATHLEN + 1];
719	char *sp, *ep;
720
721	if ((fd = open(str, O_RDONLY)) == -1) {
722		/*
723		 * Iff we get EBUSY try the raw device. Since mount always uses
724		 * the block device we know we are never passed a raw device.
725		 */
726		if (errno != EBUSY)
727			err(EXIT_FAILURE, "cannot open `%s'", str);
728		strlcpy(buf, str, MAXPATHLEN);
729		if ((sp = strrchr(buf, '/')) != NULL)
730			++sp;
731		else
732			sp = buf;
733		for (ep = sp + strlen(sp) + 1;  ep > sp; ep--)
734			*ep = *(ep - 1);
735		*sp = 'r';
736
737		/* Silently fail here - mount call can display error */
738		if ((fd = open(buf, O_RDONLY)) == -1)
739			return NULL;
740	}
741
742	/* Check to see if this is a wedge. */
743	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
744		/* Yup, this is easy. */
745		close(fd);
746		if (*dkw.dkw_ptype)
747			return dkw.dkw_ptype;
748		return NULL;
749	}
750
751	if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
752		(void) close(fd);
753		return NULL;
754	}
755
756	(void) close(fd);
757
758	part = str[strlen(str) - 1] - 'a';
759
760	if (part < 0 || part >= dl.d_npartitions)
761		return NULL;
762
763	/* Return NULL for unknown types - caller can fall back to ffs */
764	if ((fstype = dl.d_partitions[part].p_fstype) >= FSMAXMOUNTNAMES)
765		vfstype = NULL;
766	else
767		vfstype = mountnames[fstype];
768
769	return vfstype;
770}
771
772static void
773usage(void)
774{
775
776	(void)fprintf(stderr, "Usage: %s [-Aadfruvw] [-t type]\n"
777	    "\t%s [-dfruvw] special | node\n"
778	    "\t%s [-dfruvw] [-o options] [-t type] special node\n",
779	    getprogname(), getprogname(), getprogname());
780	exit(1);
781}
782