xref: /illumos-gate/usr/src/cmd/fs.d/ufs/edquota/edquota.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 /*
42  * Disk quota editor.
43  */
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <signal.h>
47 #include <errno.h>
48 #include <pwd.h>
49 #include <ctype.h>
50 #include <fcntl.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <sys/mnttab.h>
54 #include <sys/param.h>
55 #include <sys/types.h>
56 #include <sys/mntent.h>
57 #include <sys/stat.h>
58 #include <sys/file.h>
59 #include <sys/fs/ufs_quota.h>
60 #include <sys/fs/ufs_fs.h>
61 #include <sys/wait.h>
62 #include <unistd.h>
63 #include <iso/limits_iso.h>
64 
65 #define	DEFEDITOR	"/usr/bin/vi"
66 
67 #if DEV_BSIZE < 1024
68 #define	dbtok(x)	((x) / (1024 / DEV_BSIZE))
69 #define	ktodb(x)	((x) * (1024 / DEV_BSIZE))
70 #else
71 #define	dbtok(x)	((x) * (DEV_BSIZE / 1024))
72 #define	ktodb(x)	((x) / (DEV_BSIZE / 1024))
73 #endif
74 
75 struct fsquot {
76 	struct fsquot *fsq_next;
77 	struct dqblk fsq_dqb;
78 	char *fsq_fs;
79 	char *fsq_dev;
80 	char *fsq_qfile;
81 };
82 
83 static struct fsquot *fsqlist;
84 
85 static char	tmpfil[] = "/tmp/EdP.aXXXXXX";
86 #define	QFNAME	"quotas"
87 
88 static uid_t getentry(char *);
89 static int editit(void);
90 static void getprivs(uid_t);
91 static void putprivs(uid_t);
92 static void gettimes(uid_t);
93 static void puttimes(uid_t);
94 static char *next(char *, char *);
95 static int alldigits(char *);
96 static void fmttime(char *, ulong_t);
97 static int unfmttime(double, char *, uint32_t *);
98 static void setupfs(void);
99 static void getdiscq(uid_t);
100 static void putdiscq(uid_t);
101 static void sigsetmask(uint_t);
102 static uint_t sigblock(uint_t);
103 static void usage(void);
104 static int quotactl(int, char *, uid_t, caddr_t);
105 
106 int
107 main(int argc, char **argv)
108 {
109 	uid_t	uid;
110 	char	*basename;
111 	int	opt;
112 	int	i;
113 	int	tmpfd = -1;
114 
115 	basename = argv[0];
116 	if (argc < 2) {
117 		usage();
118 	}
119 	if (quotactl(Q_SYNC, (char *)NULL, 0, (caddr_t)NULL) < 0 &&
120 	    errno == EINVAL) {
121 		(void) printf("Warning: "
122 			"Quotas are not compiled into this kernel\n");
123 		(void) sleep(3);
124 	}
125 	if (getuid()) {
126 		(void) fprintf(stderr, "%s: permission denied\n", basename);
127 		exit(32);
128 	}
129 	setupfs();
130 	if (fsqlist == NULL) {
131 		(void) fprintf(stderr, "%s: no UFS filesystems with %s file\n",
132 		    MNTTAB, QFNAME);
133 		exit(32);
134 	}
135 	tmpfd = mkstemp(tmpfil);
136 	if (tmpfd == -1 || fchown(tmpfd, getuid(), getgid()) == -1) {
137 		fprintf(stderr, "failure in temporary file %s\n", tmpfil);
138 		exit(32);
139 	}
140 	(void) close(tmpfd);
141 	while ((opt = getopt(argc, argv, "p:tV")) != EOF)
142 		switch (opt) {
143 		case 't':
144 			gettimes(0);
145 			if (editit())
146 				puttimes(0);
147 			(void) unlink(tmpfil);
148 			exit(0);
149 			/*NOTREACHED*/
150 
151 		case 'p':
152 			uid = getentry(optarg);
153 			if (uid > MAXUID) {
154 				(void) unlink(tmpfil);
155 				exit(32);
156 			}
157 			getprivs(uid);
158 			if (optind == argc) {
159 				(void) unlink(tmpfil);
160 				usage();
161 			}
162 			for (i = optind; i < argc; i++) {
163 				uid = getentry(argv[i]);
164 				if (uid > MAXUID) {
165 					(void) unlink(tmpfil);
166 					exit(32);
167 				}
168 				getdiscq(uid);
169 				putprivs(uid);
170 			}
171 			(void) unlink(tmpfil);
172 			exit(0);
173 			/*NOTREACHED*/
174 
175 		case 'V':		/* Print command line */
176 			{
177 				char		*optt;
178 				int		optc;
179 
180 				(void) printf("edquota -F UFS");
181 				for (optc = 1; optc < argc; optc++) {
182 					optt = argv[optc];
183 					if (optt)
184 						(void) printf(" %s ", optt);
185 				}
186 				(void) putchar('\n');
187 			}
188 			break;
189 
190 		case '?':
191 			usage();
192 		}
193 
194 	for (i = optind; i < argc; i++) {
195 		uid = getentry(argv[i]);
196 		if (uid > MAXUID)
197 			continue;
198 		getprivs(uid);
199 		if (editit())
200 			putprivs(uid);
201 		if (uid == 0) {
202 			(void) printf("edquota: Note that uid 0's quotas "
203 			    "are used as default values for other users,\n");
204 			(void) printf("not as a limit on the uid 0 user.\n");
205 		}
206 	}
207 	(void) unlink(tmpfil);
208 	return (0);
209 }
210 
211 static uid_t
212 getentry(char *name)
213 {
214 	struct passwd *pw;
215 	uid_t uid;
216 
217 	if (alldigits(name)) {
218 		errno = 0;
219 		uid = strtol(name, NULL, 10);
220 		if (errno == ERANGE) {
221 			/* name would cause overflow in uid */
222 			(void) fprintf(stderr, "edquota: uid %s too large\n",
223 			    name);
224 			(void) sleep(1);
225 			return (-1);
226 		}
227 	} else if (pw = getpwnam(name))
228 		uid = pw->pw_uid;
229 	else {
230 		(void) fprintf(stderr, "%s: no such user\n", name);
231 		(void) sleep(1);
232 		return (-1);
233 	}
234 	return (uid);
235 }
236 
237 #define	RESPSZ	128
238 
239 static int
240 editit(void)
241 {
242 	pid_t pid, xpid;
243 	char *ed;
244 	char resp[RESPSZ];
245 	int status, omask;
246 
247 #define	mask(s)	(1 << ((s) - 1))
248 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
249 
250 	if ((ed = getenv("EDITOR")) == (char *)0)
251 		ed = DEFEDITOR;
252 
253 	/*CONSTANTCONDITION*/
254 	while (1) {
255 		if ((pid = fork()) < 0) {
256 			if (errno == EAGAIN) {
257 				(void) fprintf(stderr,
258 					"You have too many processes\n");
259 				return (0);
260 			}
261 			perror("fork");
262 			return (0);
263 		}
264 		if (pid == 0) {
265 			(void) sigsetmask(omask);
266 			(void) setgid(getgid());
267 			(void) setuid(getuid());
268 			(void) execlp(ed, ed, tmpfil, 0);
269 			(void) fprintf(stderr,
270 				"Can't exec editor \"%s\": ", ed);
271 			perror("");
272 			exit(32);
273 		}
274 		while ((xpid = wait(&status)) >= 0)
275 			if (xpid == pid)
276 				break;
277 
278 		if (!isatty(fileno(stdin))) {	/* Non-interactive */
279 			break;
280 		}
281 
282 		/*
283 		 * Certain editors can exit with a non-zero status even
284 		 * though everything is peachy. Best to ask the user what
285 		 * s/he really wants to do. (N.B.: if we're non-interactive
286 		 * we'll "break" the while loop before we get here.)
287 		 */
288 		if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
289 			(void) printf("Non-zero return from \"%s\", ", ed);
290 			(void) printf("updated file may contain errors.\n");
291 			/*CONSTANTCONDITION*/
292 			while (1) {
293 				(void) printf("Edit again (e) or quit, "
294 				    "discarding changes (q)? ");
295 				(void) fflush(stdout);
296 				if (gets(resp) == NULL) {
297 					return (0);
298 				}
299 				if ((*resp == 'e') || (*resp == 'q')) {
300 					break;
301 				}
302 			}
303 
304 			if (*resp == 'e') {
305 				continue;
306 			} else {
307 				/*
308 				 * Since (*resp == 'q'), then we just
309 				 * want to break out of here and return
310 				 * the failure.
311 				 */
312 				break;
313 			}
314 		} else {
315 			break;	/* Successful return from editor */
316 		}
317 	}
318 	(void) sigsetmask(omask);
319 	return (!status);
320 }
321 
322 static void
323 getprivs(uid_t uid)
324 {
325 	struct fsquot *fsqp;
326 	FILE *fd;
327 
328 	getdiscq(uid);
329 	if ((fd = fopen64(tmpfil, "w")) == NULL) {
330 		(void) fprintf(stderr, "edquota: ");
331 		perror(tmpfil);
332 		(void) unlink(tmpfil);
333 		exit(32);
334 	}
335 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next)
336 		(void) fprintf(fd,
337 		    "fs %s blocks (soft = %lu, hard = %lu) "
338 		    "inodes (soft = %lu, hard = %lu)\n",
339 		    fsqp->fsq_fs,
340 		    dbtok(fsqp->fsq_dqb.dqb_bsoftlimit),
341 		    dbtok(fsqp->fsq_dqb.dqb_bhardlimit),
342 		    fsqp->fsq_dqb.dqb_fsoftlimit,
343 		    fsqp->fsq_dqb.dqb_fhardlimit);
344 	(void) fclose(fd);
345 }
346 
347 static void
348 putprivs(uid_t uid)
349 {
350 	FILE *fd;
351 	uint64_t tmp_bsoftlimit, tmp_bhardlimit, tmp_fsoftlimit,
352 	    tmp_fhardlimit;
353 	char line[BUFSIZ];
354 	int changed = 0;
355 	uint32_t max_limit;
356 	int	quota_entry_printed;
357 
358 	fd = fopen64(tmpfil, "r");
359 	if (fd == NULL) {
360 		(void) fprintf(stderr, "Can't re-read temp file!!\n");
361 		return;
362 	}
363 	while (fgets(line, sizeof (line), fd) != NULL) {
364 		struct fsquot *fsqp;
365 		char *cp, *dp;
366 		int n;
367 
368 		cp = next(line, " \t");
369 		if (cp == NULL)
370 			break;
371 		*cp++ = '\0';
372 		while (*cp && *cp == '\t' && *cp == ' ')
373 			cp++;
374 		dp = cp, cp = next(cp, " \t");
375 		if (cp == NULL)
376 			break;
377 		*cp++ = '\0';
378 		for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
379 			if (strcmp(dp, fsqp->fsq_fs) == 0)
380 				break;
381 		}
382 		if (fsqp == NULL) {
383 			(void) fprintf(stderr, "%s: unknown file system\n", cp);
384 			continue;
385 		}
386 		while (*cp && *cp == '\t' && *cp == ' ')
387 			cp++;
388 
389 		/*
390 		 * At this point, dp points to the mount point of the
391 		 * file system and cp points to the remainder of the
392 		 * quota definition string.
393 		 */
394 		n = sscanf(cp,
395 		    "blocks (soft = %llu, hard = %llu) "
396 		    "inodes (soft = %llu, hard = %llu)\n",
397 			&tmp_bsoftlimit,
398 			&tmp_bhardlimit,
399 			&tmp_fsoftlimit,
400 			&tmp_fhardlimit);
401 
402 		if (n != 4) {
403 			(void) fprintf(stderr, "%s: bad format\n", cp);
404 			continue;
405 		}
406 
407 		/*
408 		 * The values in dqb_bsoftlimit and dqb_bhardlimit
409 		 * are specified in 1k blocks in the edited quota
410 		 * file (the one we're reading), but are specified in
411 		 * disk blocks in the data structure passed to quotactl().
412 		 * That means that the maximum allowed value for the
413 		 * hard and soft block limits in the edited quota file
414 		 * is the maximum number of disk blocks allowed in a
415 		 * quota (which is 2^32 - 1, since it's a 32-bit unsigned
416 		 * quantity), converted to 1k blocks.
417 		 */
418 		max_limit = dbtok(UINT_MAX);
419 
420 		quota_entry_printed = 0; /* only print quota entry once */
421 
422 		if (tmp_bsoftlimit > max_limit) {
423 			tmp_bsoftlimit = max_limit;
424 			if (!quota_entry_printed) {
425 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
426 				quota_entry_printed = 1;
427 			}
428 			(void) fprintf(stderr,
429 	"error: soft limit for blocks exceeds maximum allowed value,\n"
430 	"    soft limit for blocks set to %lu\n", max_limit);
431 		}
432 
433 		if (tmp_bhardlimit > max_limit) {
434 			tmp_bhardlimit = max_limit;
435 			if (!quota_entry_printed) {
436 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
437 				quota_entry_printed = 1;
438 			}
439 			(void) fprintf(stderr,
440 	"error: hard limit for blocks exceeds maximum allowed value,\n"
441 	"    hard limit for blocks set to %lu\n", max_limit);
442 		}
443 
444 
445 		/*
446 		 * Now check the file limits against their maximum, which
447 		 * is UINT_MAX (since it must fit in a uint32_t).
448 		 */
449 		max_limit = UINT_MAX;
450 
451 		if (tmp_fsoftlimit > max_limit) {
452 			tmp_fsoftlimit = max_limit;
453 			if (!quota_entry_printed) {
454 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
455 				quota_entry_printed = 1;
456 			}
457 			(void) fprintf(stderr,
458 	"error: soft limit for files exceeds maximum allowed value,\n"
459 	"    soft limit for files set to %lu\n", max_limit);
460 		}
461 
462 		if (tmp_fhardlimit > max_limit) {
463 			tmp_fhardlimit = max_limit;
464 			if (!quota_entry_printed) {
465 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
466 				quota_entry_printed = 1;
467 			}
468 			(void) fprintf(stderr,
469 	"error: hard limit for files exceeds maximum allowed value,\n"
470 	"    hard limit for files set to %lu\n", max_limit);
471 		}
472 
473 		changed++;
474 		tmp_bsoftlimit = ktodb(tmp_bsoftlimit);
475 		tmp_bhardlimit = ktodb(tmp_bhardlimit);
476 		/*
477 		 * It we are decreasing the soft limits, set the time limits
478 		 * to zero, in case the user is now over quota.
479 		 * the time limit will be started the next time the
480 		 * user does an allocation.
481 		 */
482 		if (tmp_bsoftlimit < fsqp->fsq_dqb.dqb_bsoftlimit)
483 			fsqp->fsq_dqb.dqb_btimelimit = 0;
484 		if (tmp_fsoftlimit < fsqp->fsq_dqb.dqb_fsoftlimit)
485 			fsqp->fsq_dqb.dqb_ftimelimit = 0;
486 		fsqp->fsq_dqb.dqb_bsoftlimit = tmp_bsoftlimit;
487 		fsqp->fsq_dqb.dqb_bhardlimit = tmp_bhardlimit;
488 		fsqp->fsq_dqb.dqb_fsoftlimit = tmp_fsoftlimit;
489 		fsqp->fsq_dqb.dqb_fhardlimit = tmp_fhardlimit;
490 	}
491 	(void) fclose(fd);
492 	if (changed)
493 		putdiscq(uid);
494 }
495 
496 static void
497 gettimes(uid_t uid)
498 {
499 	struct fsquot *fsqp;
500 	FILE *fd;
501 	char btime[80], ftime[80];
502 
503 	getdiscq(uid);
504 	if ((fd = fopen64(tmpfil, "w")) == NULL) {
505 		(void) fprintf(stderr, "edquota: ");
506 		perror(tmpfil);
507 		(void) unlink(tmpfil);
508 		exit(32);
509 	}
510 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
511 		fmttime(btime, fsqp->fsq_dqb.dqb_btimelimit);
512 		fmttime(ftime, fsqp->fsq_dqb.dqb_ftimelimit);
513 		(void) fprintf(fd,
514 		    "fs %s blocks time limit = %s, files time limit = %s\n",
515 		    fsqp->fsq_fs, btime, ftime);
516 	}
517 	(void) fclose(fd);
518 }
519 
520 static void
521 puttimes(uid_t uid)
522 {
523 	FILE *fd;
524 	char line[BUFSIZ];
525 	int changed = 0;
526 	double btimelimit, ftimelimit;
527 	char bunits[80], funits[80];
528 
529 	fd = fopen64(tmpfil, "r");
530 	if (fd == NULL) {
531 		(void) fprintf(stderr, "Can't re-read temp file!!\n");
532 		return;
533 	}
534 	while (fgets(line, sizeof (line), fd) != NULL) {
535 		struct fsquot *fsqp;
536 		char *cp, *dp;
537 		int n;
538 
539 		cp = next(line, " \t");
540 		if (cp == NULL)
541 			break;
542 		*cp++ = '\0';
543 		while (*cp && *cp == '\t' && *cp == ' ')
544 			cp++;
545 		dp = cp, cp = next(cp, " \t");
546 		if (cp == NULL)
547 			break;
548 		*cp++ = '\0';
549 		for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
550 			if (strcmp(dp, fsqp->fsq_fs) == 0)
551 				break;
552 		}
553 		if (fsqp == NULL) {
554 			(void) fprintf(stderr, "%s: unknown file system\n", cp);
555 			continue;
556 		}
557 		while (*cp && *cp == '\t' && *cp == ' ')
558 			cp++;
559 		n = sscanf(cp,
560 		    "blocks time limit = %lf %[^,], "
561 		    "files time limit = %lf %s\n",
562 		    &btimelimit, bunits, &ftimelimit, funits);
563 		if (n != 4 ||
564 		    !unfmttime(btimelimit, bunits,
565 			&fsqp->fsq_dqb.dqb_btimelimit) ||
566 		    !unfmttime(ftimelimit, funits,
567 			&fsqp->fsq_dqb.dqb_ftimelimit)) {
568 			(void) fprintf(stderr, "%s: bad format\n", cp);
569 			continue;
570 		}
571 		changed++;
572 	}
573 	(void) fclose(fd);
574 	if (changed)
575 		putdiscq(uid);
576 }
577 
578 static char *
579 next(char *cp, char *match)
580 {
581 	char *dp;
582 
583 	while (cp && *cp) {
584 		for (dp = match; dp && *dp; dp++)
585 			if (*dp == *cp)
586 				return (cp);
587 		cp++;
588 	}
589 	return ((char *)0);
590 }
591 
592 static int
593 alldigits(char *s)
594 {
595 	int c = *s++;
596 
597 	do {
598 		if (!isdigit(c))
599 			return (0);
600 	} while ((c = *s++) != '\0');
601 
602 	return (1);
603 }
604 
605 static struct {
606 	int c_secs;			/* conversion units in secs */
607 	char *c_str;			/* unit string */
608 } cunits [] = {
609 	{60*60*24*28, "month"},
610 	{60*60*24*7, "week"},
611 	{60*60*24, "day"},
612 	{60*60, "hour"},
613 	{60, "min"},
614 	{1, "sec"}
615 };
616 
617 static void
618 fmttime(char *buf, ulong_t time)
619 {
620 	double value;
621 	int i;
622 
623 	if (time == 0) {
624 		(void) strcpy(buf, "0 (default)");
625 		return;
626 	}
627 	for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++)
628 		if (time >= cunits[i].c_secs)
629 			break;
630 
631 	value = (double)time / cunits[i].c_secs;
632 	(void) sprintf(buf, "%.2f %s%s",
633 		value, cunits[i].c_str, value > 1.0 ? "s" : "");
634 }
635 
636 static int
637 unfmttime(double value, char *units, uint32_t *timep)
638 {
639 	int i;
640 
641 	if (value == 0.0) {
642 		*timep = 0;
643 		return (1);
644 	}
645 	for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++) {
646 		if (strncmp(cunits[i].c_str, units,
647 		    strlen(cunits[i].c_str)) == 0)
648 			break;
649 	}
650 	if (i >= sizeof (cunits) / sizeof (cunits[0]))
651 		return (0);
652 	*timep = (ulong_t)(value * cunits[i].c_secs);
653 	return (1);
654 }
655 
656 static void
657 setupfs(void)
658 {
659 	struct mnttab mntp;
660 	struct fsquot *fsqp;
661 	struct stat64 statb;
662 	dev_t fsdev;
663 	FILE *mtab;
664 	char qfilename[MAXPATHLEN];
665 
666 	if ((mtab = fopen(MNTTAB, "r")) == (FILE *)0) {
667 		perror("/etc/mnttab");
668 		exit(31+1);
669 	}
670 	while (getmntent(mtab, &mntp) == 0) {
671 		if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0)
672 			continue;
673 		if (stat64(mntp.mnt_special, &statb) < 0)
674 			continue;
675 		if ((statb.st_mode & S_IFMT) != S_IFBLK)
676 			continue;
677 		fsdev = statb.st_rdev;
678 		(void) snprintf(qfilename, sizeof (qfilename), "%s/%s",
679 			mntp.mnt_mountp, QFNAME);
680 		if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev)
681 			continue;
682 		fsqp = malloc(sizeof (struct fsquot));
683 		if (fsqp == NULL) {
684 			(void) fprintf(stderr, "out of memory\n");
685 			exit(31+1);
686 		}
687 		fsqp->fsq_next = fsqlist;
688 		fsqp->fsq_fs = strdup(mntp.mnt_mountp);
689 		fsqp->fsq_dev = strdup(mntp.mnt_special);
690 		fsqp->fsq_qfile = strdup(qfilename);
691 		if (fsqp->fsq_fs == NULL || fsqp->fsq_dev == NULL ||
692 		    fsqp->fsq_qfile == NULL) {
693 			(void) fprintf(stderr, "out of memory\n");
694 			exit(31+1);
695 		}
696 		fsqlist = fsqp;
697 	}
698 	(void) fclose(mtab);
699 }
700 
701 static void
702 getdiscq(uid_t uid)
703 {
704 	struct fsquot *fsqp;
705 	int fd;
706 
707 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
708 		if (quotactl(Q_GETQUOTA, fsqp->fsq_dev, uid,
709 		    (caddr_t)&fsqp->fsq_dqb) != 0) {
710 			if ((fd = open64(fsqp->fsq_qfile, O_RDONLY)) < 0) {
711 				(void) fprintf(stderr, "edquota: ");
712 				perror(fsqp->fsq_qfile);
713 				continue;
714 			}
715 			(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
716 			switch (read(fd, (char *)&fsqp->fsq_dqb,
717 			    sizeof (struct dqblk))) {
718 			case 0:
719 				/*
720 				 * Convert implicit 0 quota (EOF)
721 				 * into an explicit one (zero'ed dqblk)
722 				 */
723 				bzero((caddr_t)&fsqp->fsq_dqb,
724 				    sizeof (struct dqblk));
725 				break;
726 
727 			case sizeof (struct dqblk):	/* OK */
728 				break;
729 
730 			default:			/* ERROR */
731 				(void) fprintf(stderr,
732 				    "edquota: read error in ");
733 				perror(fsqp->fsq_qfile);
734 				break;
735 			}
736 			(void) close(fd);
737 		}
738 	}
739 }
740 
741 static void
742 putdiscq(uid_t uid)
743 {
744 	struct fsquot *fsqp;
745 
746 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
747 		if (quotactl(Q_SETQLIM, fsqp->fsq_dev, uid,
748 		    (caddr_t)&fsqp->fsq_dqb) != 0) {
749 			int fd;
750 
751 			if ((fd = open64(fsqp->fsq_qfile, O_RDWR)) < 0) {
752 				(void) fprintf(stderr, "edquota: ");
753 				perror(fsqp->fsq_qfile);
754 				continue;
755 			}
756 			(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
757 			if (write(fd, (char *)&fsqp->fsq_dqb,
758 			    sizeof (struct dqblk)) != sizeof (struct dqblk)) {
759 				(void) fprintf(stderr, "edquota: ");
760 				perror(fsqp->fsq_qfile);
761 			}
762 			(void) close(fd);
763 		}
764 	}
765 }
766 
767 static void
768 sigsetmask(uint_t omask)
769 {
770 	int i;
771 
772 	for (i = 0; i < 32; i++)
773 		if (omask & (1 << i)) {
774 			if (sigignore(1 << i) == (int)SIG_ERR) {
775 				(void) fprintf(stderr,
776 				    "Bad signal 0x%x\n", (1 << i));
777 				exit(31+1);
778 			}
779 		}
780 }
781 
782 static uint_t
783 sigblock(uint_t omask)
784 {
785 	uint_t previous = 0;
786 	uint_t temp;
787 	int i;
788 
789 	for (i = 0; i < 32; i++)
790 		if (omask & (1 << i)) {
791 			if ((temp = sigignore(1 << i)) == (int)SIG_ERR) {
792 				(void) fprintf(stderr,
793 				    "Bad signal 0x%x\n", (1 << i));
794 				exit(31+1);
795 			}
796 			if (i == 0)
797 				previous = temp;
798 		}
799 
800 	return (previous);
801 }
802 
803 static void
804 usage(void)
805 {
806 	(void) fprintf(stderr, "ufs usage:\n");
807 	(void) fprintf(stderr, "\tedquota [-p username] username ...\n");
808 	(void) fprintf(stderr, "\tedquota -t\n");
809 	exit(1);
810 }
811 
812 static int
813 quotactl(int cmd, char *special, uid_t uid, caddr_t addr)
814 {
815 	int		fd;
816 	int		status;
817 	struct quotctl	quota;
818 	char		qfile[MAXPATHLEN];
819 	FILE		*fstab;
820 	struct mnttab	mntp;
821 
822 	if ((special == NULL) && (cmd == Q_SYNC)) {
823 		cmd = Q_ALLSYNC;
824 		/*
825 		 * need to find an acceptable fd to send this Q_ALLSYNC down
826 		 * on, it needs to be a ufs fd for vfs to at least call the
827 		 * real quotactl() in the kernel
828 		 * Here, try to simply find the starting mountpoint of the
829 		 * first mounted ufs file system
830 		 */
831 	}
832 
833 	/*
834 	 * Find the mount point of the special device.   This is
835 	 * because the fcntl that implements the quotactl call has
836 	 * to go to a real file, and not to the block device.
837 	 */
838 	if ((fstab = fopen(MNTTAB, "r")) == NULL) {
839 		(void) fprintf(stderr, "%s: ", MNTTAB);
840 		perror("open");
841 		exit(31+1);
842 	}
843 	qfile[0] = '\0';
844 	while ((status = getmntent(fstab, &mntp)) == NULL) {
845 		/*
846 		 * check that it is a ufs file system
847 		 * for all quotactl()s except Q_ALLSYNC check that
848 		 * the file system is read-write since changes in the
849 		 * quotas file may be required
850 		 * for Q_ALLSYNC, this check is skipped since this option
851 		 * is to determine if quotas are configured into the system
852 		 */
853 		if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
854 		    ((cmd != Q_ALLSYNC) && hasmntopt(&mntp, MNTOPT_RO)))
855 			continue;
856 		if (cmd == Q_ALLSYNC) {	/* implies (special==0) too */
857 			if (strlcpy(qfile, mntp.mnt_mountp,
858 				    sizeof (qfile)) >= sizeof (qfile)) {
859 				errno = ENOENT;
860 				return (-1);
861 			}
862 			break;
863 		}
864 		if (strcmp(special, mntp.mnt_special) == 0) {
865 			if (strlcpy(qfile, mntp.mnt_mountp,
866 				    sizeof (qfile)) >= sizeof (qfile)) {
867 				errno = ENOENT;
868 				return (-1);
869 			}
870 		}
871 	}
872 	(void) fclose(fstab);
873 	if (qfile[0] == '\0') {
874 		errno = ENOENT;
875 		return (-1);
876 	}
877 	{
878 		int open_flags;
879 
880 		if (cmd == Q_ALLSYNC) {
881 			open_flags = O_RDONLY;
882 		} else {
883 			if (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
884 			    sizeof (qfile)) {
885 				errno = ENOENT;
886 				return (-1);
887 			}
888 			open_flags = O_RDWR;
889 		}
890 
891 		if ((fd = open64(qfile, open_flags)) < 0) {
892 			(void) fprintf(stderr, "quotactl: ");
893 			perror("open");
894 			exit(31+1);
895 		}
896 	}
897 
898 	quota.op = cmd;
899 	quota.uid = uid;
900 	quota.addr = addr;
901 	status = ioctl(fd, Q_QUOTACTL, &quota);
902 	(void) close(fd);
903 	return (status);
904 }
905