dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / ufs / edquota / edquota.c
blob160d974aaa5e27eed90dd79af0acf63bfb061049
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.
41 * Disk quota editor.
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>
64 #define DEFEDITOR "/usr/bin/vi"
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
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;
82 static struct fsquot *fsqlist;
84 static char tmpfil[] = "/tmp/EdP.aXXXXXX";
85 #define QFNAME "quotas"
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);
106 main(int argc, char **argv)
108 uid_t uid;
109 char *basename;
110 int opt;
111 int i;
112 int tmpfd = -1;
114 basename = argv[0];
115 if (argc < 2) {
116 usage();
118 if (quotactl(Q_SYNC, NULL, 0, NULL) < 0 &&
119 errno == EINVAL) {
120 (void) printf("Warning: "
121 "Quotas are not compiled into this kernel\n");
122 (void) sleep(3);
124 if (getuid()) {
125 (void) fprintf(stderr, "%s: permission denied\n", basename);
126 exit(32);
128 setupfs();
129 if (fsqlist == NULL) {
130 (void) fprintf(stderr, "%s: no UFS filesystems with %s file\n",
131 MNTTAB, QFNAME);
132 exit(32);
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);
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*/
150 case 'p':
151 uid = getentry(optarg);
152 if (uid > MAXUID) {
153 (void) unlink(tmpfil);
154 exit(32);
156 getprivs(uid);
157 if (optind == argc) {
158 (void) unlink(tmpfil);
159 usage();
161 for (i = optind; i < argc; i++) {
162 uid = getentry(argv[i]);
163 if (uid > MAXUID) {
164 (void) unlink(tmpfil);
165 exit(32);
167 getdiscq(uid);
168 putprivs(uid);
170 (void) unlink(tmpfil);
171 exit(0);
172 /*NOTREACHED*/
174 case 'V': /* Print command line */
176 char *optt;
177 int optc;
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);
185 (void) putchar('\n');
187 break;
189 case '?':
190 usage();
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");
206 (void) unlink(tmpfil);
207 return (0);
210 static uid_t
211 getentry(char *name)
213 struct passwd *pw;
214 uid_t uid;
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);
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);
233 return (uid);
236 #define RESPSZ 128
238 static int
239 editit(void)
241 pid_t pid, xpid;
242 char *ed;
243 char resp[RESPSZ];
244 int status, omask;
246 #define mask(s) (1 << ((s) - 1))
247 omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP));
249 if ((ed = getenv("EDITOR")) == NULL)
250 ed = DEFEDITOR;
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);
260 perror("fork");
261 return (0);
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);
273 while ((xpid = wait(&status)) >= 0)
274 if (xpid == pid)
275 break;
277 if (!isatty(fileno(stdin))) { /* Non-interactive */
278 break;
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.)
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);
298 if ((*resp == 'e') || (*resp == 'q')) {
299 break;
303 if (*resp == 'e') {
304 continue;
305 } else {
307 * Since (*resp == 'q'), then we just
308 * want to break out of here and return
309 * the failure.
311 break;
313 } else {
314 break; /* Successful return from editor */
317 (void) sigsetmask(omask);
318 return (!status);
321 static void
322 getprivs(uid_t uid)
324 struct fsquot *fsqp;
325 FILE *fd;
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);
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);
346 static void
347 putprivs(uid_t uid)
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;
357 fd = fopen64(tmpfil, "r");
358 if (fd == NULL) {
359 (void) fprintf(stderr, "Can't re-read temp file!!\n");
360 return;
362 while (fgets(line, sizeof (line), fd) != NULL) {
363 struct fsquot *fsqp;
364 char *cp, *dp;
365 int n;
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;
381 if (fsqp == NULL) {
382 (void) fprintf(stderr, "%s: unknown file system\n", cp);
383 continue;
385 while (*cp && *cp == '\t' && *cp == ' ')
386 cp++;
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.
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);
401 if (n != 4) {
402 (void) fprintf(stderr, "%s: bad format\n", cp);
403 continue;
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.
417 max_limit = dbtok(UINT_MAX);
419 quota_entry_printed = 0; /* only print quota entry once */
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;
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);
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;
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);
445 * Now check the file limits against their maximum, which
446 * is UINT_MAX (since it must fit in a uint32_t).
448 max_limit = UINT_MAX;
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;
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);
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;
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);
472 changed++;
473 tmp_bsoftlimit = ktodb(tmp_bsoftlimit);
474 tmp_bhardlimit = ktodb(tmp_bhardlimit);
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.
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;
490 (void) fclose(fd);
491 if (changed)
492 putdiscq(uid);
495 static void
496 gettimes(uid_t uid)
498 struct fsquot *fsqp;
499 FILE *fd;
500 char btime[80], ftime[80];
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);
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);
516 (void) fclose(fd);
519 static void
520 puttimes(uid_t uid)
522 FILE *fd;
523 char line[BUFSIZ];
524 int changed = 0;
525 double btimelimit, ftimelimit;
526 char bunits[80], funits[80];
528 fd = fopen64(tmpfil, "r");
529 if (fd == NULL) {
530 (void) fprintf(stderr, "Can't re-read temp file!!\n");
531 return;
533 while (fgets(line, sizeof (line), fd) != NULL) {
534 struct fsquot *fsqp;
535 char *cp, *dp;
536 int n;
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;
552 if (fsqp == NULL) {
553 (void) fprintf(stderr, "%s: unknown file system\n", cp);
554 continue;
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;
570 changed++;
572 (void) fclose(fd);
573 if (changed)
574 putdiscq(uid);
577 static char *
578 next(char *cp, char *match)
580 char *dp;
582 while (cp && *cp) {
583 for (dp = match; dp && *dp; dp++)
584 if (*dp == *cp)
585 return (cp);
586 cp++;
588 return (NULL);
591 static int
592 alldigits(char *s)
594 int c = *s++;
596 do {
597 if (!isdigit(c))
598 return (0);
599 } while ((c = *s++) != '\0');
601 return (1);
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"}
616 static void
617 fmttime(char *buf, ulong_t time)
619 double value;
620 int i;
622 if (time == 0) {
623 (void) strcpy(buf, "0 (default)");
624 return;
626 for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++)
627 if (time >= cunits[i].c_secs)
628 break;
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" : "");
635 static int
636 unfmttime(double value, char *units, uint32_t *timep)
638 int i;
640 if (value == 0.0) {
641 *timep = 0;
642 return (1);
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;
649 if (i >= sizeof (cunits) / sizeof (cunits[0]))
650 return (0);
651 *timep = (ulong_t)(value * cunits[i].c_secs);
652 return (1);
655 static void
656 setupfs(void)
658 struct mnttab mntp;
659 struct fsquot *fsqp;
660 struct stat64 statb;
661 dev_t fsdev;
662 FILE *mtab;
663 char qfilename[MAXPATHLEN];
665 if ((mtab = fopen(MNTTAB, "r")) == (FILE *)0) {
666 perror("/etc/mnttab");
667 exit(31+1);
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);
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);
695 fsqlist = fsqp;
697 (void) fclose(mtab);
700 static void
701 getdiscq(uid_t uid)
703 struct fsquot *fsqp;
704 int fd;
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;
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:
719 * Convert implicit 0 quota (EOF)
720 * into an explicit one (zero'ed dqblk)
722 bzero((caddr_t)&fsqp->fsq_dqb,
723 sizeof (struct dqblk));
724 break;
726 case sizeof (struct dqblk): /* OK */
727 break;
729 default: /* ERROR */
730 (void) fprintf(stderr,
731 "edquota: read error in ");
732 perror(fsqp->fsq_qfile);
733 break;
735 (void) close(fd);
740 static void
741 putdiscq(uid_t uid)
743 struct fsquot *fsqp;
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;
750 if ((fd = open64(fsqp->fsq_qfile, O_RDWR)) < 0) {
751 (void) fprintf(stderr, "edquota: ");
752 perror(fsqp->fsq_qfile);
753 continue;
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);
761 (void) close(fd);
766 static void
767 sigsetmask(uint_t omask)
769 int i;
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);
781 static uint_t
782 sigblock(uint_t omask)
784 uint_t previous = 0;
785 uint_t temp;
786 int i;
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);
795 if (i == 0)
796 previous = temp;
799 return (previous);
802 static void
803 usage(void)
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);
811 static int
812 quotactl(int cmd, char *special, uid_t uid, caddr_t addr)
814 int fd;
815 int status;
816 struct quotctl quota;
817 char qfile[MAXPATHLEN];
818 FILE *fstab;
819 struct mnttab mntp;
821 if ((special == NULL) && (cmd == Q_SYNC)) {
822 cmd = Q_ALLSYNC;
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
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.
837 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
838 (void) fprintf(stderr, "%s: ", MNTTAB);
839 perror("open");
840 exit(31+1);
842 qfile[0] = '\0';
843 while ((status = getmntent(fstab, &mntp)) == 0) {
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
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);
861 break;
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);
871 (void) fclose(fstab);
872 if (qfile[0] == '\0') {
873 errno = ENOENT;
874 return (-1);
877 int open_flags;
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);
887 open_flags = O_RDWR;
890 if ((fd = open64(qfile, open_flags)) < 0) {
891 (void) fprintf(stderr, "quotactl: ");
892 perror("open");
893 exit(31+1);
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);