8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / fs.d / ufs / edquota / edquota.c
blob385a75ea83fc5286e0acc904c37bc274a17fc5fd
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
40 #pragma ident "%Z%%M% %I% %E% SMI"
43 * Disk quota editor.
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>
66 #define DEFEDITOR "/usr/bin/vi"
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
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;
84 static struct fsquot *fsqlist;
86 static char tmpfil[] = "/tmp/EdP.aXXXXXX";
87 #define QFNAME "quotas"
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);
108 main(int argc, char **argv)
110 uid_t uid;
111 char *basename;
112 int opt;
113 int i;
114 int tmpfd = -1;
116 basename = argv[0];
117 if (argc < 2) {
118 usage();
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);
126 if (getuid()) {
127 (void) fprintf(stderr, "%s: permission denied\n", basename);
128 exit(32);
130 setupfs();
131 if (fsqlist == NULL) {
132 (void) fprintf(stderr, "%s: no UFS filesystems with %s file\n",
133 MNTTAB, QFNAME);
134 exit(32);
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);
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*/
152 case 'p':
153 uid = getentry(optarg);
154 if (uid > MAXUID) {
155 (void) unlink(tmpfil);
156 exit(32);
158 getprivs(uid);
159 if (optind == argc) {
160 (void) unlink(tmpfil);
161 usage();
163 for (i = optind; i < argc; i++) {
164 uid = getentry(argv[i]);
165 if (uid > MAXUID) {
166 (void) unlink(tmpfil);
167 exit(32);
169 getdiscq(uid);
170 putprivs(uid);
172 (void) unlink(tmpfil);
173 exit(0);
174 /*NOTREACHED*/
176 case 'V': /* Print command line */
178 char *optt;
179 int optc;
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);
187 (void) putchar('\n');
189 break;
191 case '?':
192 usage();
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");
208 (void) unlink(tmpfil);
209 return (0);
212 static uid_t
213 getentry(char *name)
215 struct passwd *pw;
216 uid_t uid;
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);
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);
235 return (uid);
238 #define RESPSZ 128
240 static int
241 editit(void)
243 pid_t pid, xpid;
244 char *ed;
245 char resp[RESPSZ];
246 int status, omask;
248 #define mask(s) (1 << ((s) - 1))
249 omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
251 if ((ed = getenv("EDITOR")) == (char *)0)
252 ed = DEFEDITOR;
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);
262 perror("fork");
263 return (0);
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);
275 while ((xpid = wait(&status)) >= 0)
276 if (xpid == pid)
277 break;
279 if (!isatty(fileno(stdin))) { /* Non-interactive */
280 break;
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.)
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);
300 if ((*resp == 'e') || (*resp == 'q')) {
301 break;
305 if (*resp == 'e') {
306 continue;
307 } else {
309 * Since (*resp == 'q'), then we just
310 * want to break out of here and return
311 * the failure.
313 break;
315 } else {
316 break; /* Successful return from editor */
319 (void) sigsetmask(omask);
320 return (!status);
323 static void
324 getprivs(uid_t uid)
326 struct fsquot *fsqp;
327 FILE *fd;
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);
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);
348 static void
349 putprivs(uid_t uid)
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;
359 fd = fopen64(tmpfil, "r");
360 if (fd == NULL) {
361 (void) fprintf(stderr, "Can't re-read temp file!!\n");
362 return;
364 while (fgets(line, sizeof (line), fd) != NULL) {
365 struct fsquot *fsqp;
366 char *cp, *dp;
367 int n;
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;
383 if (fsqp == NULL) {
384 (void) fprintf(stderr, "%s: unknown file system\n", cp);
385 continue;
387 while (*cp && *cp == '\t' && *cp == ' ')
388 cp++;
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.
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);
403 if (n != 4) {
404 (void) fprintf(stderr, "%s: bad format\n", cp);
405 continue;
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.
419 max_limit = dbtok(UINT_MAX);
421 quota_entry_printed = 0; /* only print quota entry once */
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;
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);
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;
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);
447 * Now check the file limits against their maximum, which
448 * is UINT_MAX (since it must fit in a uint32_t).
450 max_limit = UINT_MAX;
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;
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);
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;
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);
474 changed++;
475 tmp_bsoftlimit = ktodb(tmp_bsoftlimit);
476 tmp_bhardlimit = ktodb(tmp_bhardlimit);
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.
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;
492 (void) fclose(fd);
493 if (changed)
494 putdiscq(uid);
497 static void
498 gettimes(uid_t uid)
500 struct fsquot *fsqp;
501 FILE *fd;
502 char btime[80], ftime[80];
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);
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);
518 (void) fclose(fd);
521 static void
522 puttimes(uid_t uid)
524 FILE *fd;
525 char line[BUFSIZ];
526 int changed = 0;
527 double btimelimit, ftimelimit;
528 char bunits[80], funits[80];
530 fd = fopen64(tmpfil, "r");
531 if (fd == NULL) {
532 (void) fprintf(stderr, "Can't re-read temp file!!\n");
533 return;
535 while (fgets(line, sizeof (line), fd) != NULL) {
536 struct fsquot *fsqp;
537 char *cp, *dp;
538 int n;
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;
554 if (fsqp == NULL) {
555 (void) fprintf(stderr, "%s: unknown file system\n", cp);
556 continue;
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;
572 changed++;
574 (void) fclose(fd);
575 if (changed)
576 putdiscq(uid);
579 static char *
580 next(char *cp, char *match)
582 char *dp;
584 while (cp && *cp) {
585 for (dp = match; dp && *dp; dp++)
586 if (*dp == *cp)
587 return (cp);
588 cp++;
590 return ((char *)0);
593 static int
594 alldigits(char *s)
596 int c = *s++;
598 do {
599 if (!isdigit(c))
600 return (0);
601 } while ((c = *s++) != '\0');
603 return (1);
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"}
618 static void
619 fmttime(char *buf, ulong_t time)
621 double value;
622 int i;
624 if (time == 0) {
625 (void) strcpy(buf, "0 (default)");
626 return;
628 for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++)
629 if (time >= cunits[i].c_secs)
630 break;
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" : "");
637 static int
638 unfmttime(double value, char *units, uint32_t *timep)
640 int i;
642 if (value == 0.0) {
643 *timep = 0;
644 return (1);
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;
651 if (i >= sizeof (cunits) / sizeof (cunits[0]))
652 return (0);
653 *timep = (ulong_t)(value * cunits[i].c_secs);
654 return (1);
657 static void
658 setupfs(void)
660 struct mnttab mntp;
661 struct fsquot *fsqp;
662 struct stat64 statb;
663 dev_t fsdev;
664 FILE *mtab;
665 char qfilename[MAXPATHLEN];
667 if ((mtab = fopen(MNTTAB, "r")) == (FILE *)0) {
668 perror("/etc/mnttab");
669 exit(31+1);
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);
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);
697 fsqlist = fsqp;
699 (void) fclose(mtab);
702 static void
703 getdiscq(uid_t uid)
705 struct fsquot *fsqp;
706 int fd;
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;
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:
721 * Convert implicit 0 quota (EOF)
722 * into an explicit one (zero'ed dqblk)
724 bzero((caddr_t)&fsqp->fsq_dqb,
725 sizeof (struct dqblk));
726 break;
728 case sizeof (struct dqblk): /* OK */
729 break;
731 default: /* ERROR */
732 (void) fprintf(stderr,
733 "edquota: read error in ");
734 perror(fsqp->fsq_qfile);
735 break;
737 (void) close(fd);
742 static void
743 putdiscq(uid_t uid)
745 struct fsquot *fsqp;
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;
752 if ((fd = open64(fsqp->fsq_qfile, O_RDWR)) < 0) {
753 (void) fprintf(stderr, "edquota: ");
754 perror(fsqp->fsq_qfile);
755 continue;
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);
763 (void) close(fd);
768 static void
769 sigsetmask(uint_t omask)
771 int i;
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);
783 static uint_t
784 sigblock(uint_t omask)
786 uint_t previous = 0;
787 uint_t temp;
788 int i;
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);
797 if (i == 0)
798 previous = temp;
801 return (previous);
804 static void
805 usage(void)
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);
813 static int
814 quotactl(int cmd, char *special, uid_t uid, caddr_t addr)
816 int fd;
817 int status;
818 struct quotctl quota;
819 char qfile[MAXPATHLEN];
820 FILE *fstab;
821 struct mnttab mntp;
823 if ((special == NULL) && (cmd == Q_SYNC)) {
824 cmd = Q_ALLSYNC;
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
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.
839 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
840 (void) fprintf(stderr, "%s: ", MNTTAB);
841 perror("open");
842 exit(31+1);
844 qfile[0] = '\0';
845 while ((status = getmntent(fstab, &mntp)) == NULL) {
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
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);
863 break;
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);
873 (void) fclose(fstab);
874 if (qfile[0] == '\0') {
875 errno = ENOENT;
876 return (-1);
879 int open_flags;
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);
889 open_flags = O_RDWR;
892 if ((fd = open64(qfile, open_flags)) < 0) {
893 (void) fprintf(stderr, "quotactl: ");
894 perror("open");
895 exit(31+1);
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);