xref: /illumos-gate/usr/src/cmd/fs.d/ufs/edquota/edquota.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
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 2005 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 < 0) {
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 < 0) {
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 < 0)
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 	if ((uint64_t)uid > (UFS_MAXOFFSET_T / sizeof (struct dqblk))) {
235 		/*
236 		 * The 'quotas' file contains 32-byte records that are
237 		 * indexed by uid. This uid is too large to have a record
238 		 * in any 40-bit file.  Now that the quotas file can be
239 		 * a max size of 1T, a uid would need to be
240 		 * represented as a long long in order to be too large.
241 		 */
242 		(void) fprintf(stderr,
243 			"edquota: uid %ld too large for quotas\n", uid);
244 		(void) sleep(1);
245 		return (-1);
246 	}
247 	return (uid);
248 }
249 
250 #define	RESPSZ	128
251 
252 static int
253 editit(void)
254 {
255 	pid_t pid, xpid;
256 	char *ed;
257 	char resp[RESPSZ];
258 	int status, omask;
259 
260 #define	mask(s)	(1 << ((s) - 1))
261 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
262 
263 	if ((ed = getenv("EDITOR")) == (char *)0)
264 		ed = DEFEDITOR;
265 
266 	/*CONSTANTCONDITION*/
267 	while (1) {
268 		if ((pid = fork()) < 0) {
269 			if (errno == EAGAIN) {
270 				(void) fprintf(stderr,
271 					"You have too many processes\n");
272 				return (0);
273 			}
274 			perror("fork");
275 			return (0);
276 		}
277 		if (pid == 0) {
278 			(void) sigsetmask(omask);
279 			(void) setgid(getgid());
280 			(void) setuid(getuid());
281 			(void) execlp(ed, ed, tmpfil, 0);
282 			(void) fprintf(stderr,
283 				"Can't exec editor \"%s\": ", ed);
284 			perror("");
285 			exit(32);
286 		}
287 		while ((xpid = wait(&status)) >= 0)
288 			if (xpid == pid)
289 				break;
290 
291 		if (!isatty(fileno(stdin))) {	/* Non-interactive */
292 			break;
293 		}
294 
295 		/*
296 		 * Certain editors can exit with a non-zero status even
297 		 * though everything is peachy. Best to ask the user what
298 		 * s/he really wants to do. (N.B.: if we're non-interactive
299 		 * we'll "break" the while loop before we get here.)
300 		 */
301 		if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
302 			(void) printf("Non-zero return from \"%s\", ", ed);
303 			(void) printf("updated file may contain errors.\n");
304 			/*CONSTANTCONDITION*/
305 			while (1) {
306 				(void) printf("Edit again (e) or quit, "
307 				    "discarding changes (q)? ");
308 				(void) fflush(stdout);
309 				if (gets(resp) == NULL) {
310 					return (0);
311 				}
312 				if ((*resp == 'e') || (*resp == 'q')) {
313 					break;
314 				}
315 			}
316 
317 			if (*resp == 'e') {
318 				continue;
319 			} else {
320 				/*
321 				 * Since (*resp == 'q'), then we just
322 				 * want to break out of here and return
323 				 * the failure.
324 				 */
325 				break;
326 			}
327 		} else {
328 			break;	/* Successful return from editor */
329 		}
330 	}
331 	(void) sigsetmask(omask);
332 	return (!status);
333 }
334 
335 static void
336 getprivs(uid_t uid)
337 {
338 	struct fsquot *fsqp;
339 	FILE *fd;
340 
341 	getdiscq(uid);
342 	if ((fd = fopen64(tmpfil, "w")) == NULL) {
343 		(void) fprintf(stderr, "edquota: ");
344 		perror(tmpfil);
345 		(void) unlink(tmpfil);
346 		exit(32);
347 	}
348 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next)
349 		(void) fprintf(fd,
350 		    "fs %s blocks (soft = %lu, hard = %lu) "
351 		    "inodes (soft = %lu, hard = %lu)\n",
352 		    fsqp->fsq_fs,
353 		    dbtok(fsqp->fsq_dqb.dqb_bsoftlimit),
354 		    dbtok(fsqp->fsq_dqb.dqb_bhardlimit),
355 		    fsqp->fsq_dqb.dqb_fsoftlimit,
356 		    fsqp->fsq_dqb.dqb_fhardlimit);
357 	(void) fclose(fd);
358 }
359 
360 static void
361 putprivs(uid_t uid)
362 {
363 	FILE *fd;
364 	uint64_t tmp_bsoftlimit, tmp_bhardlimit, tmp_fsoftlimit,
365 	    tmp_fhardlimit;
366 	char line[BUFSIZ];
367 	int changed = 0;
368 	uint32_t max_limit;
369 	int	quota_entry_printed;
370 
371 	fd = fopen64(tmpfil, "r");
372 	if (fd == NULL) {
373 		(void) fprintf(stderr, "Can't re-read temp file!!\n");
374 		return;
375 	}
376 	while (fgets(line, sizeof (line), fd) != NULL) {
377 		struct fsquot *fsqp;
378 		char *cp, *dp;
379 		int n;
380 
381 		cp = next(line, " \t");
382 		if (cp == NULL)
383 			break;
384 		*cp++ = '\0';
385 		while (*cp && *cp == '\t' && *cp == ' ')
386 			cp++;
387 		dp = cp, cp = next(cp, " \t");
388 		if (cp == NULL)
389 			break;
390 		*cp++ = '\0';
391 		for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
392 			if (strcmp(dp, fsqp->fsq_fs) == 0)
393 				break;
394 		}
395 		if (fsqp == NULL) {
396 			(void) fprintf(stderr, "%s: unknown file system\n", cp);
397 			continue;
398 		}
399 		while (*cp && *cp == '\t' && *cp == ' ')
400 			cp++;
401 
402 		/*
403 		 * At this point, dp points to the mount point of the
404 		 * file system and cp points to the remainder of the
405 		 * quota definition string.
406 		 */
407 		n = sscanf(cp,
408 		    "blocks (soft = %llu, hard = %llu) "
409 		    "inodes (soft = %llu, hard = %llu)\n",
410 			&tmp_bsoftlimit,
411 			&tmp_bhardlimit,
412 			&tmp_fsoftlimit,
413 			&tmp_fhardlimit);
414 
415 		if (n != 4) {
416 			(void) fprintf(stderr, "%s: bad format\n", cp);
417 			continue;
418 		}
419 
420 		/*
421 		 * The values in dqb_bsoftlimit and dqb_bhardlimit
422 		 * are specified in 1k blocks in the edited quota
423 		 * file (the one we're reading), but are specified in
424 		 * disk blocks in the data structure passed to quotactl().
425 		 * That means that the maximum allowed value for the
426 		 * hard and soft block limits in the edited quota file
427 		 * is the maximum number of disk blocks allowed in a
428 		 * quota (which is 2^32 - 1, since it's a 32-bit unsigned
429 		 * quantity), converted to 1k blocks.
430 		 */
431 		max_limit = dbtok(UINT_MAX);
432 
433 		quota_entry_printed = 0; /* only print quota entry once */
434 
435 		if (tmp_bsoftlimit > max_limit) {
436 			tmp_bsoftlimit = max_limit;
437 			if (!quota_entry_printed) {
438 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
439 				quota_entry_printed = 1;
440 			}
441 			(void) fprintf(stderr,
442 	"error: soft limit for blocks exceeds maximum allowed value,\n"
443 	"    soft limit for blocks set to %lu\n", max_limit);
444 		}
445 
446 		if (tmp_bhardlimit > max_limit) {
447 			tmp_bhardlimit = max_limit;
448 			if (!quota_entry_printed) {
449 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
450 				quota_entry_printed = 1;
451 			}
452 			(void) fprintf(stderr,
453 	"error: hard limit for blocks exceeds maximum allowed value,\n"
454 	"    hard limit for blocks set to %lu\n", max_limit);
455 		}
456 
457 
458 		/*
459 		 * Now check the file limits against their maximum, which
460 		 * is UINT_MAX (since it must fit in a uint32_t).
461 		 */
462 		max_limit = UINT_MAX;
463 
464 		if (tmp_fsoftlimit > max_limit) {
465 			tmp_fsoftlimit = max_limit;
466 			if (!quota_entry_printed) {
467 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
468 				quota_entry_printed = 1;
469 			}
470 			(void) fprintf(stderr,
471 	"error: soft limit for files exceeds maximum allowed value,\n"
472 	"    soft limit for files set to %lu\n", max_limit);
473 		}
474 
475 		if (tmp_fhardlimit > max_limit) {
476 			tmp_fhardlimit = max_limit;
477 			if (!quota_entry_printed) {
478 				(void) fprintf(stderr, "%s %s%\n", dp, cp);
479 				quota_entry_printed = 1;
480 			}
481 			(void) fprintf(stderr,
482 	"error: hard limit for files exceeds maximum allowed value,\n"
483 	"    hard limit for files set to %lu\n", max_limit);
484 		}
485 
486 		changed++;
487 		tmp_bsoftlimit = ktodb(tmp_bsoftlimit);
488 		tmp_bhardlimit = ktodb(tmp_bhardlimit);
489 		/*
490 		 * It we are decreasing the soft limits, set the time limits
491 		 * to zero, in case the user is now over quota.
492 		 * the time limit will be started the next time the
493 		 * user does an allocation.
494 		 */
495 		if (tmp_bsoftlimit < fsqp->fsq_dqb.dqb_bsoftlimit)
496 			fsqp->fsq_dqb.dqb_btimelimit = 0;
497 		if (tmp_fsoftlimit < fsqp->fsq_dqb.dqb_fsoftlimit)
498 			fsqp->fsq_dqb.dqb_ftimelimit = 0;
499 		fsqp->fsq_dqb.dqb_bsoftlimit = tmp_bsoftlimit;
500 		fsqp->fsq_dqb.dqb_bhardlimit = tmp_bhardlimit;
501 		fsqp->fsq_dqb.dqb_fsoftlimit = tmp_fsoftlimit;
502 		fsqp->fsq_dqb.dqb_fhardlimit = tmp_fhardlimit;
503 	}
504 	(void) fclose(fd);
505 	if (changed)
506 		putdiscq(uid);
507 }
508 
509 static void
510 gettimes(uid_t uid)
511 {
512 	struct fsquot *fsqp;
513 	FILE *fd;
514 	char btime[80], ftime[80];
515 
516 	getdiscq(uid);
517 	if ((fd = fopen64(tmpfil, "w")) == NULL) {
518 		(void) fprintf(stderr, "edquota: ");
519 		perror(tmpfil);
520 		(void) unlink(tmpfil);
521 		exit(32);
522 	}
523 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
524 		fmttime(btime, fsqp->fsq_dqb.dqb_btimelimit);
525 		fmttime(ftime, fsqp->fsq_dqb.dqb_ftimelimit);
526 		(void) fprintf(fd,
527 		    "fs %s blocks time limit = %s, files time limit = %s\n",
528 		    fsqp->fsq_fs, btime, ftime);
529 	}
530 	(void) fclose(fd);
531 }
532 
533 static void
534 puttimes(uid_t uid)
535 {
536 	FILE *fd;
537 	char line[BUFSIZ];
538 	int changed = 0;
539 	double btimelimit, ftimelimit;
540 	char bunits[80], funits[80];
541 
542 	fd = fopen64(tmpfil, "r");
543 	if (fd == NULL) {
544 		(void) fprintf(stderr, "Can't re-read temp file!!\n");
545 		return;
546 	}
547 	while (fgets(line, sizeof (line), fd) != NULL) {
548 		struct fsquot *fsqp;
549 		char *cp, *dp;
550 		int n;
551 
552 		cp = next(line, " \t");
553 		if (cp == NULL)
554 			break;
555 		*cp++ = '\0';
556 		while (*cp && *cp == '\t' && *cp == ' ')
557 			cp++;
558 		dp = cp, cp = next(cp, " \t");
559 		if (cp == NULL)
560 			break;
561 		*cp++ = '\0';
562 		for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
563 			if (strcmp(dp, fsqp->fsq_fs) == 0)
564 				break;
565 		}
566 		if (fsqp == NULL) {
567 			(void) fprintf(stderr, "%s: unknown file system\n", cp);
568 			continue;
569 		}
570 		while (*cp && *cp == '\t' && *cp == ' ')
571 			cp++;
572 		n = sscanf(cp,
573 		    "blocks time limit = %lf %[^,], "
574 		    "files time limit = %lf %s\n",
575 		    &btimelimit, bunits, &ftimelimit, funits);
576 		if (n != 4 ||
577 		    !unfmttime(btimelimit, bunits,
578 			&fsqp->fsq_dqb.dqb_btimelimit) ||
579 		    !unfmttime(ftimelimit, funits,
580 			&fsqp->fsq_dqb.dqb_ftimelimit)) {
581 			(void) fprintf(stderr, "%s: bad format\n", cp);
582 			continue;
583 		}
584 		changed++;
585 	}
586 	(void) fclose(fd);
587 	if (changed)
588 		putdiscq(uid);
589 }
590 
591 static char *
592 next(char *cp, char *match)
593 {
594 	char *dp;
595 
596 	while (cp && *cp) {
597 		for (dp = match; dp && *dp; dp++)
598 			if (*dp == *cp)
599 				return (cp);
600 		cp++;
601 	}
602 	return ((char *)0);
603 }
604 
605 static int
606 alldigits(char *s)
607 {
608 	int c = *s++;
609 
610 	do {
611 		if (!isdigit(c))
612 			return (0);
613 	} while ((c = *s++) != '\0');
614 
615 	return (1);
616 }
617 
618 static struct {
619 	int c_secs;			/* conversion units in secs */
620 	char *c_str;			/* unit string */
621 } cunits [] = {
622 	{60*60*24*28, "month"},
623 	{60*60*24*7, "week"},
624 	{60*60*24, "day"},
625 	{60*60, "hour"},
626 	{60, "min"},
627 	{1, "sec"}
628 };
629 
630 static void
631 fmttime(char *buf, ulong_t time)
632 {
633 	double value;
634 	int i;
635 
636 	if (time == 0) {
637 		(void) strcpy(buf, "0 (default)");
638 		return;
639 	}
640 	for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++)
641 		if (time >= cunits[i].c_secs)
642 			break;
643 
644 	value = (double)time / cunits[i].c_secs;
645 	(void) sprintf(buf, "%.2f %s%s",
646 		value, cunits[i].c_str, value > 1.0 ? "s" : "");
647 }
648 
649 static int
650 unfmttime(double value, char *units, uint32_t *timep)
651 {
652 	int i;
653 
654 	if (value == 0.0) {
655 		*timep = 0;
656 		return (1);
657 	}
658 	for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++) {
659 		if (strncmp(cunits[i].c_str, units,
660 		    strlen(cunits[i].c_str)) == 0)
661 			break;
662 	}
663 	if (i >= sizeof (cunits) / sizeof (cunits[0]))
664 		return (0);
665 	*timep = (ulong_t)(value * cunits[i].c_secs);
666 	return (1);
667 }
668 
669 static void
670 setupfs(void)
671 {
672 	struct mnttab mntp;
673 	struct fsquot *fsqp;
674 	struct stat64 statb;
675 	dev_t fsdev;
676 	FILE *mtab;
677 	char qfilename[MAXPATHLEN];
678 
679 	if ((mtab = fopen(MNTTAB, "r")) == (FILE *)0) {
680 		perror("/etc/mnttab");
681 		exit(31+1);
682 	}
683 	while (getmntent(mtab, &mntp) == 0) {
684 		if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0)
685 			continue;
686 		if (stat64(mntp.mnt_special, &statb) < 0)
687 			continue;
688 		if ((statb.st_mode & S_IFMT) != S_IFBLK)
689 			continue;
690 		fsdev = statb.st_rdev;
691 		(void) snprintf(qfilename, sizeof (qfilename), "%s/%s",
692 			mntp.mnt_mountp, QFNAME);
693 		if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev)
694 			continue;
695 		fsqp = malloc(sizeof (struct fsquot));
696 		if (fsqp == NULL) {
697 			(void) fprintf(stderr, "out of memory\n");
698 			exit(31+1);
699 		}
700 		fsqp->fsq_next = fsqlist;
701 		fsqp->fsq_fs = strdup(mntp.mnt_mountp);
702 		fsqp->fsq_dev = strdup(mntp.mnt_special);
703 		fsqp->fsq_qfile = strdup(qfilename);
704 		if (fsqp->fsq_fs == NULL || fsqp->fsq_dev == NULL ||
705 		    fsqp->fsq_qfile == NULL) {
706 			(void) fprintf(stderr, "out of memory\n");
707 			exit(31+1);
708 		}
709 		fsqlist = fsqp;
710 	}
711 	(void) fclose(mtab);
712 }
713 
714 static void
715 getdiscq(uid_t uid)
716 {
717 	struct fsquot *fsqp;
718 	int fd;
719 
720 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
721 		if (quotactl(Q_GETQUOTA, fsqp->fsq_dev, uid,
722 		    (caddr_t)&fsqp->fsq_dqb) != 0) {
723 			if ((fd = open64(fsqp->fsq_qfile, O_RDONLY)) < 0) {
724 				(void) fprintf(stderr, "edquota: ");
725 				perror(fsqp->fsq_qfile);
726 				continue;
727 			}
728 			(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
729 			switch (read(fd, (char *)&fsqp->fsq_dqb,
730 			    sizeof (struct dqblk))) {
731 			case 0:
732 				/*
733 				 * Convert implicit 0 quota (EOF)
734 				 * into an explicit one (zero'ed dqblk)
735 				 */
736 				bzero((caddr_t)&fsqp->fsq_dqb,
737 				    sizeof (struct dqblk));
738 				break;
739 
740 			case sizeof (struct dqblk):	/* OK */
741 				break;
742 
743 			default:			/* ERROR */
744 				(void) fprintf(stderr,
745 				    "edquota: read error in ");
746 				perror(fsqp->fsq_qfile);
747 				break;
748 			}
749 			(void) close(fd);
750 		}
751 	}
752 }
753 
754 static void
755 putdiscq(uid_t uid)
756 {
757 	struct fsquot *fsqp;
758 
759 	for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) {
760 		if (quotactl(Q_SETQLIM, fsqp->fsq_dev, uid,
761 		    (caddr_t)&fsqp->fsq_dqb) != 0) {
762 			int fd;
763 
764 			if ((fd = open64(fsqp->fsq_qfile, O_RDWR)) < 0) {
765 				(void) fprintf(stderr, "edquota: ");
766 				perror(fsqp->fsq_qfile);
767 				continue;
768 			}
769 			(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
770 			if (write(fd, (char *)&fsqp->fsq_dqb,
771 			    sizeof (struct dqblk)) != sizeof (struct dqblk)) {
772 				(void) fprintf(stderr, "edquota: ");
773 				perror(fsqp->fsq_qfile);
774 			}
775 			(void) close(fd);
776 		}
777 	}
778 }
779 
780 static void
781 sigsetmask(uint_t omask)
782 {
783 	int i;
784 
785 	for (i = 0; i < 32; i++)
786 		if (omask & (1 << i)) {
787 			if (sigignore(1 << i) == (int)SIG_ERR) {
788 				(void) fprintf(stderr,
789 				    "Bad signal 0x%x\n", (1 << i));
790 				exit(31+1);
791 			}
792 		}
793 }
794 
795 static uint_t
796 sigblock(uint_t omask)
797 {
798 	uint_t previous = 0;
799 	uint_t temp;
800 	int i;
801 
802 	for (i = 0; i < 32; i++)
803 		if (omask & (1 << i)) {
804 			if ((temp = sigignore(1 << i)) == (int)SIG_ERR) {
805 				(void) fprintf(stderr,
806 				    "Bad signal 0x%x\n", (1 << i));
807 				exit(31+1);
808 			}
809 			if (i == 0)
810 				previous = temp;
811 		}
812 
813 	return (previous);
814 }
815 
816 static void
817 usage(void)
818 {
819 	(void) fprintf(stderr, "ufs usage:\n");
820 	(void) fprintf(stderr, "\tedquota [-p username] username ...\n");
821 	(void) fprintf(stderr, "\tedquota -t\n");
822 	exit(1);
823 }
824 
825 static int
826 quotactl(int cmd, char *special, uid_t uid, caddr_t addr)
827 {
828 	int		fd;
829 	int		status;
830 	struct quotctl	quota;
831 	char		qfile[MAXPATHLEN];
832 	FILE		*fstab;
833 	struct mnttab	mntp;
834 
835 	if ((special == NULL) && (cmd == Q_SYNC)) {
836 		cmd = Q_ALLSYNC;
837 		/*
838 		 * need to find an acceptable fd to send this Q_ALLSYNC down
839 		 * on, it needs to be a ufs fd for vfs to at least call the
840 		 * real quotactl() in the kernel
841 		 * Here, try to simply find the starting mountpoint of the
842 		 * first mounted ufs file system
843 		 */
844 	}
845 
846 	/*
847 	 * Find the mount point of the special device.   This is
848 	 * because the fcntl that implements the quotactl call has
849 	 * to go to a real file, and not to the block device.
850 	 */
851 	if ((fstab = fopen(MNTTAB, "r")) == NULL) {
852 		(void) fprintf(stderr, "%s: ", MNTTAB);
853 		perror("open");
854 		exit(31+1);
855 	}
856 	qfile[0] = '\0';
857 	while ((status = getmntent(fstab, &mntp)) == NULL) {
858 		/*
859 		 * check that it is a ufs file system
860 		 * for all quotactl()s except Q_ALLSYNC check that
861 		 * the file system is read-write since changes in the
862 		 * quotas file may be required
863 		 * for Q_ALLSYNC, this check is skipped since this option
864 		 * is to determine if quotas are configured into the system
865 		 */
866 		if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
867 		    ((cmd != Q_ALLSYNC) && hasmntopt(&mntp, MNTOPT_RO)))
868 			continue;
869 		if (cmd == Q_ALLSYNC) {	/* implies (special==0) too */
870 			if (strlcpy(qfile, mntp.mnt_mountp,
871 				    sizeof (qfile)) >= sizeof (qfile)) {
872 				errno = ENOENT;
873 				return (-1);
874 			}
875 			break;
876 		}
877 		if (strcmp(special, mntp.mnt_special) == 0) {
878 			if (strlcpy(qfile, mntp.mnt_mountp,
879 				    sizeof (qfile)) >= sizeof (qfile)) {
880 				errno = ENOENT;
881 				return (-1);
882 			}
883 		}
884 	}
885 	(void) fclose(fstab);
886 	if (qfile[0] == '\0') {
887 		errno = ENOENT;
888 		return (-1);
889 	}
890 	{
891 		int open_flags;
892 
893 		if (cmd == Q_ALLSYNC) {
894 			open_flags = O_RDONLY;
895 		} else {
896 			if (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
897 			    sizeof (qfile)) {
898 				errno = ENOENT;
899 				return (-1);
900 			}
901 			open_flags = O_RDWR;
902 		}
903 
904 		if ((fd = open64(qfile, open_flags)) < 0) {
905 			(void) fprintf(stderr, "quotactl: ");
906 			perror("open");
907 			exit(31+1);
908 		}
909 	}
910 
911 	quota.op = cmd;
912 	quota.uid = uid;
913 	quota.addr = addr;
914 	status = ioctl(fd, Q_QUOTACTL, &quota);
915 	(void) close(fd);
916 	return (status);
917 }
918