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