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