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 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
39 #pragma ident "%Z%%M% %I% %E% SMI"
42 * Fix up / report on disc quotas & usage
51 #include <sys/filio.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/mntent.h>
57 #include <sys/vnode.h>
58 #include <sys/fs/ufs_inode.h>
59 #include <sys/fs/ufs_fs.h>
60 #include <sys/fs/ufs_quota.h>
63 #include <sys/mnttab.h>
64 #include <sys/vfstab.h>
66 #include <iso/limits_iso.h>
72 #define sblock un.sblk
75 struct dinode itab
[ITABSZ
];
79 struct fileusage
*fu_next
;
81 uint64_t fu_curblocks
;
85 struct fileusage
*fuhead
[FUHASH
];
86 struct fileusage
*lookup(uid_t
);
87 struct fileusage
*adduid(uid_t
);
91 struct dinode
*ginode();
92 char *mntopt(), *hasvfsopt(), *hasmntopt();
96 extern int fsync(int);
101 static int chkquota();
102 static int quotactl();
107 int vflag
; /* verbose */
108 int aflag
; /* all file systems */
109 int pflag
; /* fsck like parallel check */
110 int fflag
; /* force flag */
112 #define QFNAME "quotas"
115 struct dqblk zerodqbuf
;
116 struct fileusage zerofileusage
;
119 main(int argc
, char **argv
)
122 struct vfstab vfsbuf
;
126 char quotafile
[MAXPATHLEN
];
131 if ((listbuf
= (char **)malloc(sizeof (char *) * CHUNK
)) == NULL
) {
132 fprintf(stderr
, "Can't alloc lisbuf array.");
136 while ((opt
= getopt(argc
, argv
, "vapVf")) != EOF
) {
151 case 'V': /* Print command line */
156 (void) fprintf(stdout
, "quotacheck -F UFS ");
157 for (opt_count
= 1; opt_count
< argc
;
159 opt_text
= argv
[opt_count
];
161 (void) fprintf(stdout
, " %s ",
164 (void) fprintf(stdout
, "\n");
176 if (argc
<= optind
&& !aflag
) {
180 if (quotactl(Q_ALLSYNC
, NULL
, (uid_t
)0, NULL
) < 0 &&
181 errno
== EINVAL
&& vflag
)
182 printf("Warning: Quotas are not compiled into this kernel\n");
187 * Go through vfstab and make a list of appropriate
192 if ((vfstab
= fopen(VFSTAB
, "r")) == NULL
) {
193 fprintf(stderr
, "Can't open ");
197 while (getvfsent(vfstab
, &vfsbuf
) == NULL
) {
198 if (strcmp(vfsbuf
.vfs_fstype
, MNTTYPE_UFS
) != 0 ||
199 (vfsbuf
.vfs_mntopts
== 0) ||
200 hasvfsopt(&vfsbuf
, MNTOPT_RO
) ||
201 (!hasvfsopt(&vfsbuf
, MNTOPT_RQ
) &&
202 !hasvfsopt(&vfsbuf
, MNTOPT_QUOTA
)))
204 *listp
= malloc(strlen(vfsbuf
.vfs_special
) + 1);
205 strcpy(*listp
, vfsbuf
.vfs_special
);
208 /* grow listbuf if needed */
209 if (listcnt
>= listmax
) {
211 listbuf
= (char **)realloc(listbuf
,
212 sizeof (char *) * listmax
);
213 if (listbuf
== NULL
) {
215 "Can't grow listbuf.\n");
218 listp
= &listbuf
[listcnt
];
225 listp
= &argv
[optind
];
226 listcnt
= argc
- optind
;
229 errs
= preen(listcnt
, listp
);
231 if ((mtab
= fopen(MNTTAB
, "r")) == NULL
) {
232 fprintf(stderr
, "Can't open ");
236 while (getmntent(mtab
, &mntp
) == NULL
) {
237 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) == 0 &&
238 !hasmntopt(&mntp
, MNTOPT_RO
) &&
239 (oneof(mntp
.mnt_special
, listp
, listcnt
) ||
240 oneof(mntp
.mnt_mountp
, listp
, listcnt
))) {
241 (void) snprintf(quotafile
, sizeof (quotafile
),
242 "%s/%s", mntp
.mnt_mountp
, QFNAME
);
244 chkquota(mntp
.mnt_special
,
245 mntp
.mnt_mountp
, quotafile
);
252 fprintf(stderr
, "Cannot check %s\n", *listp
);
269 preen(int listcnt
, char **listp
)
272 char **lp
, *rdev
, *bdev
;
273 extern char *getfullrawname(), *getfullblkname();
274 struct mnttab mntp
, mpref
;
275 struct active
*alist
, *ap
;
277 char quotafile
[MAXPATHLEN
];
278 char name
[MAXPATHLEN
];
279 int nactive
, serially
;
281 if ((mtab
= fopen(MNTTAB
, "r")) == NULL
) {
282 fprintf(stderr
, "Can't open ");
286 memset(&mpref
, 0, sizeof (struct mnttab
));
289 for (lp
= listp
, i
= 0; i
< listcnt
; lp
++, i
++) {
291 rdev
= getfullrawname(*lp
);
292 if (rdev
== NULL
|| *rdev
== '\0') {
293 fprintf(stderr
, "can't get rawname for `%s'\n", *lp
);
295 } else if (preen_addev(rdev
) != 0) {
296 fprintf(stderr
, "preen_addev error\n");
305 mpref
.mnt_special
= *lp
;
306 if (getmntany(mtab
, &mntp
, &mpref
) == 0 &&
307 strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) == 0 &&
308 !hasmntopt(&mntp
, MNTOPT_RO
)) {
309 errs
+= (31+chkquota(mntp
.mnt_special
,
310 mntp
.mnt_mountp
, quotafile
));
318 while ((rc
= preen_getdev(name
)) > 0) {
321 bdev
= getfullblkname(name
);
322 if (bdev
== NULL
|| *bdev
== '\0') {
323 fprintf(stderr
, "can't get blkname for `%s'\n",
330 mpref
.mnt_special
= bdev
;
331 if (getmntany(mtab
, &mntp
, &mpref
) != 0) {
332 fprintf(stderr
, "`%s' not mounted?\n", name
);
333 preen_releasedev(name
);
336 } else if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0 ||
337 hasmntopt(&mntp
, MNTOPT_RO
) ||
338 (!oneof(mntp
.mnt_special
, listp
, listcnt
) &&
339 !oneof(mntp
.mnt_mountp
, listp
, listcnt
))) {
340 preen_releasedev(name
);
345 ap
= (struct active
*)malloc(sizeof (struct active
));
347 fprintf(stderr
, "out of memory\n");
350 ap
->rdev
= (char *)strdup(name
);
351 if (ap
->rdev
== NULL
) {
352 fprintf(stderr
, "out of memory\n");
357 switch (ap
->pid
= fork()) {
363 (void) snprintf(quotafile
, sizeof (quotafile
),
364 "%s/%s", mntp
.mnt_mountp
, QFNAME
);
365 exit(31+chkquota(mntp
.mnt_special
,
366 mntp
.mnt_mountp
, quotafile
));
374 errs
+= waiter(&alist
);
381 while (nactive
> 0) {
382 errs
+= waiter(&alist
);
389 waiter(struct active
**alp
)
393 struct active
*ap
, *lap
;
395 curpid
= wait(&status
);
403 for (lap
= NULL
, ap
= *alp
; ap
!= NULL
; lap
= ap
, ap
= ap
->nxt
) {
404 if (ap
->pid
== curpid
)
409 fprintf(stderr
, "wait returns unknown pid\n");
416 preen_releasedev(ap
->rdev
);
419 return (WHIBYTE(status
));
423 chkquota(char *fsdev
, char *fsfile
, char *qffile
)
425 struct fileusage
*fup
;
434 extern char *getfullrawname();
436 if ((rawdisk
= getfullrawname(fsdev
)) == NULL
) {
437 fprintf(stderr
, "malloc failed\n");
441 if (*rawdisk
== '\0') {
442 fprintf(stderr
, "Could not find character device for %s\n",
448 printf("*** Checking quotas for %s (%s)\n", rawdisk
, fsfile
);
449 fi
= open64(rawdisk
, 0);
454 qf
= fopen64(qffile
, "r+");
460 if (fstat64(fileno(qf
), &statb
) < 0) {
466 quotadev
= statb
.st_dev
;
467 if (stat64(fsdev
, &statb
) < 0) {
473 if (quotadev
!= statb
.st_rdev
) {
474 fprintf(stderr
, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
475 qffile
, quotadev
, fsdev
, statb
.st_rdev
);
480 bread((diskaddr_t
)SBLOCK
, (char *)&sblock
, SBSIZE
);
483 * Flush filesystem since we are going to read
484 * disk raw and we want to make sure everything is
485 * synced to disk before we read it.
488 if (ioctl(fileno(qf
), _FIOFFS
, NULL
) == -1) {
490 (void) fprintf(stderr
, "%s: cannot flush file system.\n",
497 * no need to quotacheck a rw, mounted, and logging file system
499 if ((fflag
== 0) && pflag
&&
500 (FSOKAY
== (sblock
.fs_state
+ sblock
.fs_time
)) &&
501 (sblock
.fs_clean
== FSLOG
)) {
507 for (cg
= 0; cg
< sblock
.fs_ncg
; cg
++) {
509 for (i
= 0; i
< sblock
.fs_ipg
; i
++)
512 for (uid
= 0; uid
<= MAXUID
&& uid
>= 0; uid
++) {
513 (void) fread(&dqbuf
, sizeof (struct dqblk
), 1, qf
);
518 fup
= &zerofileusage
;
519 if (dqbuf
.dqb_bhardlimit
== 0 && dqbuf
.dqb_bsoftlimit
== 0 &&
520 dqbuf
.dqb_fhardlimit
== 0 && dqbuf
.dqb_fsoftlimit
== 0) {
521 fup
->fu_curfiles
= 0;
522 fup
->fu_curblocks
= 0;
524 if (dqbuf
.dqb_curfiles
== fup
->fu_curfiles
&&
525 dqbuf
.dqb_curblocks
== fup
->fu_curblocks
) {
526 fup
->fu_curfiles
= 0;
527 fup
->fu_curblocks
= 0;
531 * The maximum number of blocks that can be stored in the
532 * dqb_curblocks field in the quota record is 2^32 - 1,
533 * since it must fit into an unsigned 32-bit quantity.
534 * If this user has more blocks than that, print a message
535 * to that effect and reduce the count of allocated blocks
536 * to the maximum value, which is UINT_MAX.
538 if (fup
->fu_curblocks
> UINT_MAX
) {
540 printf("%s: ", rawdisk
);
541 printf("512-byte blocks allocated to user ");
542 if ((pw
= getpwuid(uid
)) && pw
->pw_name
[0])
543 printf("%-10s ", pw
->pw_name
);
545 printf("#%-9d ", uid
);
546 printf(" = %lld\n", fup
->fu_curblocks
);
548 "This exceeds the maximum number of blocks recordable in a quota record.\n");
550 "The value will be set to the maximum, which is %lu.\n", UINT_MAX
);
551 fup
->fu_curblocks
= UINT_MAX
;
556 printf("%s: ", rawdisk
);
557 if ((pw
= getpwuid(uid
)) && pw
->pw_name
[0])
558 printf("%-10s fixed:", pw
->pw_name
);
560 printf("#%-9d fixed:", uid
);
561 if (dqbuf
.dqb_curfiles
!= fup
->fu_curfiles
)
562 printf(" files %lu -> %lu",
563 dqbuf
.dqb_curfiles
, fup
->fu_curfiles
);
564 if (dqbuf
.dqb_curblocks
!= fup
->fu_curblocks
)
565 printf(" blocks %lu -> %llu",
566 dqbuf
.dqb_curblocks
, fup
->fu_curblocks
);
569 dqbuf
.dqb_curfiles
= fup
->fu_curfiles
;
570 dqbuf
.dqb_curblocks
= fup
->fu_curblocks
;
572 * If quotas are not enabled for the current filesystem
573 * then just update the quotas file directly.
575 if ((quotactl(Q_SETQUOTA
, fsfile
, uid
, &dqbuf
) < 0) &&
577 /* back up, overwrite the entry we just read */
578 (void) fseeko64(qf
, (offset_t
)dqoff(uid
), 0);
579 (void) fwrite(&dqbuf
, sizeof (struct dqblk
), 1, qf
);
582 fup
->fu_curfiles
= 0;
583 fup
->fu_curblocks
= 0;
586 (void) fsync(fileno(qf
));
593 acct(struct dinode
*ip
)
595 struct fileusage
*fup
;
599 ip
->di_mode
= ip
->di_smode
;
600 if (ip
->di_suid
!= UID_LONG
) {
601 ip
->di_uid
= ip
->di_suid
;
603 if (ip
->di_mode
== 0)
605 fup
= adduid(ip
->di_uid
);
607 if ((ip
->di_mode
& IFMT
) == IFCHR
|| (ip
->di_mode
& IFMT
) == IFBLK
)
609 fup
->fu_curblocks
+= ip
->di_blocks
;
613 oneof(char *target
, char **olistp
, int on
)
615 char **listp
= olistp
;
619 if (*listp
&& strcmp(target
, *listp
) == 0) {
633 if (dp
== NULL
|| ++dp
>= &itab
[ITABSZ
]) {
634 iblk
= itod(&sblock
, ino
);
635 bread(fsbtodb(&sblock
, iblk
),
636 (char *)itab
, sizeof (itab
));
637 dp
= &itab
[(int)ino
% (int)INOPB(&sblock
)];
639 if (ino
++ < UFSROOTINO
)
645 bread(diskaddr_t bno
, char *buf
, int cnt
)
647 extern offset_t
llseek();
650 pos
= (offset_t
)bno
* DEV_BSIZE
;
651 if (llseek(fi
, pos
, 0) != pos
) {
655 if (read(fi
, buf
, cnt
) != cnt
) {
664 struct fileusage
*fup
;
666 for (fup
= fuhead
[uid
% FUHASH
]; fup
!= 0; fup
= fup
->fu_next
)
667 if (fup
->fu_uid
== uid
)
669 return ((struct fileusage
*)0);
675 struct fileusage
*fup
, **fhp
;
680 fup
= (struct fileusage
*)calloc(1, sizeof (struct fileusage
));
682 fprintf(stderr
, "out of memory for fileusage structures\n");
685 fhp
= &fuhead
[uid
% FUHASH
];
695 fprintf(stderr
, "ufs usage:\n");
696 fprintf(stderr
, "\tquotacheck [-v] [-f] [-p] -a\n");
697 fprintf(stderr
, "\tquotacheck [-v] [-f] [-p] filesys ...\n");
702 quotactl(int cmd
, char *mountp
, uid_t uid
, caddr_t addr
)
706 struct quotctl quota
;
707 char qfile
[MAXPATHLEN
];
712 if ((mountp
== NULL
) && (cmd
== Q_ALLSYNC
)) {
714 * Find the mount point of any ufs file system. This is
715 * because the ioctl that implements the quotactl call has
716 * to go to a real file, and not to the block device.
718 if ((fstab
= fopen(MNTTAB
, "r")) == NULL
) {
719 fprintf(stderr
, "%s: ", MNTTAB
);
724 while ((status
= getmntent(fstab
, &mntp
)) == NULL
) {
725 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0 ||
726 hasmntopt(&mntp
, MNTOPT_RO
))
728 if ((strlcpy(qfile
, mntp
.mnt_mountp
,
729 sizeof (qfile
)) >= sizeof (qfile
)) ||
730 (strlcat(qfile
, "/" QFNAME
, sizeof (qfile
)) >=
734 if ((fd
= open64(qfile
, O_RDWR
)) == -1)
743 if (mountp
== NULL
|| mountp
[0] == '\0') {
747 if ((strlcpy(qfile
, mountp
, sizeof (qfile
)) >=
749 (strlcat(qfile
, "/" QFNAME
, sizeof (qfile
)) >=
754 if ((fd
= open64(qfile
, O_RDWR
)) < 0) {
755 fprintf(stderr
, "quotactl: ");
764 status
= ioctl(fd
, Q_QUOTACTL
, "a
);
771 hasvfsopt(struct vfstab
*vfs
, char *opt
)
774 static char *tmpopts
;
777 tmpopts
= (char *)calloc(256, sizeof (char));
781 strcpy(tmpopts
, vfs
->vfs_mntopts
);
784 for (; *f
; f
= mntopt(&opts
)) {
785 if (strncmp(opt
, f
, strlen(opt
)) == 0)
786 return (f
- tmpopts
+ vfs
->vfs_mntopts
);