1 /* $NetBSD: quot.c,v 1.28 2009/04/18 08:17:23 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.28 2009/04/18 08:17:23 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/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 62 /* 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). 69 */ 70 #ifdef COMPAT 71 #define SIZE(n) ((long long)(n)) 72 #else 73 #define SIZE(n) howmany((long long)(n) * DEV_BSIZE, (long long)blocksize) 74 #endif 75 76 #define INOCNT(fs) ((fs)->fs_ipg) 77 #define INOSZ(fs) \ 78 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 79 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 80 81 union dinode { 82 struct ufs1_dinode dp1; 83 struct ufs2_dinode dp2; 84 }; 85 #define DIP(fs, dp, field) \ 86 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 87 (dp)->dp1.di_##field : (dp)->dp2.di_##field) 88 89 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 *)); 106 107 108 static union dinode * 109 get_inode(fd, super, ino) 110 int fd; 111 struct fs *super; 112 ino_t ino; 113 { 114 static char *ipbuf; 115 static ino_t last; 116 117 if (fd < 0) { /* flush cache */ 118 if (ipbuf) { 119 free(ipbuf); 120 ipbuf = NULL; 121 } 122 return 0; 123 } 124 125 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 126 if (!ipbuf 127 && !(ipbuf = malloc(INOSZ(super)))) 128 errx(1, "allocate inodes"); 129 last = (ino / INOCNT(super)) * INOCNT(super); 130 if (lseek(fd, 131 (off_t)ino_to_fsba(super, last) << super->fs_fshift, 132 0) < 0 || 133 read(fd, ipbuf, INOSZ(super)) != INOSZ(super)) 134 errx(1, "read inodes"); 135 } 136 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)]); 142 } 143 144 #ifdef COMPAT 145 #define actualblocks(fs, dp) (DIP(fs, dp, blocks) / 2) 146 #else 147 #define actualblocks(fs, dp) (DIP(fs, dp, blocks)) 148 #endif 149 150 static int 151 virtualblocks(super, dp) 152 struct fs *super; 153 union dinode *dp; 154 { 155 off_t nblk, sz; 156 157 sz = DIP(super, dp, size); 158 #ifdef COMPAT 159 if (lblkno(super, sz) >= NDADDR) { 160 nblk = blkroundup(super, sz); 161 if (sz == nblk) 162 nblk += super->fs_bsize; 163 } 164 165 return sz / 1024; 166 #else /* COMPAT */ 167 168 if (lblkno(super, sz) >= NDADDR) { 169 nblk = blkroundup(super, sz); 170 sz = lblkno(super, nblk); 171 sz = howmany(sz - NDADDR, NINDIR(super)); 172 while (sz > 0) { 173 nblk += sz * super->fs_bsize; 174 /* One block on this level is in the inode itself */ 175 sz = howmany(sz - 1, NINDIR(super)); 176 } 177 } else 178 nblk = fragroundup(super, sz); 179 180 return nblk / DEV_BSIZE; 181 #endif /* COMPAT */ 182 } 183 184 static int 185 isfree(fs, dp) 186 struct fs *fs; 187 union dinode *dp; 188 { 189 #ifdef COMPAT 190 return (DIP(fs, dp, mode) & IFMT) == 0; 191 #else /* COMPAT */ 192 switch (DIP(fs, dp, mode) & IFMT) { 193 case IFIFO: 194 case IFLNK: /* should check FASTSYMLINK? */ 195 case IFDIR: 196 case IFREG: 197 return 0; 198 default: 199 return 1; 200 } 201 #endif 202 } 203 204 static struct user { 205 uid_t uid; 206 char *name; 207 daddr_t space; 208 long count; 209 daddr_t spc30; 210 daddr_t spc60; 211 daddr_t spc90; 212 } *users; 213 static int nusers; 214 215 static void 216 inituser() 217 { 218 int i; 219 struct user *usr; 220 221 if (!nusers) { 222 nusers = 8; 223 if (!(users = 224 (struct user *)calloc(nusers, sizeof(struct user)))) 225 errx(1, "allocate users"); 226 } else { 227 for (usr = users, i = nusers; --i >= 0; usr++) { 228 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 229 usr->count = 0; 230 } 231 } 232 } 233 234 static void 235 usrrehash() 236 { 237 int i; 238 struct user *usr, *usrn; 239 struct user *svusr; 240 241 svusr = users; 242 nusers <<= 1; 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)); 247 usrn->name; 248 usrn--) { 249 if (usrn <= users) 250 usrn = users + nusers; 251 } 252 *usrn = *usr; 253 } 254 } 255 256 static struct user * 257 user(uid) 258 uid_t uid; 259 { 260 struct user *usr; 261 int i; 262 struct passwd *pwd; 263 264 while (1) { 265 for (usr = users + (uid&(nusers - 1)), i = nusers; 266 --i >= 0; 267 usr--) { 268 if (!usr->name) { 269 usr->uid = uid; 270 271 if (!(pwd = getpwuid(uid))) { 272 if ((usr->name = 273 (char *)malloc(7)) != NULL) 274 sprintf(usr->name, "#%d", uid); 275 } else { 276 if ((usr->name = 277 (char *)malloc( 278 strlen(pwd->pw_name) + 1)) 279 != NULL) 280 strcpy(usr->name, pwd->pw_name); 281 } 282 if (!usr->name) 283 errx(1, "allocate users"); 284 return usr; 285 } else if (usr->uid == uid) 286 return usr; 287 288 if (usr <= users) 289 usr = users + nusers; 290 } 291 usrrehash(); 292 } 293 } 294 295 static int 296 cmpusers(u1, u2) 297 const void *u1, *u2; 298 { 299 return ((const struct user *)u2)->space - ((const struct user *)u1)->space; 300 } 301 302 #define sortusers(users) (qsort((users), nusers, sizeof(struct user), \ 303 cmpusers)) 304 305 static void 306 uses(uid, blks, act) 307 uid_t uid; 308 daddr_t blks; 309 time_t act; 310 { 311 static time_t today; 312 struct user *usr; 313 314 if (!today) 315 time(&today); 316 317 usr = user(uid); 318 usr->count++; 319 usr->space += blks; 320 321 if (today - act > 90L * 24L * 60L * 60L) 322 usr->spc90 += blks; 323 if (today - act > 60L * 24L * 60L * 60L) 324 usr->spc60 += blks; 325 if (today - act > 30L * 24L * 60L * 60L) 326 usr->spc30 += blks; 327 } 328 329 #ifdef COMPAT 330 #define FSZCNT 500 331 #else 332 #define FSZCNT 512 333 #endif 334 struct fsizes { 335 struct fsizes *fsz_next; 336 daddr_t fsz_first, fsz_last; 337 ino_t fsz_count[FSZCNT]; 338 daddr_t fsz_sz[FSZCNT]; 339 } *fsizes; 340 341 static void 342 initfsizes() 343 { 344 struct fsizes *fp; 345 int i; 346 347 for (fp = fsizes; fp; fp = fp->fsz_next) { 348 for (i = FSZCNT; --i >= 0;) { 349 fp->fsz_count[i] = 0; 350 fp->fsz_sz[i] = 0; 351 } 352 } 353 } 354 355 static void 356 dofsizes(fd, super, name) 357 int fd; 358 struct fs *super; 359 char *name; 360 { 361 ino_t inode, maxino; 362 union dinode *dp; 363 daddr_t sz, ksz; 364 struct fsizes *fp, **fsp; 365 int i; 366 367 maxino = super->fs_ncg * super->fs_ipg - 1; 368 #ifdef COMPAT 369 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 370 errx(1, "alloc fsize structure"); 371 #endif /* COMPAT */ 372 for (inode = 0; inode < maxino; inode++) { 373 errno = 0; 374 if ((dp = get_inode(fd, super, inode)) 375 #ifdef COMPAT 376 && ((DIP(super, dp, mode) & IFMT) == IFREG 377 || (DIP(dp, mode) & IFMT) == IFDIR) 378 #else /* COMPAT */ 379 && !isfree(super, dp) 380 #endif /* COMPAT */ 381 ) { 382 sz = estimate ? virtualblocks(super, dp) : 383 actualblocks(super, dp); 384 #ifdef COMPAT 385 if (sz >= FSZCNT) { 386 fsizes->fsz_count[FSZCNT-1]++; 387 fsizes->fsz_sz[FSZCNT-1] += sz; 388 } else { 389 fsizes->fsz_count[sz]++; 390 fsizes->fsz_sz[sz] += sz; 391 } 392 #else /* COMPAT */ 393 ksz = SIZE(sz); 394 for (fsp = &fsizes; (fp = *fsp) != NULL; 395 fsp = &fp->fsz_next) { 396 if (ksz < fp->fsz_last) 397 break; 398 } 399 if (!fp || ksz < fp->fsz_first) { 400 if (!(fp = (struct fsizes *) 401 malloc(sizeof(struct fsizes)))) 402 errx(1, "alloc fsize structure"); 403 fp->fsz_next = *fsp; 404 *fsp = fp; 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; 409 fp->fsz_sz[i] = 0; 410 } 411 } 412 fp->fsz_count[ksz % FSZCNT]++; 413 fp->fsz_sz[ksz % FSZCNT] += sz; 414 #endif /* COMPAT */ 415 } else if (errno) 416 errx(1, "%s", name); 417 } 418 sz = 0; 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])); 426 } 427 } 428 } 429 430 static void 431 douser(fd, super, name) 432 int fd; 433 struct fs *super; 434 char *name; 435 { 436 ino_t inode, maxino; 437 struct user *usr, *usrs; 438 union dinode *dp; 439 int n; 440 441 maxino = super->fs_ncg * super->fs_ipg - 1; 442 for (inode = 0; inode < maxino; inode++) { 443 errno = 0; 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)); 449 else if (errno) 450 errx(1, "%s", name); 451 } 452 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 453 errx(1, "allocate users"); 454 memmove(usrs, users, nusers * sizeof(struct user)); 455 sortusers(usrs); 456 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 457 printf("%5lld", SIZE(usr->space)); 458 if (count) 459 printf("\t%5ld", usr->count); 460 printf("\t%-8s", usr->name); 461 if (unused) 462 printf("\t%5lld\t%5lld\t%5lld", 463 SIZE(usr->spc30), SIZE(usr->spc60), 464 SIZE(usr->spc90)); 465 printf("\n"); 466 } 467 free(usrs); 468 } 469 470 static void 471 donames(fd, super, name) 472 int fd; 473 struct fs *super; 474 char *name; 475 { 476 int c; 477 ino_t inode, inode1; 478 ino_t maxino; 479 union dinode *dp; 480 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'); 485 ungetc(c, stdin); 486 inode1 = -1; 487 while (scanf("%" SCNu64, &inode) == 1) { 488 if (inode > maxino) { 489 #ifndef COMPAT 490 warnx("invalid inode %" PRIu64, inode); 491 #endif 492 return; 493 } 494 #ifdef COMPAT 495 if (inode < inode1) 496 continue; 497 #endif 498 errno = 0; 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') { 506 putchar(c); 507 c = getchar(); 508 } 509 putchar('\n'); 510 inode1 = inode; 511 } else { 512 if (errno) 513 errx(1, "%s", name); 514 /* skip this line */ 515 while ((c = getchar()) != EOF && c != '\n'); 516 } 517 if (c == EOF) 518 break; 519 } 520 } 521 522 static void 523 usage() 524 { 525 #ifdef COMPAT 526 fprintf(stderr, "usage: quot [-nfcvha] [filesystem ...]\n"); 527 #else /* COMPAT */ 528 fprintf(stderr, "usage: quot [ -acfhknv ] [ filesystem ... ]\n"); 529 #endif /* COMPAT */ 530 exit(1); 531 } 532 533 /* 534 * Sanity checks for old file systems. 535 * Stolen from <sys/lib/libsa/ufs.c> 536 */ 537 static void 538 ffs_oldfscompat(fs) 539 struct fs *fs; 540 { 541 int i; 542 543 if (fs->fs_old_inodefmt < FS_44INODEFMT) { 544 quad_t sizepb = fs->fs_bsize; 545 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; 550 } 551 fs->fs_qbmask = ~fs->fs_bmask; 552 fs->fs_qfmask = ~fs->fs_fmask; 553 } 554 } 555 556 /* 557 * Possible superblock locations ordered from most to least likely. 558 */ 559 static int sblock_try[] = SBLOCKSEARCH; 560 static char superblock[SBLOCKSIZE]; 561 562 563 void 564 quot(name, mp) 565 char *name, *mp; 566 { 567 int fd, i; 568 struct fs *fs; 569 int sbloc; 570 571 get_inode(-1, 0, 0); /* flush cache */ 572 inituser(); 573 initfsizes(); 574 if ((fd = open(name, 0)) < 0) { 575 warn("%s", name); 576 return; 577 } 578 579 for (i = 0; ; i++) { 580 sbloc = sblock_try[i]; 581 if (sbloc == -1) { 582 warnx("%s: not a BSD filesystem", name); 583 close(fd); 584 return; 585 } 586 if (pread(fd, superblock, SBLOCKSIZE, sbloc) != SBLOCKSIZE) 587 continue; 588 fs = (struct fs *)superblock; 589 590 if (fs->fs_magic != FS_UFS1_MAGIC && 591 fs->fs_magic != FS_UFS2_MAGIC) 592 continue; 593 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) 598 continue; 599 } else { 600 /* might be a first alt. id blocksize 64k */ 601 if (sbloc == SBLOCK_UFS2) 602 continue; 603 } 604 605 if (fs->fs_bsize > MAXBSIZE || 606 fs->fs_bsize < sizeof(struct fs)) 607 continue; 608 break; 609 } 610 611 ffs_oldfscompat((struct fs *)superblock); 612 printf("%s:", name); 613 if (mp) 614 printf(" (%s)", mp); 615 putchar('\n'); 616 (*func)(fd, fs, name); 617 close(fd); 618 } 619 620 int 621 main(argc, argv) 622 int argc; 623 char **argv; 624 { 625 char all = 0; 626 struct statvfs *mp; 627 char dev[MNAMELEN + 1]; 628 char *nm; 629 int cnt; 630 631 func = douser; 632 #ifndef COMPAT 633 header = getbsize(NULL, &blocksize); 634 #endif 635 while (--argc > 0 && **++argv == '-') { 636 while (*++*argv) { 637 switch (**argv) { 638 case 'n': 639 func = donames; 640 break; 641 case 'c': 642 func = dofsizes; 643 break; 644 case 'a': 645 all = 1; 646 break; 647 case 'f': 648 count = 1; 649 break; 650 case 'h': 651 estimate = 1; 652 break; 653 #ifndef COMPAT 654 case 'k': 655 blocksize = 1024; 656 break; 657 #endif /* COMPAT */ 658 case 'v': 659 unused = 1; 660 break; 661 default: 662 usage(); 663 } 664 } 665 } 666 if (all) { 667 cnt = getmntinfo(&mp, MNT_NOWAIT); 668 for (; --cnt >= 0; mp++) { 669 if (!strncmp(mp->f_fstypename, MOUNT_FFS, 670 sizeof(mp->f_fstypename))) { 671 if ((nm = 672 strrchr(mp->f_mntfromname, '/')) != NULL) { 673 sprintf(dev, "/dev/r%s", nm + 1); 674 nm = dev; 675 } else 676 nm = mp->f_mntfromname; 677 quot(nm, mp->f_mntonname); 678 } 679 } 680 } 681 while (--argc >= 0) 682 quot(*argv++, 0); 683 return 0; 684 } 685