quot.c revision 1.18 1 /* $NetBSD: quot.c,v 1.18 2003/04/02 10:39:50 fvdl Exp $ */
2
3 /*
4 * Copyright (C) 1991, 1994 Wolfgang Solfrank.
5 * Copyright (C) 1991, 1994 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
21 *
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.
32 */
33
34 #include <sys/cdefs.h>
35 #ifndef lint
36 __RCSID("$NetBSD: quot.c,v 1.18 2003/04/02 10:39:50 fvdl Exp $");
37 #endif /* not lint */
38
39 #include <sys/param.h>
40 #include <sys/mount.h>
41 #include <sys/time.h>
42 #include <ufs/ufs/dinode.h>
43 #include <ufs/ffs/fs.h>
44
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <pwd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 /* some flags of what to do: */
55 static char estimate;
56 static char count;
57 static char unused;
58 static void (*func) __P((int, struct fs *, char *));
59 static long blocksize;
60 static char *header;
61 static int headerlen;
62
63 /*
64 * Original BSD quot doesn't round to number of frags/blocks,
65 * doesn't account for indirection blocks and gets it totally
66 * wrong if the size is a multiple of the blocksize.
67 * The new code always counts the number of DEV_BSIZE byte blocks
68 * instead of the number of kilobytes and converts them to
69 * kByte when done (on request).
70 */
71 #ifdef COMPAT
72 #define SIZE(n) ((long long)(n))
73 #else
74 #define SIZE(n) howmany((long long)(n) * DEV_BSIZE, (long long)blocksize)
75 #endif
76
77 #define INOCNT(fs) ((fs)->fs_ipg)
78 #define INOSZ(fs) \
79 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
80 sizeof(struct ufs2_dinode)) * INOCNT(fs))
81
82 union dinode {
83 struct ufs1_dinode dp1;
84 struct ufs2_dinode dp2;
85 };
86 #define DIP(fs, dp, field) \
87 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \
88 (dp)->dp1.di_##field : (dp)->dp2.di_##field)
89
90
91 static int cmpusers __P((const void *, const void *));
92 static void dofsizes __P((int, struct fs *, char *));
93 static void donames __P((int, struct fs *, char *));
94 static void douser __P((int, struct fs *, char *));
95 static union dinode *get_inode __P((int, struct fs*, ino_t));
96 static void ffs_oldfscompat __P((struct fs *));
97 static void initfsizes __P((void));
98 static void inituser __P((void));
99 static int isfree __P((struct fs *, union dinode *));
100 int main __P((int, char **));
101 void quot __P((char *, char *));
102 static void usage __P((void));
103 static struct user *user __P((uid_t));
104 static void uses __P((uid_t, daddr_t, time_t));
105 static void usrrehash __P((void));
106 static int virtualblocks __P((struct fs *, union dinode *));
107
108
109 static union dinode *
110 get_inode(fd, super, ino)
111 int fd;
112 struct fs *super;
113 ino_t ino;
114 {
115 static char *ipbuf;
116 static ino_t last;
117
118 if (fd < 0) { /* flush cache */
119 if (ipbuf) {
120 free(ipbuf);
121 ipbuf = NULL;
122 }
123 return 0;
124 }
125
126 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
127 if (!ipbuf
128 && !(ipbuf = malloc(INOSZ(super))))
129 errx(1, "allocate inodes");
130 last = (ino / INOCNT(super)) * INOCNT(super);
131 if (lseek(fd,
132 (off_t)ino_to_fsba(super, last) << super->fs_fshift,
133 0) < 0 ||
134 read(fd, ipbuf, INOSZ(super)) != INOSZ(super))
135 errx(1, "read inodes");
136 }
137
138 if (super->fs_magic == FS_UFS1_MAGIC)
139 return ((union dinode *)
140 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
141 return ((union dinode *)
142 &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
143 }
144
145 #ifdef COMPAT
146 #define actualblocks(fs, dp) (DIP(fs, dp, blocks) / 2)
147 #else
148 #define actualblocks(fs, dp) (DIP(fs, dp, blocks))
149 #endif
150
151 static int
152 virtualblocks(super, dp)
153 struct fs *super;
154 union dinode *dp;
155 {
156 off_t nblk, sz;
157
158 sz = DIP(super, dp, size);
159 #ifdef COMPAT
160 if (lblkno(super, sz) >= NDADDR) {
161 nblk = blkroundup(super, sz);
162 if (sz == nblk)
163 nblk += super->fs_bsize;
164 }
165
166 return sz / 1024;
167 #else /* COMPAT */
168
169 if (lblkno(super, sz) >= NDADDR) {
170 nblk = blkroundup(super, sz);
171 sz = lblkno(super, nblk);
172 sz = howmany(sz - NDADDR, NINDIR(super));
173 while (sz > 0) {
174 nblk += sz * super->fs_bsize;
175 /* One block on this level is in the inode itself */
176 sz = howmany(sz - 1, NINDIR(super));
177 }
178 } else
179 nblk = fragroundup(super, sz);
180
181 return nblk / DEV_BSIZE;
182 #endif /* COMPAT */
183 }
184
185 static int
186 isfree(fs, dp)
187 struct fs *fs;
188 union dinode *dp;
189 {
190 #ifdef COMPAT
191 return (DIP(fs, dp, mode) & IFMT) == 0;
192 #else /* COMPAT */
193 switch (DIP(fs, dp, mode) & IFMT) {
194 case IFIFO:
195 case IFLNK: /* should check FASTSYMLINK? */
196 case IFDIR:
197 case IFREG:
198 return 0;
199 default:
200 return 1;
201 }
202 #endif
203 }
204
205 static struct user {
206 uid_t uid;
207 char *name;
208 daddr_t space;
209 long count;
210 daddr_t spc30;
211 daddr_t spc60;
212 daddr_t spc90;
213 } *users;
214 static int nusers;
215
216 static void
217 inituser()
218 {
219 int i;
220 struct user *usr;
221
222 if (!nusers) {
223 nusers = 8;
224 if (!(users =
225 (struct user *)calloc(nusers, sizeof(struct user))))
226 errx(1, "allocate users");
227 } else {
228 for (usr = users, i = nusers; --i >= 0; usr++) {
229 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
230 usr->count = 0;
231 }
232 }
233 }
234
235 static void
236 usrrehash()
237 {
238 int i;
239 struct user *usr, *usrn;
240 struct user *svusr;
241
242 svusr = users;
243 nusers <<= 1;
244 if (!(users = (struct user *)calloc(nusers, sizeof(struct user))))
245 errx(1, "allocate users");
246 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
247 for (usrn = users + (usr->uid&(nusers - 1));
248 usrn->name;
249 usrn--) {
250 if (usrn <= users)
251 usrn = users + nusers;
252 }
253 *usrn = *usr;
254 }
255 }
256
257 static struct user *
258 user(uid)
259 uid_t uid;
260 {
261 struct user *usr;
262 int i;
263 struct passwd *pwd;
264
265 while (1) {
266 for (usr = users + (uid&(nusers - 1)), i = nusers;
267 --i >= 0;
268 usr--) {
269 if (!usr->name) {
270 usr->uid = uid;
271
272 if (!(pwd = getpwuid(uid))) {
273 if ((usr->name =
274 (char *)malloc(7)) != NULL)
275 sprintf(usr->name, "#%d", uid);
276 } else {
277 if ((usr->name =
278 (char *)malloc(
279 strlen(pwd->pw_name) + 1))
280 != NULL)
281 strcpy(usr->name, pwd->pw_name);
282 }
283 if (!usr->name)
284 errx(1, "allocate users");
285 return usr;
286 } else if (usr->uid == uid)
287 return usr;
288
289 if (usr <= users)
290 usr = users + nusers;
291 }
292 usrrehash();
293 }
294 }
295
296 static int
297 cmpusers(u1, u2)
298 const void *u1, *u2;
299 {
300 return ((struct user *)u2)->space - ((struct user *)u1)->space;
301 }
302
303 #define sortusers(users) (qsort((users), nusers, sizeof(struct user), \
304 cmpusers))
305
306 static void
307 uses(uid, blks, act)
308 uid_t uid;
309 daddr_t blks;
310 time_t act;
311 {
312 static time_t today;
313 struct user *usr;
314
315 if (!today)
316 time(&today);
317
318 usr = user(uid);
319 usr->count++;
320 usr->space += blks;
321
322 if (today - act > 90L * 24L * 60L * 60L)
323 usr->spc90 += blks;
324 if (today - act > 60L * 24L * 60L * 60L)
325 usr->spc60 += blks;
326 if (today - act > 30L * 24L * 60L * 60L)
327 usr->spc30 += blks;
328 }
329
330 #ifdef COMPAT
331 #define FSZCNT 500
332 #else
333 #define FSZCNT 512
334 #endif
335 struct fsizes {
336 struct fsizes *fsz_next;
337 daddr_t fsz_first, fsz_last;
338 ino_t fsz_count[FSZCNT];
339 daddr_t fsz_sz[FSZCNT];
340 } *fsizes;
341
342 static void
343 initfsizes()
344 {
345 struct fsizes *fp;
346 int i;
347
348 for (fp = fsizes; fp; fp = fp->fsz_next) {
349 for (i = FSZCNT; --i >= 0;) {
350 fp->fsz_count[i] = 0;
351 fp->fsz_sz[i] = 0;
352 }
353 }
354 }
355
356 static void
357 dofsizes(fd, super, name)
358 int fd;
359 struct fs *super;
360 char *name;
361 {
362 ino_t inode, maxino;
363 union dinode *dp;
364 daddr_t sz, ksz;
365 struct fsizes *fp, **fsp;
366 int i;
367
368 maxino = super->fs_ncg * super->fs_ipg - 1;
369 #ifdef COMPAT
370 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
371 errx(1, "alloc fsize structure");
372 #endif /* COMPAT */
373 for (inode = 0; inode < maxino; inode++) {
374 errno = 0;
375 if ((dp = get_inode(fd, super, inode))
376 #ifdef COMPAT
377 && ((DIP(super, dp, mode) & IFMT) == IFREG
378 || (DIP(dp, mode) & IFMT) == IFDIR)
379 #else /* COMPAT */
380 && !isfree(super, dp)
381 #endif /* COMPAT */
382 ) {
383 sz = estimate ? virtualblocks(super, dp) :
384 actualblocks(super, dp);
385 #ifdef COMPAT
386 if (sz >= FSZCNT) {
387 fsizes->fsz_count[FSZCNT-1]++;
388 fsizes->fsz_sz[FSZCNT-1] += sz;
389 } else {
390 fsizes->fsz_count[sz]++;
391 fsizes->fsz_sz[sz] += sz;
392 }
393 #else /* COMPAT */
394 ksz = SIZE(sz);
395 for (fsp = &fsizes; (fp = *fsp) != NULL;
396 fsp = &fp->fsz_next) {
397 if (ksz < fp->fsz_last)
398 break;
399 }
400 if (!fp || ksz < fp->fsz_first) {
401 if (!(fp = (struct fsizes *)
402 malloc(sizeof(struct fsizes))))
403 errx(1, "alloc fsize structure");
404 fp->fsz_next = *fsp;
405 *fsp = fp;
406 fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
407 fp->fsz_last = fp->fsz_first + FSZCNT;
408 for (i = FSZCNT; --i >= 0;) {
409 fp->fsz_count[i] = 0;
410 fp->fsz_sz[i] = 0;
411 }
412 }
413 fp->fsz_count[ksz % FSZCNT]++;
414 fp->fsz_sz[ksz % FSZCNT] += sz;
415 #endif /* COMPAT */
416 } else if (errno)
417 errx(1, "%s", name);
418 }
419 sz = 0;
420 for (fp = fsizes; fp; fp = fp->fsz_next) {
421 for (i = 0; i < FSZCNT; i++) {
422 if (fp->fsz_count[i])
423 printf("%ld\t%ld\t%lld\n",
424 (long)(fp->fsz_first + i),
425 (long)fp->fsz_count[i],
426 SIZE(sz += fp->fsz_sz[i]));
427 }
428 }
429 }
430
431 static void
432 douser(fd, super, name)
433 int fd;
434 struct fs *super;
435 char *name;
436 {
437 ino_t inode, maxino;
438 struct user *usr, *usrs;
439 union dinode *dp;
440 int n;
441
442 maxino = super->fs_ncg * super->fs_ipg - 1;
443 for (inode = 0; inode < maxino; inode++) {
444 errno = 0;
445 if ((dp = get_inode(fd, super, inode))
446 && !isfree(super, dp))
447 uses(DIP(super, dp, uid),
448 estimate ? virtualblocks(super, dp) :
449 actualblocks(super, dp), DIP(super, dp, atime));
450 else if (errno)
451 errx(1, "%s", name);
452 }
453 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
454 errx(1, "allocate users");
455 memmove(usrs, users, nusers * sizeof(struct user));
456 sortusers(usrs);
457 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
458 printf("%5lld", SIZE(usr->space));
459 if (count)
460 printf("\t%5ld", usr->count);
461 printf("\t%-8s", usr->name);
462 if (unused)
463 printf("\t%5lld\t%5lld\t%5lld",
464 SIZE(usr->spc30), SIZE(usr->spc60),
465 SIZE(usr->spc90));
466 printf("\n");
467 }
468 free(usrs);
469 }
470
471 static void
472 donames(fd, super, name)
473 int fd;
474 struct fs *super;
475 char *name;
476 {
477 int c;
478 ino_t inode, inode1;
479 ino_t maxino;
480 union dinode *dp;
481
482 maxino = super->fs_ncg * super->fs_ipg - 1;
483 /* first skip the name of the filesystem */
484 while ((c = getchar()) != EOF && (c < '0' || c > '9'))
485 while ((c = getchar()) != EOF && c != '\n');
486 ungetc(c, stdin);
487 inode1 = -1;
488 while (scanf("%d", &inode) == 1) {
489 if (inode < 0 || inode > maxino) {
490 #ifndef COMPAT
491 warnx("invalid inode %d", inode);
492 #endif
493 return;
494 }
495 #ifdef COMPAT
496 if (inode < inode1)
497 continue;
498 #endif
499 errno = 0;
500 if ((dp = get_inode(fd, super, inode))
501 && !isfree(super, dp)) {
502 printf("%s\t", user(DIP(super, dp, uid))->name);
503 /* now skip whitespace */
504 while ((c = getchar()) == ' ' || c == '\t');
505 /* and print out the remainder of the input line */
506 while (c != EOF && c != '\n') {
507 putchar(c);
508 c = getchar();
509 }
510 putchar('\n');
511 inode1 = inode;
512 } else {
513 if (errno)
514 errx(1, "%s", name);
515 /* skip this line */
516 while ((c = getchar()) != EOF && c != '\n');
517 }
518 if (c == EOF)
519 break;
520 }
521 }
522
523 static void
524 usage()
525 {
526 #ifdef COMPAT
527 fprintf(stderr, "Usage: quot [-nfcvha] [filesystem ...]\n");
528 #else /* COMPAT */
529 fprintf(stderr, "Usage: quot [ -acfhknv ] [ filesystem ... ]\n");
530 #endif /* COMPAT */
531 exit(1);
532 }
533
534 #define max(a,b) MAX((a),(b))
535 /*
536 * Sanity checks for old file systems.
537 * Stolen from <sys/lib/libsa/ufs.c>
538 */
539 static void
540 ffs_oldfscompat(fs)
541 struct fs *fs;
542 {
543 int i;
544
545 if (fs->fs_old_inodefmt < FS_44INODEFMT) {
546 quad_t sizepb = fs->fs_bsize;
547
548 fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;
549 for (i = 0; i < NIADDR; i++) {
550 sizepb *= NINDIR(fs);
551 fs->fs_maxfilesize += sizepb;
552 }
553 fs->fs_qbmask = ~fs->fs_bmask;
554 fs->fs_qfmask = ~fs->fs_fmask;
555 }
556 }
557
558 /*
559 * Possible superblock locations ordered from most to least likely.
560 */
561 static int sblock_try[] = SBLOCKSEARCH;
562 static char superblock[SBLOCKSIZE];
563
564
565 void
566 quot(name, mp)
567 char *name, *mp;
568 {
569 int fd, i;
570 struct fs *fs;
571
572 get_inode(-1, 0, 0); /* flush cache */
573 inituser();
574 initfsizes();
575 if ((fd = open(name, 0)) < 0) {
576 warn("%s", name);
577 return;
578 }
579
580 for (i = 0; sblock_try[i] != -1; i++) {
581 if (lseek(fd, sblock_try[i], 0) != sblock_try[i])
582 continue;
583 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE)
584 continue;
585 fs = (struct fs *)superblock;
586
587 if ((fs->fs_magic == FS_UFS1_MAGIC ||
588 (fs->fs_magic == FS_UFS2_MAGIC &&
589 fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) &&
590 fs->fs_bsize <= MAXBSIZE &&
591 fs->fs_bsize >= sizeof(struct fs))
592 break;
593 }
594 if (sblock_try[i] == -1) {
595 warnx("%s: not a BSD filesystem", name);
596 close(fd);
597 return;
598 }
599 ffs_oldfscompat((struct fs *)superblock);
600 printf("%s:", name);
601 if (mp)
602 printf(" (%s)", mp);
603 putchar('\n');
604 (*func)(fd, fs, name);
605 close(fd);
606 }
607
608 int
609 main(argc, argv)
610 int argc;
611 char **argv;
612 {
613 char all = 0;
614 struct statfs *mp;
615 char dev[MNAMELEN + 1];
616 char *nm;
617 int cnt;
618
619 func = douser;
620 #ifndef COMPAT
621 header = getbsize(&headerlen, &blocksize);
622 #endif
623 while (--argc > 0 && **++argv == '-') {
624 while (*++*argv) {
625 switch (**argv) {
626 case 'n':
627 func = donames;
628 break;
629 case 'c':
630 func = dofsizes;
631 break;
632 case 'a':
633 all = 1;
634 break;
635 case 'f':
636 count = 1;
637 break;
638 case 'h':
639 estimate = 1;
640 break;
641 #ifndef COMPAT
642 case 'k':
643 blocksize = 1024;
644 break;
645 #endif /* COMPAT */
646 case 'v':
647 unused = 1;
648 break;
649 default:
650 usage();
651 }
652 }
653 }
654 if (all) {
655 cnt = getmntinfo(&mp, MNT_NOWAIT);
656 for (; --cnt >= 0; mp++) {
657 if (!strncmp(mp->f_fstypename, MOUNT_FFS, MFSNAMELEN)) {
658 if ((nm =
659 strrchr(mp->f_mntfromname, '/')) != NULL) {
660 sprintf(dev, "/dev/r%s", nm + 1);
661 nm = dev;
662 } else
663 nm = mp->f_mntfromname;
664 quot(nm, mp->f_mntonname);
665 }
666 }
667 }
668 while (--argc >= 0)
669 quot(*argv++, 0);
670 return 0;
671 }
672