1 /* $NetBSD: quot.c,v 1.27 2007/07/17 22:00:46 christos Exp $ */
4 * Copyright (C) 1991, 1994 Wolfgang Solfrank.
5 * Copyright (C) 1991, 1994 TooLs GmbH.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: quot.c,v 1.27 2007/07/17 22:00:46 christos Exp $");
39 #include <sys/param.h>
40 #include <sys/mount.h>
42 #include <ufs/ufs/dinode.h>
43 #include <ufs/ffs/fs.h>
54 /* some flags of what to do: */
58 static void (*func
) __P((int, struct fs
*, char *));
59 static long blocksize
;
63 * Original BSD quot doesn't round to number of frags/blocks,
64 * doesn't account for indirection blocks and gets it totally
65 * wrong if the size is a multiple of the blocksize.
66 * The new code always counts the number of DEV_BSIZE byte blocks
67 * instead of the number of kilobytes and converts them to
68 * kByte when done (on request).
71 #define SIZE(n) ((long long)(n))
73 #define SIZE(n) howmany((long long)(n) * DEV_BSIZE, (long long)blocksize)
76 #define INOCNT(fs) ((fs)->fs_ipg)
78 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
79 sizeof(struct ufs2_dinode)) * INOCNT(fs))
82 struct ufs1_dinode dp1
;
83 struct ufs2_dinode dp2
;
85 #define DIP(fs, dp, field) \
86 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \
87 (dp)->dp1.di_##field : (dp)->dp2.di_##field)
90 static int cmpusers
__P((const void *, const void *));
91 static void dofsizes
__P((int, struct fs
*, char *));
92 static void donames
__P((int, struct fs
*, char *));
93 static void douser
__P((int, struct fs
*, char *));
94 static union dinode
*get_inode
__P((int, struct fs
*, ino_t
));
95 static void ffs_oldfscompat
__P((struct fs
*));
96 static void initfsizes
__P((void));
97 static void inituser
__P((void));
98 static int isfree
__P((struct fs
*, union dinode
*));
99 int main
__P((int, char **));
100 void quot
__P((char *, char *));
101 static void usage
__P((void));
102 static struct user
*user
__P((uid_t
));
103 static void uses
__P((uid_t
, daddr_t
, time_t));
104 static void usrrehash
__P((void));
105 static int virtualblocks
__P((struct fs
*, union dinode
*));
108 static union dinode
*
109 get_inode(fd
, super
, ino
)
117 if (fd
< 0) { /* flush cache */
125 if (!ipbuf
|| ino
< last
|| ino
>= last
+ INOCNT(super
)) {
127 && !(ipbuf
= malloc(INOSZ(super
))))
128 errx(1, "allocate inodes");
129 last
= (ino
/ INOCNT(super
)) * INOCNT(super
);
131 (off_t
)ino_to_fsba(super
, last
) << super
->fs_fshift
,
133 read(fd
, ipbuf
, INOSZ(super
)) != INOSZ(super
))
134 errx(1, "read inodes");
137 if (super
->fs_magic
== FS_UFS1_MAGIC
)
138 return ((union dinode
*)
139 &((struct ufs1_dinode
*)ipbuf
)[ino
% INOCNT(super
)]);
140 return ((union dinode
*)
141 &((struct ufs2_dinode
*)ipbuf
)[ino
% INOCNT(super
)]);
145 #define actualblocks(fs, dp) (DIP(fs, dp, blocks) / 2)
147 #define actualblocks(fs, dp) (DIP(fs, dp, blocks))
151 virtualblocks(super
, dp
)
157 sz
= DIP(super
, dp
, size
);
159 if (lblkno(super
, sz
) >= NDADDR
) {
160 nblk
= blkroundup(super
, sz
);
162 nblk
+= super
->fs_bsize
;
168 if (lblkno(super
, sz
) >= NDADDR
) {
169 nblk
= blkroundup(super
, sz
);
170 sz
= lblkno(super
, nblk
);
171 sz
= howmany(sz
- NDADDR
, NINDIR(super
));
173 nblk
+= sz
* super
->fs_bsize
;
174 /* One block on this level is in the inode itself */
175 sz
= howmany(sz
- 1, NINDIR(super
));
178 nblk
= fragroundup(super
, sz
);
180 return nblk
/ DEV_BSIZE
;
190 return (DIP(fs
, dp
, mode
) & IFMT
) == 0;
192 switch (DIP(fs
, dp
, mode
) & IFMT
) {
194 case IFLNK
: /* should check FASTSYMLINK? */
224 (struct user
*)calloc(nusers
, sizeof(struct user
))))
225 errx(1, "allocate users");
227 for (usr
= users
, i
= nusers
; --i
>= 0; usr
++) {
228 usr
->space
= usr
->spc30
= usr
->spc60
= usr
->spc90
= 0;
238 struct user
*usr
, *usrn
;
243 if (!(users
= (struct user
*)calloc(nusers
, sizeof(struct user
))))
244 errx(1, "allocate users");
245 for (usr
= svusr
, i
= nusers
>> 1; --i
>= 0; usr
++) {
246 for (usrn
= users
+ (usr
->uid
&(nusers
- 1));
250 usrn
= users
+ nusers
;
265 for (usr
= users
+ (uid
&(nusers
- 1)), i
= nusers
;
271 if (!(pwd
= getpwuid(uid
))) {
273 (char *)malloc(7)) != NULL
)
274 sprintf(usr
->name
, "#%d", uid
);
278 strlen(pwd
->pw_name
) + 1))
280 strcpy(usr
->name
, pwd
->pw_name
);
283 errx(1, "allocate users");
285 } else if (usr
->uid
== uid
)
289 usr
= users
+ nusers
;
299 return ((const struct user
*)u2
)->space
- ((const struct user
*)u1
)->space
;
302 #define sortusers(users) (qsort((users), nusers, sizeof(struct user), \
321 if (today
- act
> 90L * 24L * 60L * 60L)
323 if (today
- act
> 60L * 24L * 60L * 60L)
325 if (today
- act
> 30L * 24L * 60L * 60L)
335 struct fsizes
*fsz_next
;
336 daddr_t fsz_first
, fsz_last
;
337 ino_t fsz_count
[FSZCNT
];
338 daddr_t fsz_sz
[FSZCNT
];
347 for (fp
= fsizes
; fp
; fp
= fp
->fsz_next
) {
348 for (i
= FSZCNT
; --i
>= 0;) {
349 fp
->fsz_count
[i
] = 0;
356 dofsizes(fd
, super
, name
)
364 struct fsizes
*fp
, **fsp
;
367 maxino
= super
->fs_ncg
* super
->fs_ipg
- 1;
369 if (!(fsizes
= (struct fsizes
*)malloc(sizeof(struct fsizes
))))
370 errx(1, "alloc fsize structure");
372 for (inode
= 0; inode
< maxino
; inode
++) {
374 if ((dp
= get_inode(fd
, super
, inode
))
376 && ((DIP(super
, dp
, mode
) & IFMT
) == IFREG
377 || (DIP(dp
, mode
) & IFMT
) == IFDIR
)
379 && !isfree(super
, dp
)
382 sz
= estimate
? virtualblocks(super
, dp
) :
383 actualblocks(super
, dp
);
386 fsizes
->fsz_count
[FSZCNT
-1]++;
387 fsizes
->fsz_sz
[FSZCNT
-1] += sz
;
389 fsizes
->fsz_count
[sz
]++;
390 fsizes
->fsz_sz
[sz
] += sz
;
394 for (fsp
= &fsizes
; (fp
= *fsp
) != NULL
;
395 fsp
= &fp
->fsz_next
) {
396 if (ksz
< fp
->fsz_last
)
399 if (!fp
|| ksz
< fp
->fsz_first
) {
400 if (!(fp
= (struct fsizes
*)
401 malloc(sizeof(struct fsizes
))))
402 errx(1, "alloc fsize structure");
405 fp
->fsz_first
= (ksz
/ FSZCNT
) * FSZCNT
;
406 fp
->fsz_last
= fp
->fsz_first
+ FSZCNT
;
407 for (i
= FSZCNT
; --i
>= 0;) {
408 fp
->fsz_count
[i
] = 0;
412 fp
->fsz_count
[ksz
% FSZCNT
]++;
413 fp
->fsz_sz
[ksz
% FSZCNT
] += sz
;
419 for (fp
= fsizes
; fp
; fp
= fp
->fsz_next
) {
420 for (i
= 0; i
< FSZCNT
; i
++) {
421 if (fp
->fsz_count
[i
])
422 printf("%ld\t%ld\t%lld\n",
423 (long)(fp
->fsz_first
+ i
),
424 (long)fp
->fsz_count
[i
],
425 SIZE(sz
+= fp
->fsz_sz
[i
]));
431 douser(fd
, super
, name
)
437 struct user
*usr
, *usrs
;
441 maxino
= super
->fs_ncg
* super
->fs_ipg
- 1;
442 for (inode
= 0; inode
< maxino
; inode
++) {
444 if ((dp
= get_inode(fd
, super
, inode
))
445 && !isfree(super
, dp
))
446 uses(DIP(super
, dp
, uid
),
447 estimate
? virtualblocks(super
, dp
) :
448 actualblocks(super
, dp
), DIP(super
, dp
, atime
));
452 if (!(usrs
= (struct user
*)malloc(nusers
* sizeof(struct user
))))
453 errx(1, "allocate users");
454 memmove(usrs
, users
, nusers
* sizeof(struct user
));
456 for (usr
= usrs
, n
= nusers
; --n
>= 0 && usr
->count
; usr
++) {
457 printf("%5lld", SIZE(usr
->space
));
459 printf("\t%5ld", usr
->count
);
460 printf("\t%-8s", usr
->name
);
462 printf("\t%5lld\t%5lld\t%5lld",
463 SIZE(usr
->spc30
), SIZE(usr
->spc60
),
471 donames(fd
, super
, name
)
481 maxino
= super
->fs_ncg
* super
->fs_ipg
- 1;
482 /* first skip the name of the filesystem */
483 while ((c
= getchar()) != EOF
&& (c
< '0' || c
> '9'))
484 while ((c
= getchar()) != EOF
&& c
!= '\n');
487 while (scanf("%" SCNu64
, &inode
) == 1) {
488 if (inode
> maxino
) {
490 warnx("invalid inode %" PRIu64
, inode
);
499 if ((dp
= get_inode(fd
, super
, inode
))
500 && !isfree(super
, dp
)) {
501 printf("%s\t", user(DIP(super
, dp
, uid
))->name
);
502 /* now skip whitespace */
503 while ((c
= getchar()) == ' ' || c
== '\t');
504 /* and print out the remainder of the input line */
505 while (c
!= EOF
&& c
!= '\n') {
515 while ((c
= getchar()) != EOF
&& c
!= '\n');
526 fprintf(stderr
, "usage: quot [-nfcvha] [filesystem ...]\n");
528 fprintf(stderr
, "usage: quot [ -acfhknv ] [ filesystem ... ]\n");
534 * Sanity checks for old file systems.
535 * Stolen from <sys/lib/libsa/ufs.c>
543 if (fs
->fs_old_inodefmt
< FS_44INODEFMT
) {
544 quad_t sizepb
= fs
->fs_bsize
;
546 fs
->fs_maxfilesize
= fs
->fs_bsize
* NDADDR
- 1;
547 for (i
= 0; i
< NIADDR
; i
++) {
548 sizepb
*= NINDIR(fs
);
549 fs
->fs_maxfilesize
+= sizepb
;
551 fs
->fs_qbmask
= ~fs
->fs_bmask
;
552 fs
->fs_qfmask
= ~fs
->fs_fmask
;
557 * Possible superblock locations ordered from most to least likely.
559 static int sblock_try
[] = SBLOCKSEARCH
;
560 static char superblock
[SBLOCKSIZE
];
571 get_inode(-1, 0, 0); /* flush cache */
574 if ((fd
= open(name
, 0)) < 0) {
580 sbloc
= sblock_try
[i
];
582 warnx("%s: not a BSD filesystem", name
);
586 if (pread(fd
, superblock
, SBLOCKSIZE
, sbloc
) != SBLOCKSIZE
)
588 fs
= (struct fs
*)superblock
;
590 if (fs
->fs_magic
!= FS_UFS1_MAGIC
&&
591 fs
->fs_magic
!= FS_UFS2_MAGIC
)
594 if (fs
->fs_magic
== FS_UFS2_MAGIC
595 || fs
->fs_old_flags
& FS_FLAGS_UPDATED
) {
596 /* Not the main superblock */
597 if (fs
->fs_sblockloc
!= sbloc
)
600 /* might be a first alt. id blocksize 64k */
601 if (sbloc
== SBLOCK_UFS2
)
605 if (fs
->fs_bsize
> MAXBSIZE
||
606 fs
->fs_bsize
< sizeof(struct fs
))
611 ffs_oldfscompat((struct fs
*)superblock
);
616 (*func
)(fd
, fs
, name
);
627 char dev
[MNAMELEN
+ 1];
633 header
= getbsize(NULL
, &blocksize
);
635 while (--argc
> 0 && **++argv
== '-') {
667 cnt
= getmntinfo(&mp
, MNT_NOWAIT
);
668 for (; --cnt
>= 0; mp
++) {
669 if (!strncmp(mp
->f_fstypename
, MOUNT_FFS
,
670 sizeof(mp
->f_fstypename
))) {
672 strrchr(mp
->f_mntfromname
, '/')) != NULL
) {
673 sprintf(dev
, "/dev/r%s", nm
+ 1);
676 nm
= mp
->f_mntfromname
;
677 quot(nm
, mp
->f_mntonname
);