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
74struct 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
82static struct fsquot *fsqlist;
83
84static char	tmpfil[] = "/tmp/EdP.aXXXXXX";
85#define	QFNAME	"quotas"
86
87static uid_t getentry(char *);
88static int editit(void);
89static void getprivs(uid_t);
90static void putprivs(uid_t);
91static void gettimes(uid_t);
92static void puttimes(uid_t);
93static char *next(char *, char *);
94static int alldigits(char *);
95static void fmttime(char *, ulong_t);
96static int unfmttime(double, char *, uint32_t *);
97static void setupfs(void);
98static void getdiscq(uid_t);
99static void putdiscq(uid_t);
100static void sigsetmask(uint_t);
101static uint_t sigblock(uint_t);
102static void usage(void);
103static int quotactl(int, char *, uid_t, caddr_t);
104
105int
106main(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
210static uid_t
211getentry(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
238static int
239editit(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
321static void
322getprivs(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
346static void
347putprivs(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
495static void
496gettimes(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
519static void
520puttimes(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
577static char *
578next(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
591static int
592alldigits(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
604static 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
616static void
617fmttime(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
635static int
636unfmttime(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
655static void
656setupfs(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
700static void
701getdiscq(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
740static void
741putdiscq(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
766static void
767sigsetmask(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
781static uint_t
782sigblock(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
802static void
803usage(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
811static int
812quotactl(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