]>
git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/quota.tproj/quota.c
2 * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1980, 1990, 1993
25 * The Regents of the University of California. All rights reserved.
27 * This code is derived from software contributed to Berkeley by
28 * Robert Elz at The University of Melbourne.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * Disk quota reporting program.
63 #include <sys/param.h>
66 #include <sys/mount.h>
69 #include <sys/queue.h>
71 #include <sys/quota.h>
72 #include <libkern/OSByteOrder.h>
84 char *qfname
= QUOTAFILENAME
;
85 char *qfextension
[] = INITQFNAMES
;
88 u_int32_t quotamagic
[MAXQUOTAS
] = INITQMAGICS
;
89 #endif /* __APPLE__ */
92 struct quotause
*next
;
95 char fsname
[MAXPATHLEN
+ 1];
102 int alldigits
__P((char *));
103 int hasquota
__P((struct statfs
*, int, char **));
104 void heading
__P((int, u_long
, char *, char *));
105 void showgid
__P((u_long
));
106 void showuid
__P((u_long
));
107 void showgrpname
__P((char *));
108 void showquotas
__P((int, u_long
, char *));
109 void showusrname
__P((char *));
110 void usage
__P((void));
113 int qflookup(int, u_long
, int, struct dqblk
*);
114 #endif /* __APPLE__ */
121 gid_t gidset
[NGROUPS
];
122 int i
, gflag
= 0, uflag
= 0;
126 if (quotactl("/", 0, 0, (caddr_t
)0) < 0 && errno
== ENOTSUP
) {
127 fprintf(stderr
, "There are no quotas on this system\n");
132 while ((ch
= getopt(argc
, argv
, "ugvq")) != EOF
) {
152 if (!uflag
&& !gflag
)
158 ngroups
= getgroups(NGROUPS
, gidset
);
160 perror("quota: getgroups");
163 for (i
= 1; i
< ngroups
; i
++)
171 for (; argc
> 0; argc
--, argv
++) {
172 if (alldigits(*argv
))
173 showuid(atoi(*argv
));
180 for (; argc
> 0; argc
--, argv
++) {
181 if (alldigits(*argv
))
182 showgid(atoi(*argv
));
195 fprintf(stderr
, "%s\n%s\n%s\n",
196 "Usage: quota [-guqv]",
197 "\tquota [-qv] -u username ...",
198 "\tquota [-qv] -g groupname ...");
203 * Print out quotas for a specified user identifier.
209 struct passwd
*pwd
= getpwuid(uid
);
214 name
= "(no account)";
218 if (uid
!= myuid
&& myuid
!= 0) {
219 printf("quota: %s (uid %ld): permission denied\n", name
, uid
);
222 showquotas(USRQUOTA
, uid
, name
);
226 * Print out quotas for a specifed user name.
232 struct passwd
*pwd
= getpwnam(name
);
236 fprintf(stderr
, "quota: %s: unknown user\n", name
);
240 if (pwd
->pw_uid
!= myuid
&& myuid
!= 0) {
241 fprintf(stderr
, "quota: %s (uid %d): permission denied\n",
245 showquotas(USRQUOTA
, pwd
->pw_uid
, name
);
249 * Print out quotas for a specified group identifier.
255 struct group
*grp
= getgrgid(gid
);
257 gid_t gidset
[NGROUPS
];
265 ngroups
= getgroups(NGROUPS
, gidset
);
267 perror("quota: getgroups");
270 for (i
= 1; i
< ngroups
; i
++)
271 if (gid
== gidset
[i
])
273 if (i
>= ngroups
&& getuid() != 0) {
274 fprintf(stderr
, "quota: %s (gid %ld): permission denied\n",
278 showquotas(GRPQUOTA
, gid
, name
);
282 * Print out quotas for a specifed group name.
288 struct group
*grp
= getgrnam(name
);
290 gid_t gidset
[NGROUPS
];
294 fprintf(stderr
, "quota: %s: unknown group\n", name
);
297 ngroups
= getgroups(NGROUPS
, gidset
);
299 perror("quota: getgroups");
302 for (i
= 1; i
< ngroups
; i
++)
303 if (grp
->gr_gid
== gidset
[i
])
305 if (i
>= ngroups
&& getuid() != 0) {
306 fprintf(stderr
, "quota: %s (gid %d): permission denied\n",
310 showquotas(GRPQUOTA
, grp
->gr_gid
, name
);
314 showquotas(type
, id
, name
)
319 register struct quotause
*qup
;
320 struct quotause
*quplist
, *getprivs();
321 char *msgi
, *msgb
, *timeprt();
327 quplist
= getprivs(id
, type
);
328 for (qup
= quplist
; qup
; qup
= qup
->next
) {
330 qup
->dqblk
.dqb_isoftlimit
== 0 &&
331 qup
->dqblk
.dqb_ihardlimit
== 0 &&
332 qup
->dqblk
.dqb_bsoftlimit
== 0 &&
333 qup
->dqblk
.dqb_bhardlimit
== 0)
336 if (qup
->dqblk
.dqb_ihardlimit
&&
337 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_ihardlimit
) {
338 msgi
= "File limit reached on";
339 } else if (qup
->dqblk
.dqb_isoftlimit
&&
340 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_isoftlimit
) {
341 if (qup
->dqblk
.dqb_itime
> now
) {
342 msgi
= "In file grace period on";
344 msgi
= "Over file quota on";
349 if (qup
->dqblk
.dqb_bhardlimit
&&
350 qup
->dqblk
.dqb_curbytes
>= qup
->dqblk
.dqb_bhardlimit
) {
351 msgb
= "Block limit reached on";
352 } else if (qup
->dqblk
.dqb_bsoftlimit
&&
353 qup
->dqblk
.dqb_curbytes
>= qup
->dqblk
.dqb_bsoftlimit
) {
354 if (qup
->dqblk
.dqb_btime
> now
) {
355 msgb
= "In block grace period on";
357 msgb
= "Over block quota on";
361 if (qup
->dqblk
.dqb_bhardlimit
&&
362 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bhardlimit
) {
363 msgb
= "Block limit reached on";
364 } else if (qup
->dqblk
.dqb_bsoftlimit
&&
365 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bsoftlimit
) {
366 if (qup
->dqblk
.dqb_btime
> now
) {
367 msgb
= "In block grace period on";
369 msgb
= "Over block quota on";
372 #endif /* __APPLE__ */
375 if ((msgi
!= (char *)0 || msgb
!= (char *)0) &&
377 heading(type
, id
, name
, "");
378 if (msgi
!= (char *)0)
379 printf("\t%s %s\n", msgi
, qup
->fsname
);
380 if (msgb
!= (char *)0)
381 printf("\t%s %s\n", msgb
, qup
->fsname
);
386 qup
->dqblk
.dqb_curbytes
||
387 qup
->dqblk
.dqb_curinodes
) {
389 heading(type
, id
, name
, "");
391 printf("%15s %12qd%c %12qd %12qd %8s"
393 , qup
->dqblk
.dqb_curbytes
/ 1024
394 , (msgb
== (char *)0) ? ' ' : '*'
395 , qup
->dqblk
.dqb_bsoftlimit
/ 1024
396 , qup
->dqblk
.dqb_bhardlimit
/ 1024
397 , (msgb
== (char *)0) ? ""
398 : timeprt(qup
->dqblk
.dqb_btime
));
401 qup
->dqblk
.dqb_curblocks
||
402 qup
->dqblk
.dqb_curinodes
) {
404 heading(type
, id
, name
, "");
406 printf("%15s%8d%c%7d%8d%8s"
408 , dbtob(qup
->dqblk
.dqb_curblocks
) / 1024
409 , (msgb
== (char *)0) ? ' ' : '*'
410 , dbtob(qup
->dqblk
.dqb_bsoftlimit
) / 1024
411 , dbtob(qup
->dqblk
.dqb_bhardlimit
) / 1024
412 , (msgb
== (char *)0) ? ""
413 : timeprt(qup
->dqblk
.dqb_btime
));
414 #endif /* __APPLE__ */
415 printf("%8d%c%7d%8d%8s\n"
416 , qup
->dqblk
.dqb_curinodes
417 , (msgi
== (char *)0) ? ' ' : '*'
418 , qup
->dqblk
.dqb_isoftlimit
419 , qup
->dqblk
.dqb_ihardlimit
420 , (msgi
== (char *)0) ? ""
421 : timeprt(qup
->dqblk
.dqb_itime
)
426 if (!qflag
&& lines
== 0)
427 heading(type
, id
, name
, "none");
431 heading(type
, id
, name
, tag
)
437 printf("Disk quotas for %s %s (%cid %ld): %s\n", qfextension
[type
],
438 name
, *qfextension
[type
], id
, tag
);
439 if (!qflag
&& tag
[0] == '\0') {
441 printf("%15s %12s %12s %12s %8s%8s %7s%8s%8s\n"
445 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
448 #endif /* __APPLE __*/
461 * Calculate the grace period and return a printable string for it.
467 time_t hours
, minutes
;
476 minutes
= (seconds
+ 30) / 60;
477 hours
= (minutes
+ 30) / 60;
479 snprintf(buf
, sizeof(buf
), "%ddays", (int)((hours
+ 12) / 24));
483 snprintf(buf
, sizeof(buf
), "%2d:%d", (int)(minutes
/ 60), (int)(minutes
% 60));
486 snprintf(buf
, sizeof(buf
), "%2d", (int)minutes
);
499 mount_list_t
*retval
= NULL
;
500 retval
= malloc(sizeof(*retval
));
502 retval
->strings
= malloc(sizeof(char*) * max
);
503 if (retval
->strings
) {
504 retval
->alloced
= max
;
513 free_list(void *list
)
515 mount_list_t
*tmp
= list
;
520 for (i
= 0; i
< tmp
->nels
; i
++) {
521 free(tmp
->strings
[i
]);
528 hasseen(void *tmp
, const char *mp
)
531 mount_list_t
*list
= tmp
;
534 if (tmp
== NULL
|| mp
== NULL
)
538 * This could also be a binary search, but then we'd have to sort
539 * after each addition. We may want to change the behaviour based
540 * on the number of elements in the array.
543 for (i
= 0; i
< list
->nels
; i
++) {
544 if (strcmp(list
->strings
[i
], mp
) == 0) {
549 if (list
->nels
<= list
->alloced
) {
550 char **a
= realloc(list
->strings
, (list
->alloced
+ 10) * sizeof(char*));
552 list
->alloced
= list
->alloced
+ 10;
558 list
->strings
[list
->nels
++] = strdup(mp
);
565 * Collect the requested quota information.
569 getprivs(id
, quotatype
)
574 register struct quotause
*qup
, *quptail
;
575 struct quotause
*quphead
;
583 quptail
= quphead
= (struct quotause
*)0;
584 qcmd
= QCMD(Q_GETQUOTA
, quotatype
);
586 nfst
= getmntinfo(&fst
, MNT_WAIT
);
588 fprintf(stderr
, "quota: no mounted filesystems\n");
592 cache
= init_list(nfst
);
594 for (i
=0; i
<nfst
; i
++) {
595 if (hasseen(cache
, fst
[i
].f_mntonname
))
597 error
= quotactl(fst
[i
].f_mntonname
, qcmd
, id
, (char *)&dqb
);
599 if (strcmp(fst
[i
].f_fstypename
, "hfs"))
601 if (!hasquota(&fst
[i
], quotatype
, &qfpathname
))
604 if ((qup
= (struct quotause
*)malloc(sizeof *qup
)) == NULL
) {
605 fprintf(stderr
, "quota: out of memory\n");
609 bcopy(&dqb
, &qup
->dqblk
, sizeof(dqb
));
611 if ((fd
= open(qfpathname
, O_RDONLY
)) < 0) {
616 if ((error
= qflookup(fd
, id
, quotatype
, &qup
->dqblk
))) {
624 strlcpy(qup
->fsname
, fst
[i
].f_mntonname
, sizeof(qup
->fsname
));
638 getprivs(id
, quotatype
)
642 register struct fstab
*fs
;
643 register struct quotause
*qup
, *quptail
;
644 struct quotause
*quphead
;
649 quphead
= (struct quotause
*)0;
650 qcmd
= QCMD(Q_GETQUOTA
, quotatype
);
651 while (fs
= getfsent()) {
652 if (strcmp(fs
->fs_vfstype
, "ufs"))
654 if (!hasquota(fs
, quotatype
, &qfpathname
))
656 if ((qup
= (struct quotause
*)malloc(sizeof *qup
)) == NULL
) {
657 fprintf(stderr
, "quota: out of memory\n");
660 if (quotactl(fs
->fs_file
, qcmd
, id
, &qup
->dqblk
) != 0) {
661 if ((fd
= open(qfpathname
, O_RDONLY
)) < 0) {
666 lseek(fd
, (off_t
)(id
* sizeof(struct dqblk
)), L_SET
);
667 switch (read(fd
, &qup
->dqblk
, sizeof(struct dqblk
))) {
670 * Convert implicit 0 quota (EOF)
671 * into an explicit one (zero'ed dqblk)
673 bzero((caddr_t
)&qup
->dqblk
,
674 sizeof(struct dqblk
));
677 case sizeof(struct dqblk
): /* OK */
681 fprintf(stderr
, "quota: read error");
689 strlcpy(qup
->fsname
, fs
->fs_file
, sizeof(qup
->fsname
));
701 #endif /* __APPLE__ */
706 * Lookup an entry in the quota file.
709 qflookup(fd
, id
, type
, dqbp
)
715 struct dqfilehdr dqhdr
;
716 int i
, skip
, last
, m
;
719 bzero(dqbp
, sizeof(struct dqblk
));
725 if (read(fd
, &dqhdr
, sizeof(struct dqfilehdr
)) != sizeof(struct dqfilehdr
)) {
726 fprintf(stderr
, "quota: read error\n");
730 /* Sanity check the quota file header. */
731 if ((OSSwapBigToHostInt32(dqhdr
.dqh_magic
) != quotamagic
[type
]) ||
732 (OSSwapBigToHostInt32(dqhdr
.dqh_version
) > QF_VERSION
) ||
733 (!powerof2(OSSwapBigToHostInt32(dqhdr
.dqh_maxentries
)))) {
734 fprintf(stderr
, "quota: invalid quota file header\n");
738 m
= OSSwapBigToHostInt32(dqhdr
.dqh_maxentries
);
740 i
= dqhash1(id
, dqhashshift(m
), mask
);
741 skip
= dqhash2(id
, mask
);
743 for (last
= (i
+ (m
-1) * skip
) & mask
;
745 i
= (i
+ skip
) & mask
) {
746 lseek(fd
, dqoffset(i
), L_SET
);
747 if (read(fd
, dqbp
, sizeof(struct dqblk
)) < sizeof(struct dqblk
)) {
748 fprintf(stderr
, "quota: read error at index %d\n", i
);
752 * Stop when an empty entry is found
753 * or we encounter a matching id.
755 if (dqbp
->dqb_id
== 0 || OSSwapBigToHostInt32(dqbp
->dqb_id
) == id
)
758 /* Put data in host native byte order. */
759 dqbp
->dqb_bhardlimit
= OSSwapBigToHostInt64(dqbp
->dqb_bhardlimit
);
760 dqbp
->dqb_bsoftlimit
= OSSwapBigToHostInt64(dqbp
->dqb_bsoftlimit
);
761 dqbp
->dqb_curbytes
= OSSwapBigToHostInt64(dqbp
->dqb_curbytes
);
762 dqbp
->dqb_ihardlimit
= OSSwapBigToHostInt32(dqbp
->dqb_ihardlimit
);
763 dqbp
->dqb_isoftlimit
= OSSwapBigToHostInt32(dqbp
->dqb_isoftlimit
);
764 dqbp
->dqb_curinodes
= OSSwapBigToHostInt32(dqbp
->dqb_curinodes
);
765 dqbp
->dqb_btime
= OSSwapBigToHostInt32(dqbp
->dqb_btime
);
766 dqbp
->dqb_itime
= OSSwapBigToHostInt32(dqbp
->dqb_itime
);
767 dqbp
->dqb_id
= OSSwapBigToHostInt32(dqbp
->dqb_id
);
771 #endif /* __APPLE__ */
775 * Check to see if a particular quota is to be enabled.
779 hasquota(fst
, type
, qfnamep
)
780 register struct statfs
*fst
;
785 static char initname
, usrname
[100], grpname
[100];
786 static char buf
[BUFSIZ
];
789 snprintf(usrname
, sizeof(usrname
), "%s%s", qfextension
[USRQUOTA
], qfname
);
790 snprintf(grpname
, sizeof(grpname
), "%s%s", qfextension
[GRPQUOTA
], qfname
);
794 We only support the default path to the
798 (void)snprintf(buf
, sizeof(buf
), "%s/%s.%s", fst
->f_mntonname
,
799 QUOTAOPSNAME
, qfextension
[type
] );
800 if (stat(buf
, &sb
) != 0) {
801 /* There appears to be no mount option file */
805 (void) snprintf(buf
, sizeof(buf
), "%s/%s.%s", fst
->f_mntonname
, qfname
, qfextension
[type
]);
810 hasquota(fs
, type
, qfnamep
)
811 register struct fstab
*fs
;
816 char *cp
, *index(), *strtok();
817 static char initname
, usrname
[100], grpname
[100];
818 static char buf
[BUFSIZ
];
821 snprintf(usrname
, sizeof(usrname
), "%s%s", qfextension
[USRQUOTA
], qfname
);
822 snprintf(grpname
, sizeof(grpname
), "%s%s", qfextension
[GRPQUOTA
], qfname
);
825 strlcpy(buf
, fs
->fs_mntops
, sizeof(buf
));
826 for (opt
= strtok(buf
, ","); opt
; opt
= strtok(NULL
, ",")) {
827 if (cp
= index(opt
, '='))
829 if (type
== USRQUOTA
&& strcmp(opt
, usrname
) == 0)
831 if (type
== GRPQUOTA
&& strcmp(opt
, grpname
) == 0)
840 (void) snprintf(buf
, sizeof(buf
), "%s/%s.%s", fs
->fs_file
, qfname
, qfextension
[type
]);
844 #endif /* __APPLE__ */
856 } while ((c
= *s
++));