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