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