xref: /openbsd-src/usr.sbin/quot/quot.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: quot.c,v 1.28 2015/11/12 22:33:07 deraadt Exp $	*/
2 
3 /*
4  * Copyright (C) 1991, 1994 Wolfgang Solfrank.
5  * Copyright (C) 1991, 1994 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>	/* DEV_BSIZE MAXBSIZE */
35 #include <sys/mount.h>
36 #include <sys/time.h>
37 #include <ufs/ufs/dinode.h>
38 #include <ufs/ffs/fs.h>
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <paths.h>
47 #include <pwd.h>
48 #include <unistd.h>
49 
50 /* some flags of what to do: */
51 static char estimate;
52 static char count;
53 static char unused;
54 static void (*func)(int, struct fs *, char *);
55 static int cmpusers(const void *, const void *);
56 void	quot(char *, char *);
57 static long blocksize;
58 static char *header;
59 static int headerlen;
60 
61 /*
62  * Original BSD quot doesn't round to number of frags/blocks,
63  * doesn't account for indirection blocks and gets it totally
64  * wrong if the	size is a multiple of the blocksize.
65  * The new code always counts the number of DEV_BSIZE byte blocks
66  * instead of the number of kilobytes and converts them	to
67  * KByte when done (on request).
68  */
69 #ifdef	COMPAT
70 #define	SIZE(n)	(n)
71 #else
72 #define	SIZE(n)	(howmany(((off_t)(n)) * DEV_BSIZE, blocksize))
73 #endif
74 
75 #define	INOCNT(fs)	((fs)->fs_ipg)
76 #define	INOSZ(fs)	(((fs)->fs_magic == FS_UFS1_MAGIC ? \
77 			    sizeof(struct ufs1_dinode) : \
78 			    sizeof(struct ufs2_dinode)) * INOCNT(fs))
79 
80 union dinode {
81 	struct ufs1_dinode dp1;
82 	struct ufs2_dinode dp2;
83 };
84 #define	DIP(fs, dp, field) \
85 	(((fs)->fs_magic == FS_UFS1_MAGIC) ? \
86 	(dp)->dp1.field : (dp)->dp2.field)
87 
88 static union dinode *
89 get_inode(int fd, struct fs *super, ino_t ino)
90 {
91 	static caddr_t ipbuf;
92 	static struct cg *cgp;
93 	static ino_t last;
94 	static int cg;
95 	struct ufs2_dinode *di2;
96 
97 	if (fd < 0) {		/* flush cache */
98 		if (ipbuf) {
99 			free(ipbuf);
100 			ipbuf = NULL;
101 			if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) {
102 				free(cgp);
103 				cgp = NULL;
104 			}
105 		}
106 		return 0;
107 	}
108 
109 	if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
110 		if (super->fs_magic == FS_UFS2_MAGIC &&
111 		    (!cgp || cg != ino_to_cg(super, ino))) {
112 			cg = ino_to_cg(super, ino);
113 			if (!cgp && !(cgp = malloc(super->fs_cgsize)))
114 				errx(1, "allocate cg");
115 			if (pread(fd, cgp, super->fs_cgsize,
116 			    (off_t)cgtod(super, cg) << super->fs_fshift)
117 			    != super->fs_cgsize)
118 			if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize)
119 				err(1, "read cg");
120 			if (!cg_chkmagic(cgp))
121 				errx(1, "cg has bad magic");
122 		}
123 		if (!ipbuf && !(ipbuf = malloc(INOSZ(super))))
124 			err(1, "allocate inodes");
125 		last = (ino / INOCNT(super)) * INOCNT(super);
126 		if (lseek(fd, (off_t)ino_to_fsba(super, last)
127 		    << super->fs_fshift, SEEK_SET) < 0 ||
128 		    read(fd, ipbuf, INOSZ(super)) != INOSZ(super)) {
129 			err(1, "read inodes");
130 		}
131 	}
132 
133 	if (super->fs_magic == FS_UFS1_MAGIC)
134 		return ((union dinode *)
135 		    &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
136 	di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)];
137 	/* If the inode is unused, it might be unallocated too, so zero it. */
138 	if (isclr(cg_inosused(cgp), ino % super->fs_ipg))
139 		memset(di2, 0, sizeof(*di2));
140 	return ((union dinode *)di2);
141 }
142 
143 #ifdef	COMPAT
144 #define	actualblocks(fs, ip)	(DIP(fs, dp, di_blocks) / 2)
145 #else
146 #define	actualblocks(fs, ip)	DIP(fs, dp, di_blocks)
147 #endif
148 
149 static int
150 virtualblocks(struct fs *super, union dinode *dp)
151 {
152 	off_t nblk, sz;
153 
154 	sz = DIP(super, dp, di_size);
155 #ifdef	COMPAT
156 	if (lblkno(super, sz) >= NDADDR) {
157 		nblk = blkroundup(super, sz);
158 		if (sz == nblk)
159 			nblk += super->fs_bsize;
160 	}
161 
162 	return sz / 1024;
163 #else	/* COMPAT */
164 
165 	if (lblkno(super, sz) >= NDADDR) {
166 		nblk = blkroundup(super, sz);
167 		sz = lblkno(super, nblk);
168 		sz = howmany(sz - NDADDR, NINDIR(super));
169 		while (sz > 0) {
170 			nblk += sz * super->fs_bsize;
171 			/* One block on this level is in the inode itself */
172 			sz = howmany(sz - 1, NINDIR(super));
173 		}
174 	} else
175 		nblk = fragroundup(super, sz);
176 
177 	return nblk / DEV_BSIZE;
178 #endif	/* COMPAT */
179 }
180 
181 static int
182 isfree(struct fs *super, union dinode *dp)
183 {
184 #ifdef	COMPAT
185 	return (DIP(super, dp, di_mode) & IFMT) == 0;
186 #else	/* COMPAT */
187 	switch (DIP(super, dp, di_mode) & IFMT) {
188 	case IFIFO:
189 	case IFLNK:		/* should check FASTSYMLINK? */
190 	case IFDIR:
191 	case IFREG:
192 		return 0;
193 	case IFCHR:
194 	case IFBLK:
195 	case IFSOCK:
196 	case 0:
197 		return 1;
198 	default:
199 		errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT);
200 	}
201 #endif
202 }
203 
204 static struct user {
205 	uid_t uid;
206 	char *name;
207 	daddr_t space;
208 	long count;
209 	daddr_t spc30;
210 	daddr_t spc60;
211 	daddr_t spc90;
212 } *users;
213 static int nusers;
214 
215 static void
216 inituser(void)
217 {
218 	int i;
219 	struct user *usr;
220 
221 	if (!nusers) {
222 		nusers = 8;
223 		if (!(users = calloc(nusers, sizeof(struct user)))) {
224 			err(1, "allocate users");
225 		}
226 	} else {
227 		for (usr = users, i = nusers; --i >= 0; usr++) {
228 			usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
229 			usr->count = 0;
230 		}
231 	}
232 }
233 
234 static void
235 usrrehash(void)
236 {
237 	int i;
238 	struct user *usr, *usrn;
239 	struct user *svusr;
240 
241 	svusr = users;
242 	nusers <<= 1;
243 	if (!(users = calloc(nusers, sizeof(struct user))))
244 		err(1, "allocate users");
245 	for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
246 		for (usrn = users + (usr->uid&(nusers - 1));
247 		     usrn->name;
248 		    usrn--) {
249 			if (usrn <= users)
250 				usrn = users + nusers;
251 		}
252 		*usrn = *usr;
253 	}
254 }
255 
256 static struct user *
257 user(uid_t uid)
258 {
259 	int i;
260 	struct passwd *pwd;
261 	struct user *usr;
262 
263 	while (1) {
264 		for (usr = users + (uid&(nusers - 1)), i = nusers;
265 		     --i >= 0;
266 		    usr--) {
267 			if (!usr->name) {
268 				usr->uid = uid;
269 
270 				if (!(pwd = getpwuid(uid)))
271 					asprintf(&usr->name, "#%u", uid);
272 				else
273 					usr->name = strdup(pwd->pw_name);
274 				if (!usr->name)
275 					err(1, "allocate users");
276 				return usr;
277 			} else if (usr->uid == uid)
278 				return usr;
279 
280 			if (usr <= users)
281 				usr = users + nusers;
282 		}
283 		usrrehash();
284 	}
285 }
286 
287 static int
288 cmpusers(const void *v1, const void *v2)
289 {
290 	const struct user *u1 = v1, *u2 = v2;
291 
292 	return u2->space - u1->space;
293 }
294 
295 #define	sortusers(users)	(qsort((users), nusers, sizeof(struct user), \
296 				    cmpusers))
297 
298 static void
299 uses(uid_t uid, daddr_t blks, time_t act)
300 {
301 	static time_t today;
302 	struct user *usr;
303 
304 	if (!today)
305 		time(&today);
306 
307 	usr = user(uid);
308 	usr->count++;
309 	usr->space += blks;
310 
311 	if (today - act > 90L * 24L * 60L * 60L)
312 		usr->spc90 += blks;
313 	if (today - act > 60L * 24L * 60L * 60L)
314 		usr->spc60 += blks;
315 	if (today - act > 30L * 24L * 60L * 60L)
316 		usr->spc30 += blks;
317 }
318 
319 #ifdef	COMPAT
320 #define	FSZCNT	500
321 #else
322 #define	FSZCNT	512
323 #endif
324 struct fsizes {
325 	struct fsizes *fsz_next;
326 	daddr_t fsz_first, fsz_last;
327 	ino_t fsz_count[FSZCNT];
328 	daddr_t fsz_sz[FSZCNT];
329 } *fsizes;
330 
331 static void
332 initfsizes(void)
333 {
334 	struct fsizes *fp;
335 	int i;
336 
337 	for (fp = fsizes; fp; fp = fp->fsz_next) {
338 		for (i = FSZCNT; --i >= 0;) {
339 			fp->fsz_count[i] = 0;
340 			fp->fsz_sz[i] = 0;
341 		}
342 	}
343 }
344 
345 static void
346 dofsizes(int fd, struct fs *super, char *name)
347 {
348 	ino_t inode, maxino;
349 	union dinode *dp;
350 	daddr_t sz, ksz;
351 	struct fsizes *fp, **fsp;
352 	int i;
353 
354 	maxino = super->fs_ncg * super->fs_ipg - 1;
355 #ifdef	COMPAT
356 	if (!(fsizes = malloc(sizeof(struct fsizes))))
357 		err(1, "alloc fsize structure");
358 #endif	/* COMPAT */
359 	for (inode = 0; inode < maxino; inode++) {
360 		errno = 0;
361 		if ((dp = get_inode(fd, super, inode))
362 #ifdef	COMPAT
363 		    && ((DIP(super, dp, di_mode) & IFMT) == IFREG
364 			|| (DIP(super, dp, di_mode) & IFMT) == IFDIR)
365 #else	/* COMPAT */
366 		    && !isfree(super, dp)
367 #endif	/* COMPAT */
368 		    ) {
369 			sz = estimate ? virtualblocks(super, dp) :
370 			    actualblocks(super, dp);
371 #ifdef	COMPAT
372 			if (sz >= FSZCNT) {
373 				fsizes->fsz_count[FSZCNT-1]++;
374 				fsizes->fsz_sz[FSZCNT-1] += sz;
375 			} else {
376 				fsizes->fsz_count[sz]++;
377 				fsizes->fsz_sz[sz] += sz;
378 			}
379 #else	/* COMPAT */
380 			ksz = SIZE(sz);
381 			for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
382 				if (ksz < fp->fsz_last)
383 					break;
384 			}
385 			if (!fp || ksz < fp->fsz_first) {
386 				if (!(fp = (struct fsizes *)
387 				    malloc(sizeof(struct fsizes)))) {
388 					err(1, "alloc fsize structure");
389 				}
390 				fp->fsz_next = *fsp;
391 				*fsp = fp;
392 				fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
393 				fp->fsz_last = fp->fsz_first + FSZCNT;
394 				for (i = FSZCNT; --i >= 0;) {
395 					fp->fsz_count[i] = 0;
396 					fp->fsz_sz[i] = 0;
397 				}
398 			}
399 			fp->fsz_count[ksz % FSZCNT]++;
400 			fp->fsz_sz[ksz % FSZCNT] += sz;
401 #endif	/* COMPAT */
402 		} else if (errno)
403 			err(1, "%s", name);
404 	}
405 	sz = 0;
406 	for (fp = fsizes; fp; fp = fp->fsz_next) {
407 		for (i = 0; i < FSZCNT; i++) {
408 			if (fp->fsz_count[i])
409 				printf("%lld\t%llu\t%lld\n",
410 				    (long long)fp->fsz_first + i,
411 				    (unsigned long long)fp->fsz_count[i],
412 				    SIZE(sz += fp->fsz_sz[i]));
413 		}
414 	}
415 }
416 
417 static void
418 douser(int fd, struct fs *super, char *name)
419 {
420 	ino_t inode, maxino;
421 	struct user *usr, *usrs;
422 	union dinode *dp;
423 	int n;
424 
425 	maxino = super->fs_ncg * super->fs_ipg - 1;
426 	for (inode = 0; inode < maxino; inode++) {
427 		errno = 0;
428 		if ((dp = get_inode(fd,super,inode))
429 		    && !isfree(super, dp))
430 			uses(DIP(super, dp, di_uid),
431 			    estimate ? virtualblocks(super, dp) :
432 				actualblocks(super, dp),
433 			    DIP(super, dp, di_atime));
434 		else if (errno)
435 			err(1, "%s", name);
436 	}
437 	if (!(usrs = calloc(nusers, sizeof(struct user))))
438 		err(1, "allocate users");
439 	memcpy(usrs, users, nusers * sizeof(struct user));
440 	sortusers(usrs);
441 	for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
442 		printf("%14lld", SIZE(usr->space));
443 		if (count)
444 			printf("\t%5ld", usr->count);
445 		printf("\t%-8s", usr->name);
446 		if (unused)
447 			printf("\t%14lld\t%14lld\t%14lld",
448 			       SIZE(usr->spc30),
449 			       SIZE(usr->spc60),
450 			       SIZE(usr->spc90));
451 		printf("\n");
452 	}
453 	free(usrs);
454 }
455 
456 static void
457 donames(int fd, struct fs *super, char *name)
458 {
459 	int c;
460 	unsigned long long inode;
461 	ino_t inode1;
462 	ino_t maxino;
463 	union dinode *dp;
464 
465 	maxino = super->fs_ncg * super->fs_ipg - 1;
466 	/* first skip the name of the filesystem */
467 	while ((c = getchar()) != EOF && (c < '0' || c > '9'))
468 		while ((c = getchar()) != EOF && c != '\n');
469 	ungetc(c, stdin);
470 	inode1 = -1;
471 	while (scanf("%llu", &inode) == 1) {
472 		if (inode < 0 || inode > maxino) {
473 #ifndef	COMPAT
474 			fprintf(stderr, "invalid inode %llu\n",
475 			    (unsigned long long)inode);
476 #endif
477 			return;
478 		}
479 #ifdef	COMPAT
480 		if (inode < inode1)
481 			continue;
482 #endif
483 		errno = 0;
484 		if ((dp = get_inode(fd, super, inode)) && !isfree(super, dp)) {
485 			printf("%s\t", user(DIP(super, dp, di_uid))->name);
486 			/* now skip whitespace */
487 			while ((c = getchar()) == ' ' || c == '\t');
488 			/* and print out the remainder of the input line */
489 			while (c != EOF && c != '\n') {
490 				putchar(c);
491 				c = getchar();
492 			}
493 			putchar('\n');
494 			inode1 = inode;
495 		} else {
496 			if (errno)
497 				err(1, "%s", name);
498 			/* skip this line */
499 			while ((c = getchar()) != EOF && c != '\n')
500 				;
501 		}
502 		if (c == EOF)
503 			break;
504 	}
505 }
506 
507 static void
508 usage(void)
509 {
510 #ifdef	COMPAT
511 	fprintf(stderr, "usage: quot [-nfcvha] [filesystem ...]\n");
512 #else	/* COMPAT */
513 	fprintf(stderr, "usage: quot [-acfhknv] [filesystem ...]\n");
514 #endif	/* COMPAT */
515 	exit(1);
516 }
517 
518 /*
519  * Possible superblock locations ordered from most to least likely.
520  */
521 static int sblock_try[] = SBLOCKSEARCH;
522 static char superblock[SBLOCKSIZE];
523 
524 #define	max(a,b)	MAX((a),(b))
525 /*
526  * Sanity checks for old file systems.
527  * Stolen from <sys/lib/libsa/ufs.c>
528  */
529 static void
530 ffs_oldfscompat(struct fs *fs)
531 {
532 	int i;
533 
534 	fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);	/* XXX */
535 	fs->fs_interleave = max(fs->fs_interleave, 1);		/* XXX */
536 	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
537 		fs->fs_nrpos = 8;				/* XXX */
538 	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
539 		quad_t sizepb = fs->fs_bsize;			/* XXX */
540 								/* XXX */
541 		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;	/* XXX */
542 		for (i = 0; i < NIADDR; i++) {			/* XXX */
543 			sizepb *= NINDIR(fs);			/* XXX */
544 			fs->fs_maxfilesize += sizepb;		/* XXX */
545 		}						/* XXX */
546 		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
547 		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
548 	}							/* XXX */
549 }
550 
551 void
552 quot(char *name, char *mp)
553 {
554 	int i, fd;
555 	struct fs *fs;
556 
557 	get_inode(-1, NULL, 0);		/* flush cache */
558 	inituser();
559 	initfsizes();
560 	/*
561 	 * XXX this is completely broken.  Of course you can't read a
562 	 * directory, well, not anymore.  How to fix this, though...
563 	 */
564 	if ((fd = open(name, O_RDONLY)) < 0) {
565 		warn("%s", name);
566 		return;
567 	}
568 	for (i = 0; sblock_try[i] != -1; i++) {
569 		if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
570 			close(fd);
571 			return;
572 		}
573 		if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
574 			close(fd);
575 			return;
576 		}
577 		fs = (struct fs *)superblock;
578 		if ((fs->fs_magic == FS_UFS1_MAGIC ||
579 		    (fs->fs_magic == FS_UFS2_MAGIC &&
580 		    fs->fs_sblockloc == sblock_try[i])) &&
581 		    fs->fs_bsize <= MAXBSIZE &&
582 		    fs->fs_bsize >= sizeof(struct fs))
583 			break;
584 	}
585 	if (sblock_try[i] == -1) {
586 		warnx("%s: not a BSD filesystem", name);
587 		close(fd);
588 		return;
589 	}
590 	ffs_oldfscompat(fs);
591 	printf("%s:", name);
592 	if (mp)
593 		printf(" (%s)", mp);
594 	putchar('\n');
595 	(*func)(fd, fs, name);
596 	close(fd);
597 }
598 
599 int
600 main(int argc, char *argv[])
601 {
602 	int cnt, all, i;
603 	char dev[MNAMELEN], *nm, *mountpoint, *cp;
604 	struct statfs *mp;
605 
606 	all = 0;
607 	func = douser;
608 #ifndef	COMPAT
609 	header = getbsize(&headerlen, &blocksize);
610 #endif
611 	while (--argc > 0 && **++argv == '-') {
612 		while (*++*argv) {
613 			switch (**argv) {
614 			case 'n':
615 				func = donames;
616 				break;
617 			case 'c':
618 				func = dofsizes;
619 				break;
620 			case 'a':
621 				all = 1;
622 				break;
623 			case 'f':
624 				count = 1;
625 				break;
626 			case 'h':
627 				estimate = 1;
628 				break;
629 #ifndef	COMPAT
630 			case 'k':
631 				blocksize = 1024;
632 				break;
633 #endif	/* COMPAT */
634 			case 'v':
635 				unused = 1;
636 				break;
637 			default:
638 				usage();
639 			}
640 		}
641 	}
642 	cnt = getmntinfo(&mp, MNT_NOWAIT);
643 	if (all) {
644 		for (; --cnt >= 0; mp++) {
645 			if (strcmp(mp->f_fstypename, MOUNT_FFS) == 0 ||
646 			    strcmp(mp->f_fstypename, "ufs") == 0) {
647 				if ((nm = strrchr(mp->f_mntfromname, '/'))) {
648 					snprintf(dev, sizeof(dev), "%sr%s",
649 					    _PATH_DEV, nm + 1);
650 					nm = dev;
651 				} else
652 					nm = mp->f_mntfromname;
653 				quot(nm, mp->f_mntonname);
654 			}
655 		}
656 	}
657 	for (; --argc >= 0; argv++) {
658 		mountpoint = NULL;
659 		nm = *argv;
660 
661 		/* Remove trailing slashes from name. */
662 		cp = nm + strlen(nm);
663 		while (*(--cp) == '/' && cp != nm)
664 			*cp = '\0';
665 
666 		/* Look up the name in the mount table. */
667 		for (i = 0; i < cnt; i++) {
668 			/* Remove trailing slashes from name. */
669 			cp = mp[i].f_mntonname + strlen(mp[i].f_mntonname);
670 			while (*(--cp) == '/' && cp != mp[i].f_mntonname)
671 				*cp = '\0';
672 
673 			if ((!strcmp(mp->f_fstypename, MOUNT_FFS) ||
674 			     !strcmp(mp->f_fstypename, MOUNT_MFS) ||
675 			     !strcmp(mp->f_fstypename, "ufs")) &&
676 			    strcmp(nm, mp[i].f_mntonname) == 0) {
677 				nm = mp[i].f_mntfromname;
678 				mountpoint = mp[i].f_mntonname;
679 				break;
680 			}
681 		}
682 
683 		/* Make sure we have the raw device... */
684 		if (strncmp(nm, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0 &&
685 		    nm[sizeof(_PATH_DEV) - 1] != 'r') {
686 			snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV,
687 			    nm + sizeof(_PATH_DEV) - 1);
688 			nm = dev;
689 		}
690 		quot(nm, mountpoint);
691 	}
692 	exit(0);
693 }
694