Home | History | Annotate | Line # | Download | only in quot
quot.c revision 1.18
      1 /*	$NetBSD: quot.c,v 1.18 2003/04/02 10:39:50 fvdl 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/cdefs.h>
     35 #ifndef lint
     36 __RCSID("$NetBSD: quot.c,v 1.18 2003/04/02 10:39:50 fvdl Exp $");
     37 #endif /* not lint */
     38 
     39 #include <sys/param.h>
     40 #include <sys/mount.h>
     41 #include <sys/time.h>
     42 #include <ufs/ufs/dinode.h>
     43 #include <ufs/ffs/fs.h>
     44 
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <fcntl.h>
     48 #include <pwd.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <unistd.h>
     53 
     54 /* some flags of what to do: */
     55 static char estimate;
     56 static char count;
     57 static char unused;
     58 static void (*func) __P((int, struct fs *, char *));
     59 static long blocksize;
     60 static char *header;
     61 static int headerlen;
     62 
     63 /*
     64  * Original BSD quot doesn't round to number of frags/blocks,
     65  * doesn't account for indirection blocks and gets it totally
     66  * wrong if the	size is a multiple of the blocksize.
     67  * The new code always counts the number of DEV_BSIZE byte blocks
     68  * instead of the number of kilobytes and converts them	to
     69  * kByte when done (on request).
     70  */
     71 #ifdef	COMPAT
     72 #define	SIZE(n)	((long long)(n))
     73 #else
     74 #define	SIZE(n)	howmany((long long)(n) * DEV_BSIZE, (long long)blocksize)
     75 #endif
     76 
     77 #define	INOCNT(fs)	((fs)->fs_ipg)
     78 #define INOSZ(fs) \
     79 	(((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
     80 	sizeof(struct ufs2_dinode)) * INOCNT(fs))
     81 
     82 union dinode {
     83 	struct ufs1_dinode dp1;
     84 	struct ufs2_dinode dp2;
     85 };
     86 #define       DIP(fs, dp, field) \
     87 	(((fs)->fs_magic == FS_UFS1_MAGIC) ? \
     88 	(dp)->dp1.di_##field : (dp)->dp2.di_##field)
     89 
     90 
     91 static	int		cmpusers __P((const void *, const void *));
     92 static	void		dofsizes __P((int, struct fs *, char *));
     93 static	void		donames __P((int, struct fs *, char *));
     94 static	void		douser __P((int, struct fs *, char *));
     95 static	union dinode  *get_inode __P((int, struct fs*, ino_t));
     96 static	void		ffs_oldfscompat __P((struct fs *));
     97 static	void		initfsizes __P((void));
     98 static	void		inituser __P((void));
     99 static	int		isfree __P((struct fs *, union dinode *));
    100 	int		main __P((int, char **));
    101 	void		quot __P((char *, char *));
    102 static	void		usage __P((void));
    103 static	struct user    *user __P((uid_t));
    104 static	void		uses __P((uid_t, daddr_t, time_t));
    105 static	void		usrrehash __P((void));
    106 static	int		virtualblocks __P((struct fs *, union dinode *));
    107 
    108 
    109 static union dinode *
    110 get_inode(fd, super, ino)
    111 	int fd;
    112 	struct fs *super;
    113 	ino_t ino;
    114 {
    115 	static char *ipbuf;
    116 	static ino_t last;
    117 
    118 	if (fd < 0) {		/* flush cache */
    119 		if (ipbuf) {
    120 			free(ipbuf);
    121 			ipbuf = NULL;
    122 		}
    123 		return 0;
    124 	}
    125 
    126 	if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
    127 		if (!ipbuf
    128 		    && !(ipbuf = malloc(INOSZ(super))))
    129 			errx(1, "allocate inodes");
    130 		last = (ino / INOCNT(super)) * INOCNT(super);
    131 		if (lseek(fd,
    132 		    (off_t)ino_to_fsba(super, last) << super->fs_fshift,
    133 		    0) < 0 ||
    134 		    read(fd, ipbuf, INOSZ(super)) != INOSZ(super))
    135 			errx(1, "read inodes");
    136 	}
    137 
    138 	if (super->fs_magic == FS_UFS1_MAGIC)
    139 		return ((union dinode *)
    140 		    &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
    141 	return ((union dinode *)
    142 	    &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
    143 }
    144 
    145 #ifdef	COMPAT
    146 #define	actualblocks(fs, dp)	(DIP(fs, dp, blocks) / 2)
    147 #else
    148 #define	actualblocks(fs, dp)	(DIP(fs, dp, blocks))
    149 #endif
    150 
    151 static int
    152 virtualblocks(super, dp)
    153 	struct fs *super;
    154 	union dinode *dp;
    155 {
    156 	off_t nblk, sz;
    157 
    158 	sz = DIP(super, dp, size);
    159 #ifdef	COMPAT
    160 	if (lblkno(super, sz) >= NDADDR) {
    161 		nblk = blkroundup(super, sz);
    162 		if (sz == nblk)
    163 			nblk += super->fs_bsize;
    164 	}
    165 
    166 	return sz / 1024;
    167 #else	/* COMPAT */
    168 
    169 	if (lblkno(super, sz) >= NDADDR) {
    170 		nblk = blkroundup(super, sz);
    171 		sz = lblkno(super, nblk);
    172 		sz = howmany(sz - NDADDR, NINDIR(super));
    173 		while (sz > 0) {
    174 			nblk += sz * super->fs_bsize;
    175 			/* One block on this level is in the inode itself */
    176 			sz = howmany(sz - 1, NINDIR(super));
    177 		}
    178 	} else
    179 		nblk = fragroundup(super, sz);
    180 
    181 	return nblk / DEV_BSIZE;
    182 #endif	/* COMPAT */
    183 }
    184 
    185 static int
    186 isfree(fs, dp)
    187 	struct fs *fs;
    188 	union dinode *dp;
    189 {
    190 #ifdef	COMPAT
    191 	return (DIP(fs, dp, mode) & IFMT) == 0;
    192 #else	/* COMPAT */
    193 	switch (DIP(fs, dp, mode) & IFMT) {
    194 	case IFIFO:
    195 	case IFLNK:		/* should check FASTSYMLINK? */
    196 	case IFDIR:
    197 	case IFREG:
    198 		return 0;
    199 	default:
    200 		return 1;
    201 	}
    202 #endif
    203 }
    204 
    205 static struct user {
    206 	uid_t uid;
    207 	char *name;
    208 	daddr_t space;
    209 	long count;
    210 	daddr_t spc30;
    211 	daddr_t spc60;
    212 	daddr_t spc90;
    213 } *users;
    214 static int nusers;
    215 
    216 static void
    217 inituser()
    218 {
    219 	int i;
    220 	struct user *usr;
    221 
    222 	if (!nusers) {
    223 		nusers = 8;
    224 		if (!(users =
    225 		    (struct user *)calloc(nusers, sizeof(struct user))))
    226 			errx(1, "allocate users");
    227 	} else {
    228 		for (usr = users, i = nusers; --i >= 0; usr++) {
    229 			usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
    230 			usr->count = 0;
    231 		}
    232 	}
    233 }
    234 
    235 static void
    236 usrrehash()
    237 {
    238 	int i;
    239 	struct user *usr, *usrn;
    240 	struct user *svusr;
    241 
    242 	svusr = users;
    243 	nusers <<= 1;
    244 	if (!(users = (struct user *)calloc(nusers, sizeof(struct user))))
    245 		errx(1, "allocate users");
    246 	for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
    247 		for (usrn = users + (usr->uid&(nusers - 1));
    248 		     usrn->name;
    249 		     usrn--) {
    250 			if (usrn <= users)
    251 				usrn = users + nusers;
    252 		}
    253 		*usrn = *usr;
    254 	}
    255 }
    256 
    257 static struct user *
    258 user(uid)
    259 	uid_t uid;
    260 {
    261 	struct user *usr;
    262 	int i;
    263 	struct passwd *pwd;
    264 
    265 	while (1) {
    266 		for (usr = users + (uid&(nusers - 1)), i = nusers;
    267 		     --i >= 0;
    268 		     usr--) {
    269 			if (!usr->name) {
    270 				usr->uid = uid;
    271 
    272 				if (!(pwd = getpwuid(uid))) {
    273 					if ((usr->name =
    274 					    (char *)malloc(7)) != NULL)
    275 						sprintf(usr->name, "#%d", uid);
    276 				} else {
    277 					if ((usr->name =
    278 					    (char *)malloc(
    279 						strlen(pwd->pw_name) + 1))
    280 					    != NULL)
    281 						strcpy(usr->name, pwd->pw_name);
    282 				}
    283 				if (!usr->name)
    284 					errx(1, "allocate users");
    285 				return usr;
    286 			} else if (usr->uid == uid)
    287 				return usr;
    288 
    289 			if (usr <= users)
    290 				usr = users + nusers;
    291 		}
    292 		usrrehash();
    293 	}
    294 }
    295 
    296 static int
    297 cmpusers(u1, u2)
    298 	const void *u1, *u2;
    299 {
    300 	return ((struct user *)u2)->space - ((struct user *)u1)->space;
    301 }
    302 
    303 #define	sortusers(users)	(qsort((users), nusers, sizeof(struct user), \
    304 				       cmpusers))
    305 
    306 static void
    307 uses(uid, blks, act)
    308 	uid_t uid;
    309 	daddr_t blks;
    310 	time_t act;
    311 {
    312 	static time_t today;
    313 	struct user *usr;
    314 
    315 	if (!today)
    316 		time(&today);
    317 
    318 	usr = user(uid);
    319 	usr->count++;
    320 	usr->space += blks;
    321 
    322 	if (today - act > 90L * 24L * 60L * 60L)
    323 		usr->spc90 += blks;
    324 	if (today - act > 60L * 24L * 60L * 60L)
    325 		usr->spc60 += blks;
    326 	if (today - act > 30L * 24L * 60L * 60L)
    327 		usr->spc30 += blks;
    328 }
    329 
    330 #ifdef	COMPAT
    331 #define	FSZCNT	500
    332 #else
    333 #define	FSZCNT	512
    334 #endif
    335 struct fsizes {
    336 	struct fsizes *fsz_next;
    337 	daddr_t fsz_first, fsz_last;
    338 	ino_t fsz_count[FSZCNT];
    339 	daddr_t fsz_sz[FSZCNT];
    340 } *fsizes;
    341 
    342 static void
    343 initfsizes()
    344 {
    345 	struct fsizes *fp;
    346 	int i;
    347 
    348 	for (fp = fsizes; fp; fp = fp->fsz_next) {
    349 		for (i = FSZCNT; --i >= 0;) {
    350 			fp->fsz_count[i] = 0;
    351 			fp->fsz_sz[i] = 0;
    352 		}
    353 	}
    354 }
    355 
    356 static void
    357 dofsizes(fd, super, name)
    358 	int fd;
    359 	struct fs *super;
    360 	char *name;
    361 {
    362 	ino_t inode, maxino;
    363 	union dinode *dp;
    364 	daddr_t sz, ksz;
    365 	struct fsizes *fp, **fsp;
    366 	int i;
    367 
    368 	maxino = super->fs_ncg * super->fs_ipg - 1;
    369 #ifdef	COMPAT
    370 	if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
    371 		errx(1, "alloc fsize structure");
    372 #endif	/* COMPAT */
    373 	for (inode = 0; inode < maxino; inode++) {
    374 		errno = 0;
    375 		if ((dp = get_inode(fd, super, inode))
    376 #ifdef	COMPAT
    377 		    && ((DIP(super, dp, mode) & IFMT) == IFREG
    378 			|| (DIP(dp, mode) & IFMT) == IFDIR)
    379 #else	/* COMPAT */
    380 		    && !isfree(super, dp)
    381 #endif	/* COMPAT */
    382 		    ) {
    383 			sz = estimate ? virtualblocks(super, dp) :
    384 			    actualblocks(super, dp);
    385 #ifdef	COMPAT
    386 			if (sz >= FSZCNT) {
    387 				fsizes->fsz_count[FSZCNT-1]++;
    388 				fsizes->fsz_sz[FSZCNT-1] += sz;
    389 			} else {
    390 				fsizes->fsz_count[sz]++;
    391 				fsizes->fsz_sz[sz] += sz;
    392 			}
    393 #else	/* COMPAT */
    394 			ksz = SIZE(sz);
    395 			for (fsp = &fsizes; (fp = *fsp) != NULL;
    396 			    fsp = &fp->fsz_next) {
    397 				if (ksz < fp->fsz_last)
    398 					break;
    399 			}
    400 			if (!fp || ksz < fp->fsz_first) {
    401 				if (!(fp = (struct fsizes *)
    402 				      malloc(sizeof(struct fsizes))))
    403 					errx(1, "alloc fsize structure");
    404 				fp->fsz_next = *fsp;
    405 				*fsp = fp;
    406 				fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
    407 				fp->fsz_last = fp->fsz_first + FSZCNT;
    408 				for (i = FSZCNT; --i >= 0;) {
    409 					fp->fsz_count[i] = 0;
    410 					fp->fsz_sz[i] = 0;
    411 				}
    412 			}
    413 			fp->fsz_count[ksz % FSZCNT]++;
    414 			fp->fsz_sz[ksz % FSZCNT] += sz;
    415 #endif	/* COMPAT */
    416 		} else if (errno)
    417 			errx(1, "%s", name);
    418 	}
    419 	sz = 0;
    420 	for (fp = fsizes; fp; fp = fp->fsz_next) {
    421 		for (i = 0; i < FSZCNT; i++) {
    422 			if (fp->fsz_count[i])
    423 				printf("%ld\t%ld\t%lld\n",
    424 				    (long)(fp->fsz_first + i),
    425 				    (long)fp->fsz_count[i],
    426 				    SIZE(sz += fp->fsz_sz[i]));
    427 		}
    428 	}
    429 }
    430 
    431 static void
    432 douser(fd, super, name)
    433 	int fd;
    434 	struct fs *super;
    435 	char *name;
    436 {
    437 	ino_t inode, maxino;
    438 	struct user *usr, *usrs;
    439 	union dinode *dp;
    440 	int n;
    441 
    442 	maxino = super->fs_ncg * super->fs_ipg - 1;
    443 	for (inode = 0; inode < maxino; inode++) {
    444 		errno = 0;
    445 		if ((dp = get_inode(fd, super, inode))
    446 		    && !isfree(super, dp))
    447 			uses(DIP(super, dp, uid),
    448 			    estimate ? virtualblocks(super, dp) :
    449 			    actualblocks(super, dp), DIP(super, dp, atime));
    450 		else if (errno)
    451 			errx(1, "%s", name);
    452 	}
    453 	if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
    454 		errx(1, "allocate users");
    455 	memmove(usrs, users, nusers * sizeof(struct user));
    456 	sortusers(usrs);
    457 	for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
    458 		printf("%5lld", SIZE(usr->space));
    459 		if (count)
    460 			printf("\t%5ld", usr->count);
    461 		printf("\t%-8s", usr->name);
    462 		if (unused)
    463 			printf("\t%5lld\t%5lld\t%5lld",
    464 			    SIZE(usr->spc30), SIZE(usr->spc60),
    465 			    SIZE(usr->spc90));
    466 		printf("\n");
    467 	}
    468 	free(usrs);
    469 }
    470 
    471 static void
    472 donames(fd, super, name)
    473 	int fd;
    474 	struct fs *super;
    475 	char *name;
    476 {
    477 	int c;
    478 	ino_t inode, inode1;
    479 	ino_t maxino;
    480 	union dinode *dp;
    481 
    482 	maxino = super->fs_ncg * super->fs_ipg - 1;
    483 	/* first skip the name of the filesystem */
    484 	while ((c = getchar()) != EOF && (c < '0' || c > '9'))
    485 		while ((c = getchar()) != EOF && c != '\n');
    486 	ungetc(c, stdin);
    487 	inode1 = -1;
    488 	while (scanf("%d", &inode) == 1) {
    489 		if (inode < 0 || inode > maxino) {
    490 #ifndef	COMPAT
    491 			warnx("invalid inode %d", inode);
    492 #endif
    493 			return;
    494 		}
    495 #ifdef	COMPAT
    496 		if (inode < inode1)
    497 			continue;
    498 #endif
    499 		errno = 0;
    500 		if ((dp = get_inode(fd, super, inode))
    501 		    && !isfree(super, dp)) {
    502 			printf("%s\t", user(DIP(super, dp, uid))->name);
    503 			/* now skip whitespace */
    504 			while ((c = getchar()) == ' ' || c == '\t');
    505 			/* and print out the remainder of the input line */
    506 			while (c != EOF && c != '\n') {
    507 				putchar(c);
    508 				c = getchar();
    509 			}
    510 			putchar('\n');
    511 			inode1 = inode;
    512 		} else {
    513 			if (errno)
    514 				errx(1, "%s", name);
    515 			/* skip this line */
    516 			while ((c = getchar()) != EOF && c != '\n');
    517 		}
    518 		if (c == EOF)
    519 			break;
    520 	}
    521 }
    522 
    523 static void
    524 usage()
    525 {
    526 #ifdef	COMPAT
    527 	fprintf(stderr, "Usage: quot [-nfcvha] [filesystem ...]\n");
    528 #else	/* COMPAT */
    529 	fprintf(stderr, "Usage: quot [ -acfhknv ] [ filesystem ... ]\n");
    530 #endif	/* COMPAT */
    531 	exit(1);
    532 }
    533 
    534 #define	max(a,b)	MAX((a),(b))
    535 /*
    536  * Sanity checks for old file systems.
    537  * Stolen from <sys/lib/libsa/ufs.c>
    538  */
    539 static void
    540 ffs_oldfscompat(fs)
    541 	struct fs *fs;
    542 {
    543 	int i;
    544 
    545 	if (fs->fs_old_inodefmt < FS_44INODEFMT) {
    546 		quad_t sizepb = fs->fs_bsize;
    547 
    548 		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;
    549 		for (i = 0; i < NIADDR; i++) {
    550 			sizepb *= NINDIR(fs);
    551 			fs->fs_maxfilesize += sizepb;
    552 		}
    553 		fs->fs_qbmask = ~fs->fs_bmask;
    554 		fs->fs_qfmask = ~fs->fs_fmask;
    555 	}
    556 }
    557 
    558 /*
    559  * Possible superblock locations ordered from most to least likely.
    560  */
    561 static int sblock_try[] = SBLOCKSEARCH;
    562 static char superblock[SBLOCKSIZE];
    563 
    564 
    565 void
    566 quot(name, mp)
    567 	char *name, *mp;
    568 {
    569 	int fd, i;
    570 	struct fs *fs;
    571 
    572 	get_inode(-1, 0, 0);		/* flush cache */
    573 	inituser();
    574 	initfsizes();
    575 	if ((fd = open(name, 0)) < 0) {
    576 		warn("%s", name);
    577 		return;
    578 	}
    579 
    580 	for (i = 0; sblock_try[i] != -1; i++) {
    581 		if (lseek(fd, sblock_try[i], 0) != sblock_try[i])
    582 			continue;
    583 		if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE)
    584 			continue;
    585 		fs = (struct fs *)superblock;
    586 
    587 		if ((fs->fs_magic == FS_UFS1_MAGIC ||
    588 		    (fs->fs_magic == FS_UFS2_MAGIC &&
    589 		     fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) &&
    590 		    fs->fs_bsize <= MAXBSIZE &&
    591 		    fs->fs_bsize >= sizeof(struct fs))
    592 			break;
    593 	}
    594 	if (sblock_try[i] == -1) {
    595 		warnx("%s: not a BSD filesystem", name);
    596 		close(fd);
    597 		return;
    598 	}
    599 	ffs_oldfscompat((struct fs *)superblock);
    600 	printf("%s:", name);
    601 	if (mp)
    602 		printf(" (%s)", mp);
    603 	putchar('\n');
    604 	(*func)(fd, fs, name);
    605 	close(fd);
    606 }
    607 
    608 int
    609 main(argc, argv)
    610 	int argc;
    611 	char **argv;
    612 {
    613 	char all = 0;
    614 	struct statfs *mp;
    615 	char dev[MNAMELEN + 1];
    616 	char *nm;
    617 	int cnt;
    618 
    619 	func = douser;
    620 #ifndef	COMPAT
    621 	header = getbsize(&headerlen, &blocksize);
    622 #endif
    623 	while (--argc > 0 && **++argv == '-') {
    624 		while (*++*argv) {
    625 			switch (**argv) {
    626 			case 'n':
    627 				func = donames;
    628 				break;
    629 			case 'c':
    630 				func = dofsizes;
    631 				break;
    632 			case 'a':
    633 				all = 1;
    634 				break;
    635 			case 'f':
    636 				count = 1;
    637 				break;
    638 			case 'h':
    639 				estimate = 1;
    640 				break;
    641 #ifndef	COMPAT
    642 			case 'k':
    643 				blocksize = 1024;
    644 				break;
    645 #endif	/* COMPAT */
    646 			case 'v':
    647 				unused = 1;
    648 				break;
    649 			default:
    650 				usage();
    651 			}
    652 		}
    653 	}
    654 	if (all) {
    655 		cnt = getmntinfo(&mp, MNT_NOWAIT);
    656 		for (; --cnt >= 0; mp++) {
    657 			if (!strncmp(mp->f_fstypename, MOUNT_FFS, MFSNAMELEN)) {
    658 				if ((nm =
    659 				    strrchr(mp->f_mntfromname, '/')) != NULL) {
    660 					sprintf(dev, "/dev/r%s", nm + 1);
    661 					nm = dev;
    662 				} else
    663 					nm = mp->f_mntfromname;
    664 				quot(nm, mp->f_mntonname);
    665 			}
    666 		}
    667 	}
    668 	while (--argc >= 0)
    669 		quot(*argv++, 0);
    670 	return 0;
    671 }
    672