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