xref: /titanic_50/usr/src/cmd/fs.d/ufs/quotacheck/quotacheck.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * Fix up / report on disc quotas & usage
44  */
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <ctype.h>
49 #include <signal.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <sys/filio.h>
53 #include <limits.h>
54 #include <sys/param.h>
55 #include <sys/types.h>
56 #include <sys/mntent.h>
57 
58 #include <sys/vnode.h>
59 #include <sys/fs/ufs_inode.h>
60 #include <sys/fs/ufs_fs.h>
61 #include <sys/fs/ufs_quota.h>
62 #include <sys/stat.h>
63 #include <sys/wait.h>
64 #include <sys/mnttab.h>
65 #include <sys/vfstab.h>
66 #include <pwd.h>
67 #include <iso/limits_iso.h>
68 
69 union {
70 	struct	fs	sblk;
71 	char	dummy[MAXBSIZE];
72 } un;
73 #define	sblock	un.sblk
74 
75 #define	ITABSZ	256
76 struct	dinode	itab[ITABSZ];
77 struct	dinode	*dp;
78 
79 struct fileusage {
80 	struct fileusage *fu_next;
81 	ulong_t fu_curfiles;
82 	uint64_t fu_curblocks;
83 	uid_t	fu_uid;
84 };
85 #define	FUHASH 997
86 struct fileusage *fuhead[FUHASH];
87 struct fileusage *lookup(uid_t);
88 struct fileusage *adduid(uid_t);
89 
90 int fi;
91 ino_t ino;
92 struct	dinode	*ginode();
93 char *mntopt(), *hasvfsopt(), *hasmntopt();
94 
95 extern int	optind;
96 extern char	*optarg;
97 extern int	fsync(int);
98 
99 static void acct();
100 static void bread();
101 static void usage();
102 static int chkquota();
103 static int quotactl();
104 static int preen();
105 static int waiter();
106 static int oneof();
107 
108 int	vflag;		/* verbose */
109 int	aflag;		/* all file systems */
110 int	pflag;		/* fsck like parallel check */
111 int	fflag;		/* force flag */
112 
113 #define	QFNAME "quotas"
114 #define	CHUNK	50
115 char **listbuf;
116 struct dqblk zerodqbuf;
117 struct fileusage zerofileusage;
118 
119 void
120 main(int argc, char **argv)
121 {
122 	struct mnttab mntp;
123 	struct vfstab vfsbuf;
124 	char **listp;
125 	int listcnt;
126 	int listmax = 0;
127 	char quotafile[MAXPATHLEN];
128 	FILE *mtab, *vfstab;
129 	int errs = 0;
130 	int	opt;
131 
132 	if ((listbuf = (char **)malloc(sizeof (char *) * CHUNK)) == NULL) {
133 		fprintf(stderr, "Can't alloc lisbuf array.");
134 		exit(31+1);
135 	}
136 	listmax = CHUNK;
137 	while ((opt = getopt(argc, argv, "vapVf")) != EOF) {
138 		switch (opt) {
139 
140 		case 'v':
141 			vflag++;
142 			break;
143 
144 		case 'a':
145 			aflag++;
146 			break;
147 
148 		case 'p':
149 			pflag++;
150 			break;
151 
152 		case 'V':		/* Print command line */
153 			{
154 				char		*opt_text;
155 				int		opt_count;
156 
157 				(void) fprintf(stdout, "quotacheck -F UFS ");
158 				for (opt_count = 1; opt_count < argc;
159 				    opt_count++) {
160 					opt_text = argv[opt_count];
161 					if (opt_text)
162 						(void) fprintf(stdout, " %s ",
163 						    opt_text);
164 				}
165 				(void) fprintf(stdout, "\n");
166 			}
167 			break;
168 
169 		case 'f':
170 			fflag++;
171 			break;
172 
173 		case '?':
174 			usage();
175 		}
176 	}
177 	if (argc <= optind && !aflag) {
178 		usage();
179 	}
180 
181 	if (quotactl(Q_ALLSYNC, NULL, (uid_t)0, NULL) < 0 &&
182 	    errno == EINVAL && vflag)
183 		printf("Warning: Quotas are not compiled into this kernel\n");
184 	sync();
185 
186 	if (aflag) {
187 		/*
188 		 * Go through vfstab and make a list of appropriate
189 		 * filesystems.
190 		 */
191 		listp = listbuf;
192 		listcnt = 0;
193 		if ((vfstab = fopen(VFSTAB, "r")) == NULL) {
194 			fprintf(stderr, "Can't open ");
195 			perror(VFSTAB);
196 			exit(31+8);
197 		}
198 		while (getvfsent(vfstab, &vfsbuf) == NULL) {
199 			if (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) != 0 ||
200 			    (vfsbuf.vfs_mntopts == 0) ||
201 			    hasvfsopt(&vfsbuf, MNTOPT_RO) ||
202 			    (!hasvfsopt(&vfsbuf, MNTOPT_RQ) &&
203 			    !hasvfsopt(&vfsbuf, MNTOPT_QUOTA)))
204 				continue;
205 			*listp = malloc(strlen(vfsbuf.vfs_special) + 1);
206 			strcpy(*listp, vfsbuf.vfs_special);
207 			listp++;
208 			listcnt++;
209 			/* grow listbuf if needed */
210 			if (listcnt >= listmax) {
211 				listmax += CHUNK;
212 				listbuf = (char **)realloc(listbuf,
213 					sizeof (char *) * listmax);
214 				if (listbuf == NULL) {
215 					fprintf(stderr,
216 						"Can't grow listbuf.\n");
217 					exit(31+1);
218 				}
219 				listp = &listbuf[listcnt];
220 			}
221 		}
222 		fclose(vfstab);
223 		*listp = (char *)0;
224 		listp = listbuf;
225 	} else {
226 		listp = &argv[optind];
227 		listcnt = argc - optind;
228 	}
229 	if (pflag) {
230 		errs = preen(listcnt, listp);
231 	} else {
232 		if ((mtab = fopen(MNTTAB, "r")) == NULL) {
233 			fprintf(stderr, "Can't open ");
234 			perror(MNTTAB);
235 			exit(31+8);
236 		}
237 		while (getmntent(mtab, &mntp) == NULL) {
238 			if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) == 0 &&
239 			    !hasmntopt(&mntp, MNTOPT_RO) &&
240 			    (oneof(mntp.mnt_special, listp, listcnt) ||
241 			    oneof(mntp.mnt_mountp, listp, listcnt))) {
242 				(void) snprintf(quotafile, sizeof (quotafile),
243 					"%s/%s", mntp.mnt_mountp, QFNAME);
244 				errs +=
245 				    chkquota(mntp.mnt_special,
246 					mntp.mnt_mountp, quotafile);
247 			}
248 		}
249 		fclose(mtab);
250 	}
251 	while (listcnt--) {
252 		if (*listp) {
253 			fprintf(stderr, "Cannot check %s\n", *listp);
254 			errs++;
255 		}
256 		listp++;
257 	}
258 	if (errs > 0)
259 		errs += 31;
260 	exit(errs);
261 }
262 
263 static struct active {
264 	char *rdev;
265 	pid_t pid;
266 	struct active *nxt;
267 };
268 
269 int
270 preen(listcnt, listp)
271 	int listcnt;
272 	char **listp;
273 {
274 	register int i, rc, errs;
275 	register char **lp, *rdev, *bdev;
276 	extern char *getfullrawname(), *getfullblkname();
277 	struct mnttab mntp, mpref;
278 	struct active *alist, *ap;
279 	FILE *mtab;
280 	char quotafile[MAXPATHLEN];
281 	char name[MAXPATHLEN];
282 	int nactive, serially;
283 
284 	if ((mtab = fopen(MNTTAB, "r")) == NULL) {
285 		fprintf(stderr, "Can't open ");
286 		perror(MNTTAB);
287 		exit(31+8);
288 	}
289 	memset(&mpref, 0, sizeof (struct mnttab));
290 	errs = 0;
291 
292 	for (lp = listp, i = 0; i < listcnt; lp++, i++) {
293 		serially = 0;
294 		rdev = getfullrawname(*lp);
295 		if (rdev == NULL || *rdev == '\0') {
296 			fprintf(stderr, "can't get rawname for `%s'\n", *lp);
297 			serially = 1;
298 		} else if (preen_addev(rdev) != 0) {
299 			fprintf(stderr, "preen_addev error\n");
300 			serially = 1;
301 		}
302 
303 		if (rdev != NULL)
304 			free(rdev);
305 
306 		if (serially) {
307 			rewind(mtab);
308 			mpref.mnt_special = *lp;
309 			if (getmntany(mtab, &mntp, &mpref) == 0 &&
310 			    strcmp(mntp.mnt_fstype, MNTTYPE_UFS) == 0 &&
311 			    !hasmntopt(&mntp, MNTOPT_RO)) {
312 				errs += (31+chkquota(mntp.mnt_special,
313 				    mntp.mnt_mountp, quotafile));
314 				*lp = (char *)0;
315 			}
316 		}
317 	}
318 
319 	nactive = 0;
320 	alist = NULL;
321 	while ((rc = preen_getdev(name)) > 0) {
322 		switch (rc) {
323 		case 1:
324 			bdev = getfullblkname(name);
325 			if (bdev == NULL || *bdev == '\0') {
326 				fprintf(stderr, "can't get blkname for `%s'\n",
327 				    name);
328 				if (bdev)
329 					free(bdev);
330 				continue;
331 			}
332 			rewind(mtab);
333 			mpref.mnt_special = bdev;
334 			if (getmntany(mtab, &mntp, &mpref) != 0) {
335 				fprintf(stderr, "`%s' not mounted?\n", name);
336 				preen_releasedev(name);
337 				free(bdev);
338 				continue;
339 			} else if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
340 			    hasmntopt(&mntp, MNTOPT_RO) ||
341 			    (!oneof(mntp.mnt_special, listp, listcnt) &&
342 			    !oneof(mntp.mnt_mountp, listp, listcnt))) {
343 				preen_releasedev(name);
344 				free(bdev);
345 				continue;
346 			}
347 			free(bdev);
348 			ap = (struct active *)malloc(sizeof (struct active));
349 			if (ap == NULL) {
350 				fprintf(stderr, "out of memory\n");
351 				exit(31+8);
352 			}
353 			ap->rdev = (char *)strdup(name);
354 			if (ap->rdev == NULL) {
355 				fprintf(stderr, "out of memory\n");
356 				exit(31+8);
357 			}
358 			ap->nxt = alist;
359 			alist = ap;
360 			switch (ap->pid = fork()) {
361 			case -1:
362 				perror("fork");
363 				exit(31+8);
364 				break;
365 			case 0:
366 				(void) snprintf(quotafile, sizeof (quotafile),
367 					"%s/%s", mntp.mnt_mountp, QFNAME);
368 				exit(31+chkquota(mntp.mnt_special,
369 				    mntp.mnt_mountp, quotafile));
370 				break;
371 			default:
372 				nactive++;
373 				break;
374 			}
375 			break;
376 		case 2:
377 			errs += waiter(&alist);
378 			nactive--;
379 			break;
380 		}
381 	}
382 	fclose(mtab);
383 
384 	while (nactive > 0) {
385 		errs += waiter(&alist);
386 		nactive--;
387 	}
388 	return (errs);
389 }
390 
391 int
392 waiter(alp)
393 	struct active **alp;
394 {
395 	pid_t curpid;
396 	int status;
397 	register struct active *ap, *lap;
398 
399 	curpid = wait(&status);
400 	if (curpid == -1) {
401 		if (errno == ECHILD)
402 			return (0);
403 		perror("wait");
404 		exit(31+8);
405 	}
406 
407 	for (lap = NULL, ap = *alp; ap != NULL; lap = ap, ap = ap->nxt) {
408 		if (ap->pid == curpid)
409 			break;
410 	}
411 
412 	if (ap == NULL) {
413 		fprintf(stderr, "wait returns unknown pid\n");
414 		exit(31+8);
415 	} else if (lap) {
416 		lap->nxt = ap->nxt;
417 	} else {
418 		*alp = ap->nxt;
419 	}
420 	preen_releasedev(ap->rdev);
421 	free(ap->rdev);
422 	free(ap);
423 	return (WHIBYTE(status));
424 }
425 
426 int
427 chkquota(fsdev, fsfile, qffile)
428 	char *fsdev;
429 	char *fsfile;
430 	char *qffile;
431 {
432 	register struct fileusage *fup;
433 	dev_t quotadev;
434 	FILE *qf;
435 	uid_t uid;
436 	struct passwd *pw;
437 	int cg, i;
438 	char *rawdisk;
439 	struct stat64 statb;
440 	struct dqblk dqbuf;
441 	extern char *getfullrawname();
442 
443 	if ((rawdisk = getfullrawname(fsdev)) == NULL) {
444 		fprintf(stderr, "malloc failed\n");
445 		return (1);
446 	}
447 
448 	if (*rawdisk == '\0') {
449 		fprintf(stderr, "Could not find character device for %s\n",
450 		    fsdev);
451 		return (1);
452 	}
453 
454 	if (vflag)
455 		printf("*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
456 	fi = open64(rawdisk, 0);
457 	if (fi < 0) {
458 		perror(rawdisk);
459 		return (1);
460 	}
461 	qf = fopen64(qffile, "r+");
462 	if (qf == NULL) {
463 		perror(qffile);
464 		close(fi);
465 		return (1);
466 	}
467 	if (fstat64(fileno(qf), &statb) < 0) {
468 		perror(qffile);
469 		fclose(qf);
470 		close(fi);
471 		return (1);
472 	}
473 	quotadev = statb.st_dev;
474 	if (stat64(fsdev, &statb) < 0) {
475 		perror(fsdev);
476 		fclose(qf);
477 		close(fi);
478 		return (1);
479 	}
480 	if (quotadev != statb.st_rdev) {
481 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
482 			qffile, quotadev, fsdev, statb.st_rdev);
483 		fclose(qf);
484 		close(fi);
485 		return (1);
486 	}
487 	bread((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE);
488 
489 	/*
490 	 * Flush filesystem since we are going to read
491 	 * disk raw and we want to make sure everything is
492 	 * synced to disk before we read it.
493 	 */
494 
495 	if (ioctl(fileno(qf), _FIOFFS, NULL) == -1) {
496 		perror(qffile);
497 		(void) fprintf(stderr, "%s: cannot flush file system.\n",
498 				qffile);
499 		(void) fclose(qf);
500 		return (1);
501 	}
502 
503 	/*
504 	 * no need to quotacheck a rw, mounted, and logging file system
505 	 */
506 	if ((fflag == 0) && pflag &&
507 	    (FSOKAY == (sblock.fs_state + sblock.fs_time)) &&
508 	    (sblock.fs_clean == FSLOG)) {
509 		fclose(qf);
510 		close(fi);
511 		return (0);
512 	}
513 	ino = 0;
514 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
515 		dp = NULL;
516 		for (i = 0; i < sblock.fs_ipg; i++)
517 			acct(ginode());
518 	}
519 	for (uid = 0; uid <= MAXUID && uid >= 0; uid++) {
520 		(void) fread(&dqbuf, sizeof (struct dqblk), 1, qf);
521 		if (feof(qf))
522 			break;
523 		fup = lookup(uid);
524 		if (fup == 0)
525 			fup = &zerofileusage;
526 		if (dqbuf.dqb_bhardlimit == 0 && dqbuf.dqb_bsoftlimit == 0 &&
527 		    dqbuf.dqb_fhardlimit == 0 && dqbuf.dqb_fsoftlimit == 0) {
528 			fup->fu_curfiles = 0;
529 			fup->fu_curblocks = 0;
530 		}
531 		if (dqbuf.dqb_curfiles == fup->fu_curfiles &&
532 		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
533 			fup->fu_curfiles = 0;
534 			fup->fu_curblocks = 0;
535 			continue;
536 		}
537 		/*
538 		 * The maximum number of blocks that can be stored in the
539 		 * dqb_curblocks field in the quota record is 2^32 - 1,
540 		 * since it must fit into an unsigned 32-bit quantity.
541 		 * If this user has more blocks than that, print a message
542 		 * to that effect and reduce the count of allocated blocks
543 		 * to the maximum value, which is UINT_MAX.
544 		 */
545 		if (fup->fu_curblocks > UINT_MAX) {
546 			if (pflag || aflag)
547 				printf("%s: ", rawdisk);
548 			printf("512-byte blocks allocated to user ");
549 			if ((pw = getpwuid(uid)) && pw->pw_name[0])
550 				printf("%-10s ", pw->pw_name);
551 			else
552 				printf("#%-9d ", uid);
553 			printf(" = %lld\n", fup->fu_curblocks);
554 			printf(
555 "This exceeds the maximum number of blocks recordable in a quota record.\n");
556 			printf(
557 "The value will be set to the maximum, which is %lu.\n", UINT_MAX);
558 			fup->fu_curblocks = UINT_MAX;
559 		}
560 
561 		if (vflag) {
562 			if (pflag || aflag)
563 				printf("%s: ", rawdisk);
564 			if ((pw = getpwuid(uid)) && pw->pw_name[0])
565 				printf("%-10s fixed:", pw->pw_name);
566 			else
567 				printf("#%-9d fixed:", uid);
568 			if (dqbuf.dqb_curfiles != fup->fu_curfiles)
569 				printf("  files %lu -> %lu",
570 				    dqbuf.dqb_curfiles, fup->fu_curfiles);
571 			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
572 				printf("  blocks %lu -> %llu",
573 				    dqbuf.dqb_curblocks, fup->fu_curblocks);
574 			printf("\n");
575 		}
576 		dqbuf.dqb_curfiles = fup->fu_curfiles;
577 		dqbuf.dqb_curblocks = fup->fu_curblocks;
578 		/*
579 		 * If quotas are not enabled for the current filesystem
580 		 * then just update the quotas file directly.
581 		 */
582 		if ((quotactl(Q_SETQUOTA, fsfile, uid, &dqbuf) < 0) &&
583 		    (errno == ESRCH)) {
584 			/* back up, overwrite the entry we just read */
585 			(void) fseeko64(qf, (offset_t)dqoff(uid), 0);
586 			(void) fwrite(&dqbuf, sizeof (struct dqblk), 1, qf);
587 			(void) fflush(qf);
588 		}
589 		fup->fu_curfiles = 0;
590 		fup->fu_curblocks = 0;
591 	}
592 	(void) fflush(qf);
593 	(void) fsync(fileno(qf));
594 	fclose(qf);
595 	close(fi);
596 	return (0);
597 }
598 
599 void
600 acct(ip)
601 	register struct dinode *ip;
602 {
603 	register struct fileusage *fup;
604 
605 	if (ip == NULL)
606 		return;
607 	ip->di_mode = ip->di_smode;
608 	if (ip->di_suid != UID_LONG) {
609 		ip->di_uid = ip->di_suid;
610 	}
611 	if (ip->di_mode == 0)
612 		return;
613 	fup = adduid(ip->di_uid);
614 	fup->fu_curfiles++;
615 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
616 		return;
617 	fup->fu_curblocks += ip->di_blocks;
618 }
619 
620 int
621 oneof(target, olistp, on)
622 	char *target;
623 	register char **olistp;
624 	register int on;
625 {
626 	char **listp = olistp;
627 	int n = on;
628 
629 	while (n--) {
630 		if (*listp && strcmp(target, *listp) == 0) {
631 			*listp = (char *)0;
632 			return (1);
633 		}
634 		listp++;
635 	}
636 	return (0);
637 }
638 
639 struct dinode *
640 ginode()
641 {
642 	ulong_t iblk;
643 
644 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
645 		iblk = itod(&sblock, ino);
646 		bread(fsbtodb(&sblock, iblk),
647 		    (char *)itab, sizeof (itab));
648 		dp = &itab[(int)ino % (int)INOPB(&sblock)];
649 	}
650 	if (ino++ < UFSROOTINO)
651 		return (NULL);
652 	return (dp);
653 }
654 
655 void
656 bread(diskaddr_t bno, char *buf, int cnt)
657 {
658 	extern offset_t llseek();
659 	offset_t pos;
660 
661 	pos = (offset_t)bno * DEV_BSIZE;
662 	if (llseek(fi, pos, 0) != pos) {
663 		perror("lseek");
664 		exit(31+1);
665 	}
666 	if (read(fi, buf, cnt) != cnt) {
667 		perror("read");
668 		exit(31+1);
669 	}
670 }
671 
672 struct fileusage *
673 lookup(uid_t uid)
674 {
675 	register struct fileusage *fup;
676 
677 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
678 		if (fup->fu_uid == uid)
679 			return (fup);
680 	return ((struct fileusage *)0);
681 }
682 
683 struct fileusage *
684 adduid(uid_t uid)
685 {
686 	struct fileusage *fup, **fhp;
687 
688 	fup = lookup(uid);
689 	if (fup != 0)
690 		return (fup);
691 	fup = (struct fileusage *)calloc(1, sizeof (struct fileusage));
692 	if (fup == 0) {
693 		fprintf(stderr, "out of memory for fileusage structures\n");
694 		exit(31+1);
695 	}
696 	fhp = &fuhead[uid % FUHASH];
697 	fup->fu_next = *fhp;
698 	*fhp = fup;
699 	fup->fu_uid = uid;
700 	return (fup);
701 }
702 
703 void
704 usage()
705 {
706 	fprintf(stderr, "ufs usage:\n");
707 	fprintf(stderr, "\tquotacheck [-v] [-f] [-p] -a\n");
708 	fprintf(stderr, "\tquotacheck [-v] [-f] [-p] filesys ...\n");
709 	exit(31+1);
710 }
711 
712 int
713 quotactl(cmd, mountp, uid, addr)
714 	int		cmd;
715 	char		*mountp;
716 	uid_t		uid;
717 	caddr_t		addr;
718 {
719 	int 		fd;
720 	int 		status;
721 	struct quotctl	quota;
722 	char		qfile[MAXPATHLEN];
723 	FILE		*fstab;
724 	struct mnttab	mntp;
725 
726 
727 	if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
728 		/*
729 		 * Find the mount point of any ufs file system.   This is
730 		 * because the ioctl that implements the quotactl call has
731 		 * to go to a real file, and not to the block device.
732 		 */
733 		if ((fstab = fopen(MNTTAB, "r")) == NULL) {
734 			fprintf(stderr, "%s: ", MNTTAB);
735 			perror("open");
736 			exit(31+1);
737 		}
738 		fd = -1;
739 		while ((status = getmntent(fstab, &mntp)) == NULL) {
740 			if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
741 				hasmntopt(&mntp, MNTOPT_RO))
742 				continue;
743 			if ((strlcpy(qfile, mntp.mnt_mountp,
744 				sizeof (qfile)) >= sizeof (qfile)) ||
745 			    (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
746 				sizeof (qfile))) {
747 				continue;
748 			}
749 			if ((fd = open64(qfile, O_RDWR)) == -1)
750 				break;
751 		}
752 		fclose(fstab);
753 		if (fd == -1) {
754 			errno = ENOENT;
755 			return (-1);
756 		}
757 	} else {
758 		if (mountp == NULL || mountp[0] == '\0') {
759 			errno =  ENOENT;
760 			return (-1);
761 		}
762 		if ((strlcpy(qfile, mountp, sizeof (qfile)) >=
763 			sizeof (qfile)) ||
764 		    (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
765 			sizeof (qfile))) {
766 			errno = ENOENT;
767 			return (-1);
768 		}
769 		if ((fd = open64(qfile, O_RDWR)) < 0) {
770 			fprintf(stderr, "quotactl: ");
771 			perror("open");
772 			exit(31+1);
773 		}
774 	}	/* else */
775 
776 	quota.op = cmd;
777 	quota.uid = uid;
778 	quota.addr = addr;
779 	status = ioctl(fd, Q_QUOTACTL, &quota);
780 	if (fd != 0)
781 		close(fd);
782 	return (status);
783 }
784 
785 char *
786 hasvfsopt(vfs, opt)
787 	register struct vfstab *vfs;
788 	register char *opt;
789 {
790 	char *f, *opts;
791 	static char *tmpopts;
792 
793 	if (tmpopts == 0) {
794 		tmpopts = (char *)calloc(256, sizeof (char));
795 		if (tmpopts == 0)
796 			return (0);
797 	}
798 	strcpy(tmpopts, vfs->vfs_mntopts);
799 	opts = tmpopts;
800 	f = mntopt(&opts);
801 	for (; *f; f = mntopt(&opts)) {
802 		if (strncmp(opt, f, strlen(opt)) == 0)
803 			return (f - tmpopts + vfs->vfs_mntopts);
804 	}
805 	return (NULL);
806 }
807