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
52 #include <sys/mnttab.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/mntent.h>
58 #include <sys/fs/ufs_quota.h>
59 #include <sys/fs/ufs_fs.h>
62 #include <iso/limits_iso.h>
64 #define DEFEDITOR "/usr/bin/vi"
67 #define dbtok(x) ((x) / (1024 / DEV_BSIZE))
68 #define ktodb(x) ((x) * (1024 / DEV_BSIZE))
70 #define dbtok(x) ((x) * (DEV_BSIZE / 1024))
71 #define ktodb(x) ((x) / (DEV_BSIZE / 1024))
75 struct fsquot
*fsq_next
;
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
)
118 if (quotactl(Q_SYNC
, NULL
, 0, NULL
) < 0 &&
120 (void) printf("Warning: "
121 "Quotas are not compiled into this kernel\n");
125 (void) fprintf(stderr
, "%s: permission denied\n", basename
);
129 if (fsqlist
== NULL
) {
130 (void) fprintf(stderr
, "%s: no UFS filesystems with %s file\n",
134 tmpfd
= mkstemp(tmpfil
);
135 if (tmpfd
== -1 || fchown(tmpfd
, getuid(), getgid()) == -1) {
136 fprintf(stderr
, "failure in temporary file %s\n", tmpfil
);
140 while ((opt
= getopt(argc
, argv
, "p:tV")) != EOF
)
146 (void) unlink(tmpfil
);
151 uid
= getentry(optarg
);
153 (void) unlink(tmpfil
);
157 if (optind
== argc
) {
158 (void) unlink(tmpfil
);
161 for (i
= optind
; i
< argc
; i
++) {
162 uid
= getentry(argv
[i
]);
164 (void) unlink(tmpfil
);
170 (void) unlink(tmpfil
);
174 case 'V': /* Print command line */
179 (void) printf("edquota -F UFS");
180 for (optc
= 1; optc
< argc
; optc
++) {
183 (void) printf(" %s ", optt
);
185 (void) putchar('\n');
193 for (i
= optind
; i
< argc
; i
++) {
194 uid
= getentry(argv
[i
]);
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
);
216 if (alldigits(name
)) {
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",
226 } else if (pw
= getpwnam(name
))
229 (void) fprintf(stderr
, "%s: no such user\n", name
);
246 #define mask(s) (1 << ((s) - 1))
247 omask
= sigblock(mask(SIGINT
)|mask(SIGQUIT
)|mask(SIGHUP
));
249 if ((ed
= getenv("EDITOR")) == NULL
)
252 /*CONSTANTCONDITION*/
254 if ((pid
= fork()) < 0) {
255 if (errno
== EAGAIN
) {
256 (void) fprintf(stderr
,
257 "You have too many processes\n");
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
);
273 while ((xpid
= wait(&status
)) >= 0)
277 if (!isatty(fileno(stdin
))) { /* Non-interactive */
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*/
292 (void) printf("Edit again (e) or quit, "
293 "discarding changes (q)? ");
294 (void) fflush(stdout
);
295 if (gets(resp
) == NULL
) {
298 if ((*resp
== 'e') || (*resp
== 'q')) {
307 * Since (*resp == 'q'), then we just
308 * want to break out of here and return
314 break; /* Successful return from editor */
317 (void) sigsetmask(omask
);
328 if ((fd
= fopen64(tmpfil
, "w")) == NULL
) {
329 (void) fprintf(stderr
, "edquota: ");
331 (void) unlink(tmpfil
);
334 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
)
336 "fs %s blocks (soft = %lu, hard = %lu) "
337 "inodes (soft = %lu, hard = %lu)\n",
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
);
350 uint64_t tmp_bsoftlimit
, tmp_bhardlimit
, tmp_fsoftlimit
,
355 int quota_entry_printed
;
357 fd
= fopen64(tmpfil
, "r");
359 (void) fprintf(stderr
, "Can't re-read temp file!!\n");
362 while (fgets(line
, sizeof (line
), fd
) != NULL
) {
367 cp
= next(line
, " \t");
371 while (*cp
&& *cp
== '\t' && *cp
== ' ')
373 dp
= cp
, cp
= next(cp
, " \t");
377 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
) {
378 if (strcmp(dp
, fsqp
->fsq_fs
) == 0)
382 (void) fprintf(stderr
, "%s: unknown file system\n", cp
);
385 while (*cp
&& *cp
== '\t' && *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.
394 "blocks (soft = %llu, hard = %llu) "
395 "inodes (soft = %llu, hard = %llu)\n",
402 (void) fprintf(stderr
, "%s: bad format\n", cp
);
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
);
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
;
500 char btime
[80], ftime
[80];
503 if ((fd
= fopen64(tmpfil
, "w")) == NULL
) {
504 (void) fprintf(stderr
, "edquota: ");
506 (void) unlink(tmpfil
);
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
);
513 "fs %s blocks time limit = %s, files time limit = %s\n",
514 fsqp
->fsq_fs
, btime
, ftime
);
525 double btimelimit
, ftimelimit
;
526 char bunits
[80], funits
[80];
528 fd
= fopen64(tmpfil
, "r");
530 (void) fprintf(stderr
, "Can't re-read temp file!!\n");
533 while (fgets(line
, sizeof (line
), fd
) != NULL
) {
538 cp
= next(line
, " \t");
542 while (*cp
&& *cp
== '\t' && *cp
== ' ')
544 dp
= cp
, cp
= next(cp
, " \t");
548 for (fsqp
= fsqlist
; fsqp
; fsqp
= fsqp
->fsq_next
) {
549 if (strcmp(dp
, fsqp
->fsq_fs
) == 0)
553 (void) fprintf(stderr
, "%s: unknown file system\n", cp
);
556 while (*cp
&& *cp
== '\t' && *cp
== ' ')
559 "blocks time limit = %lf %[^,], "
560 "files time limit = %lf %s\n",
561 &btimelimit
, bunits
, &ftimelimit
, funits
);
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
);
578 next(char *cp
, char *match
)
583 for (dp
= match
; dp
&& *dp
; dp
++)
599 } while ((c
= *s
++) != '\0');
605 int c_secs
; /* conversion units in secs */
606 char *c_str
; /* unit string */
608 {60*60*24*28, "month"},
609 {60*60*24*7, "week"},
617 fmttime(char *buf
, ulong_t time
)
623 (void) strcpy(buf
, "0 (default)");
626 for (i
= 0; i
< sizeof (cunits
) / sizeof (cunits
[0]); i
++)
627 if (time
>= cunits
[i
].c_secs
)
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" : "");
636 unfmttime(double value
, char *units
, uint32_t *timep
)
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)
649 if (i
>= sizeof (cunits
) / sizeof (cunits
[0]))
651 *timep
= (ulong_t
)(value
* cunits
[i
].c_secs
);
663 char qfilename
[MAXPATHLEN
];
665 if ((mtab
= fopen(MNTTAB
, "r")) == (FILE *)0) {
666 perror("/etc/mnttab");
669 while (getmntent(mtab
, &mntp
) == 0) {
670 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0)
672 if (stat64(mntp
.mnt_special
, &statb
) < 0)
674 if ((statb
.st_mode
& S_IFMT
) != S_IFBLK
)
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
)
681 fsqp
= malloc(sizeof (struct fsquot
));
683 (void) fprintf(stderr
, "out of memory\n");
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");
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
);
714 (void) llseek(fd
, (offset_t
)dqoff(uid
), L_SET
);
715 switch (read(fd
, (char *)&fsqp
->fsq_dqb
,
716 sizeof (struct dqblk
))) {
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
));
726 case sizeof (struct dqblk
): /* OK */
730 (void) fprintf(stderr
,
731 "edquota: read error in ");
732 perror(fsqp
->fsq_qfile
);
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) {
750 if ((fd
= open64(fsqp
->fsq_qfile
, O_RDWR
)) < 0) {
751 (void) fprintf(stderr
, "edquota: ");
752 perror(fsqp
->fsq_qfile
);
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
);
767 sigsetmask(uint_t omask
)
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
));
782 sigblock(uint_t omask
)
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
));
805 (void) fprintf(stderr
, "ufs usage:\n");
806 (void) fprintf(stderr
, "\tedquota [-p username] username ...\n");
807 (void) fprintf(stderr
, "\tedquota -t\n");
812 quotactl(int cmd
, char *special
, uid_t uid
, caddr_t addr
)
816 struct quotctl quota
;
817 char qfile
[MAXPATHLEN
];
821 if ((special
== NULL
) && (cmd
== Q_SYNC
)) {
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
);
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
)))
855 if (cmd
== Q_ALLSYNC
) { /* implies (special==0) too */
856 if (strlcpy(qfile
, mntp
.mnt_mountp
,
857 sizeof (qfile
)) >= sizeof (qfile
)) {
863 if (strcmp(special
, mntp
.mnt_special
) == 0) {
864 if (strlcpy(qfile
, mntp
.mnt_mountp
,
865 sizeof (qfile
)) >= sizeof (qfile
)) {
871 (void) fclose(fstab
);
872 if (qfile
[0] == '\0') {
879 if (cmd
== Q_ALLSYNC
) {
880 open_flags
= O_RDONLY
;
882 if (strlcat(qfile
, "/" QFNAME
, sizeof (qfile
)) >=
890 if ((fd
= open64(qfile
, open_flags
)) < 0) {
891 (void) fprintf(stderr
, "quotactl: ");
900 status
= ioctl(fd
, Q_QUOTACTL
, "a
);