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]
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
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
54 #include <sys/mnttab.h>
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <sys/mntent.h>
60 #include <sys/fs/ufs_quota.h>
61 #include <sys/fs/ufs_fs.h>
64 #include <iso/limits_iso.h>
66 #define DEFEDITOR "/usr/bin/vi"
69 #define dbtok(x) ((x) / (1024 / DEV_BSIZE))
70 #define ktodb(x) ((x) * (1024 / DEV_BSIZE))
72 #define dbtok(x) ((x) * (DEV_BSIZE / 1024))
73 #define ktodb(x) ((x) / (DEV_BSIZE / 1024))
77 struct fsquot
*fsq_next
;
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
)
120 if (quotactl(Q_SYNC
, (char *)NULL
, 0, (caddr_t
)NULL
) < 0 &&
122 (void) printf("Warning: "
123 "Quotas are not compiled into this kernel\n");
127 (void) fprintf(stderr
, "%s: permission denied\n", basename
);
131 if (fsqlist
== NULL
) {
132 (void) fprintf(stderr
, "%s: no UFS filesystems with %s file\n",
136 tmpfd
= mkstemp(tmpfil
);
137 if (tmpfd
== -1 || fchown(tmpfd
, getuid(), getgid()) == -1) {
138 fprintf(stderr
, "failure in temporary file %s\n", tmpfil
);
142 while ((opt
= getopt(argc
, argv
, "p:tV")) != EOF
)
148 (void) unlink(tmpfil
);
153 uid
= getentry(optarg
);
155 (void) unlink(tmpfil
);
159 if (optind
== argc
) {
160 (void) unlink(tmpfil
);
163 for (i
= optind
; i
< argc
; i
++) {
164 uid
= getentry(argv
[i
]);
166 (void) unlink(tmpfil
);
172 (void) unlink(tmpfil
);
176 case 'V': /* Print command line */
181 (void) printf("edquota -F UFS");
182 for (optc
= 1; optc
< argc
; optc
++) {
185 (void) printf(" %s ", optt
);
187 (void) putchar('\n');
195 for (i
= optind
; i
< argc
; i
++) {
196 uid
= getentry(argv
[i
]);
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
);
218 if (alldigits(name
)) {
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",
228 } else if (pw
= getpwnam(name
))
231 (void) fprintf(stderr
, "%s: no such user\n", name
);
248 #define mask(s) (1 << ((s) - 1))
249 omask
= sigblock(mask(SIGINT
)|mask(SIGQUIT
)|mask(SIGHUP
));
251 if ((ed
= getenv("EDITOR")) == (char *)0)
254 /*CONSTANTCONDITION*/
256 if ((pid
= fork()) < 0) {
257 if (errno
== EAGAIN
) {
258 (void) fprintf(stderr
,
259 "You have too many processes\n");
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
);
275 while ((xpid
= wait(&status
)) >= 0)
279 if (!isatty(fileno(stdin
))) { /* Non-interactive */
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*/
294 (void) printf("Edit again (e) or quit, "
295 "discarding changes (q)? ");
296 (void) fflush(stdout
);
297 if (gets(resp
) == NULL
) {
300 if ((*resp
== 'e') || (*resp
== 'q')) {
309 * Since (*resp == 'q'), then we just
310 * want to break out of here and return
316 break; /* Successful return from editor */
319 (void) sigsetmask(omask
);
330 if ((fd
= fopen64(tmpfil
, "w")) == NULL
) {
331 (void) fprintf(stderr
, "edquota: ");
333 (void) unlink(tmpfil
);
336 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
)
338 "fs %s blocks (soft = %lu, hard = %lu) "
339 "inodes (soft = %lu, hard = %lu)\n",
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
);
352 uint64_t tmp_bsoftlimit
, tmp_bhardlimit
, tmp_fsoftlimit
,
357 int quota_entry_printed
;
359 fd
= fopen64(tmpfil
, "r");
361 (void) fprintf(stderr
, "Can't re-read temp file!!\n");
364 while (fgets(line
, sizeof (line
), fd
) != NULL
) {
369 cp
= next(line
, " \t");
373 while (*cp
&& *cp
== '\t' && *cp
== ' ')
375 dp
= cp
, cp
= next(cp
, " \t");
379 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
) {
380 if (strcmp(dp
, fsqp
->fsq_fs
) == 0)
384 (void) fprintf(stderr
, "%s: unknown file system\n", cp
);
387 while (*cp
&& *cp
== '\t' && *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.
396 "blocks (soft = %llu, hard = %llu) "
397 "inodes (soft = %llu, hard = %llu)\n",
404 (void) fprintf(stderr
, "%s: bad format\n", cp
);
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
);
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
;
502 char btime
[80], ftime
[80];
505 if ((fd
= fopen64(tmpfil
, "w")) == NULL
) {
506 (void) fprintf(stderr
, "edquota: ");
508 (void) unlink(tmpfil
);
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
);
515 "fs %s blocks time limit = %s, files time limit = %s\n",
516 fsqp
->fsq_fs
, btime
, ftime
);
527 double btimelimit
, ftimelimit
;
528 char bunits
[80], funits
[80];
530 fd
= fopen64(tmpfil
, "r");
532 (void) fprintf(stderr
, "Can't re-read temp file!!\n");
535 while (fgets(line
, sizeof (line
), fd
) != NULL
) {
540 cp
= next(line
, " \t");
544 while (*cp
&& *cp
== '\t' && *cp
== ' ')
546 dp
= cp
, cp
= next(cp
, " \t");
550 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
) {
551 if (strcmp(dp
, fsqp
->fsq_fs
) == 0)
555 (void) fprintf(stderr
, "%s: unknown file system\n", cp
);
558 while (*cp
&& *cp
== '\t' && *cp
== ' ')
561 "blocks time limit = %lf %[^,], "
562 "files time limit = %lf %s\n",
563 &btimelimit
, bunits
, &ftimelimit
, funits
);
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
);
580 next(char *cp
, char *match
)
585 for (dp
= match
; dp
&& *dp
; dp
++)
601 } while ((c
= *s
++) != '\0');
607 int c_secs
; /* conversion units in secs */
608 char *c_str
; /* unit string */
610 {60*60*24*28, "month"},
611 {60*60*24*7, "week"},
619 fmttime(char *buf
, ulong_t time
)
625 (void) strcpy(buf
, "0 (default)");
628 for (i
= 0; i
< sizeof (cunits
) / sizeof (cunits
[0]); i
++)
629 if (time
>= cunits
[i
].c_secs
)
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" : "");
638 unfmttime(double value
, char *units
, uint32_t *timep
)
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)
651 if (i
>= sizeof (cunits
) / sizeof (cunits
[0]))
653 *timep
= (ulong_t
)(value
* cunits
[i
].c_secs
);
665 char qfilename
[MAXPATHLEN
];
667 if ((mtab
= fopen(MNTTAB
, "r")) == (FILE *)0) {
668 perror("/etc/mnttab");
671 while (getmntent(mtab
, &mntp
) == 0) {
672 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0)
674 if (stat64(mntp
.mnt_special
, &statb
) < 0)
676 if ((statb
.st_mode
& S_IFMT
) != S_IFBLK
)
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
)
683 fsqp
= malloc(sizeof (struct fsquot
));
685 (void) fprintf(stderr
, "out of memory\n");
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");
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
);
716 (void) llseek(fd
, (offset_t
)dqoff(uid
), L_SET
);
717 switch (read(fd
, (char *)&fsqp
->fsq_dqb
,
718 sizeof (struct dqblk
))) {
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
));
728 case sizeof (struct dqblk
): /* OK */
732 (void) fprintf(stderr
,
733 "edquota: read error in ");
734 perror(fsqp
->fsq_qfile
);
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) {
752 if ((fd
= open64(fsqp
->fsq_qfile
, O_RDWR
)) < 0) {
753 (void) fprintf(stderr
, "edquota: ");
754 perror(fsqp
->fsq_qfile
);
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
);
769 sigsetmask(uint_t omask
)
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
));
784 sigblock(uint_t omask
)
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
));
807 (void) fprintf(stderr
, "ufs usage:\n");
808 (void) fprintf(stderr
, "\tedquota [-p username] username ...\n");
809 (void) fprintf(stderr
, "\tedquota -t\n");
814 quotactl(int cmd
, char *special
, uid_t uid
, caddr_t addr
)
818 struct quotctl quota
;
819 char qfile
[MAXPATHLEN
];
823 if ((special
== NULL
) && (cmd
== Q_SYNC
)) {
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
);
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
)))
857 if (cmd
== Q_ALLSYNC
) { /* implies (special==0) too */
858 if (strlcpy(qfile
, mntp
.mnt_mountp
,
859 sizeof (qfile
)) >= sizeof (qfile
)) {
865 if (strcmp(special
, mntp
.mnt_special
) == 0) {
866 if (strlcpy(qfile
, mntp
.mnt_mountp
,
867 sizeof (qfile
)) >= sizeof (qfile
)) {
873 (void) fclose(fstab
);
874 if (qfile
[0] == '\0') {
881 if (cmd
== Q_ALLSYNC
) {
882 open_flags
= O_RDONLY
;
884 if (strlcat(qfile
, "/" QFNAME
, sizeof (qfile
)) >=
892 if ((fd
= open64(qfile
, open_flags
)) < 0) {
893 (void) fprintf(stderr
, "quotactl: ");
902 status
= ioctl(fd
, Q_QUOTACTL
, "a
);