1 /* $NetBSD: quot.c,v 1.34 2016/07/28 08:24:58 martin 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.34 2016/07/28 08:24:58 martin 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)(int, struct fs *, const 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(const void *, const void *); 91 static void dofsizes(int, struct fs *, const char *); 92 static void donames(int, struct fs *, const char *); 93 static void douser(int, struct fs *, const char *); 94 static union dinode *get_inode(int, struct fs *, ino_t); 95 static void ffs_oldfscompat(struct fs *); 96 static void initfsizes(void); 97 static void inituser(void); 98 static int isfree(struct fs *, union dinode *); 99 static void quot(const char *, const char *); 100 static void usage(void) __attribute__((__noreturn__)); 101 static struct user *user(uid_t); 102 static void uses(uid_t, daddr_t, time_t); 103 static void usrrehash(void); 104 static int virtualblocks(struct fs *, union dinode *); 105 106 107 static union dinode * 108 get_inode(int fd, struct fs *super, ino_t ino) 109 { 110 static char *ipbuf; 111 static ino_t last; 112 113 if (fd < 0) { /* flush cache */ 114 if (ipbuf) { 115 free(ipbuf); 116 ipbuf = NULL; 117 } 118 return 0; 119 } 120 121 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 122 if (!ipbuf 123 && !(ipbuf = malloc(INOSZ(super)))) 124 errx(1, "allocate inodes"); 125 last = (ino / INOCNT(super)) * INOCNT(super); 126 if (lseek(fd, 127 (off_t)ino_to_fsba(super, last) << super->fs_fshift, 128 0) < 0 || 129 read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 130 errx(1, "read inodes"); 131 } 132 133 if (super->fs_magic == FS_UFS1_MAGIC) 134 return ((union dinode *) 135 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 136 return ((union dinode *) 137 &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]); 138 } 139 140 #ifdef COMPAT 141 #define actualblocks(fs, dp) (int)(DIP(fs, dp, blocks) / 2) 142 #else 143 #define actualblocks(fs, dp) (int)(DIP(fs, dp, blocks)) 144 #endif 145 146 static int 147 virtualblocks(struct fs *super, union dinode *dp) 148 { 149 off_t nblk, sz; 150 151 sz = DIP(super, dp, size); 152 #ifdef COMPAT 153 if (ffs_lblkno(super, sz) >= UFS_NDADDR) { 154 nblk = ffs_blkroundup(super, sz); 155 if (sz == nblk) 156 nblk += super->fs_bsize; 157 } 158 159 return sz / 1024; 160 #else /* COMPAT */ 161 162 if (ffs_lblkno(super, sz) >= UFS_NDADDR) { 163 nblk = ffs_blkroundup(super, sz); 164 sz = ffs_lblkno(super, nblk); 165 sz = howmany(sz - UFS_NDADDR, FFS_NINDIR(super)); 166 while (sz > 0) { 167 nblk += sz * super->fs_bsize; 168 /* One block on this level is in the inode itself */ 169 sz = howmany(sz - 1, FFS_NINDIR(super)); 170 } 171 } else 172 nblk = ffs_fragroundup(super, sz); 173 174 return nblk / DEV_BSIZE; 175 #endif /* COMPAT */ 176 } 177 178 static int 179 isfree(fs, dp) 180 struct fs *fs; 181 union dinode *dp; 182 { 183 #ifdef COMPAT 184 return (DIP(fs, dp, mode) & IFMT) == 0; 185 #else /* COMPAT */ 186 switch (DIP(fs, dp, mode) & IFMT) { 187 case IFIFO: 188 case IFLNK: /* should check FASTSYMLINK? */ 189 case IFDIR: 190 case IFREG: 191 return 0; 192 default: 193 return 1; 194 } 195 #endif 196 } 197 198 static struct user { 199 uid_t uid; 200 char *name; 201 daddr_t space; 202 long count; 203 daddr_t spc30; 204 daddr_t spc60; 205 daddr_t spc90; 206 } *users; 207 static int nusers; 208 209 static void 210 inituser(void) 211 { 212 int i; 213 struct user *usr; 214 215 if (!nusers) { 216 nusers = 8; 217 if (!(users = calloc(nusers, sizeof(*users)))) 218 err(1, "allocate users"); 219 } else { 220 for (usr = users, i = nusers; --i >= 0; usr++) { 221 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 222 usr->count = 0; 223 } 224 } 225 } 226 227 static void 228 usrrehash(void) 229 { 230 int i; 231 struct user *usr, *usrn; 232 struct user *svusr; 233 234 svusr = users; 235 nusers <<= 1; 236 if (!(users = calloc(nusers, sizeof(*users)))) 237 err(1, "allocate users"); 238 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 239 for (usrn = users + (usr->uid&(nusers - 1)); 240 usrn->name; 241 usrn--) { 242 if (usrn <= users) 243 usrn = users + nusers; 244 } 245 *usrn = *usr; 246 } 247 } 248 249 static struct user * 250 user(uid_t uid) 251 { 252 struct user *usr; 253 int i; 254 struct passwd *pwd; 255 256 for (;;) { 257 for (usr = users + (uid & (nusers - 1)), i = nusers; 258 --i >= 0; 259 usr--) { 260 if (!usr->name) { 261 usr->uid = uid; 262 263 if (!(pwd = getpwuid(uid))) 264 asprintf(&usr->name, "#%u", uid); 265 else 266 asprintf(&usr->name, "%s", 267 pwd->pw_name); 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 ((const struct user *)u2)->space - ((const 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(int fd, struct fs *super, const char *name) 343 { 344 ino_t inode, maxino; 345 union dinode *dp; 346 daddr_t sz, ksz; 347 struct fsizes *fp, **fsp; 348 int i; 349 350 maxino = super->fs_ncg * super->fs_ipg - 1; 351 #ifdef COMPAT 352 if (!(fsizes = malloc(sizeof(*fsizes)))) 353 err(1, "alloc fsize structure"); 354 #endif /* COMPAT */ 355 for (inode = 0; inode < maxino; inode++) { 356 errno = 0; 357 if ((dp = get_inode(fd, super, inode)) 358 #ifdef COMPAT 359 && ((DIP(super, dp, mode) & IFMT) == IFREG 360 || (DIP(dp, mode) & IFMT) == IFDIR) 361 #else /* COMPAT */ 362 && !isfree(super, dp) 363 #endif /* COMPAT */ 364 ) { 365 sz = estimate ? virtualblocks(super, dp) : 366 actualblocks(super, dp); 367 #ifdef COMPAT 368 if (sz >= FSZCNT) { 369 fsizes->fsz_count[FSZCNT-1]++; 370 fsizes->fsz_sz[FSZCNT-1] += sz; 371 } else { 372 fsizes->fsz_count[sz]++; 373 fsizes->fsz_sz[sz] += sz; 374 } 375 #else /* COMPAT */ 376 ksz = SIZE(sz); 377 for (fsp = &fsizes; (fp = *fsp) != NULL; 378 fsp = &fp->fsz_next) { 379 if (ksz < fp->fsz_last) 380 break; 381 } 382 if (!fp || ksz < fp->fsz_first) { 383 if (!(fp = malloc(sizeof(*fp)))) 384 err(1, "alloc fsize structure"); 385 fp->fsz_next = *fsp; 386 *fsp = fp; 387 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 388 fp->fsz_last = fp->fsz_first + FSZCNT; 389 for (i = FSZCNT; --i >= 0;) { 390 fp->fsz_count[i] = 0; 391 fp->fsz_sz[i] = 0; 392 } 393 } 394 fp->fsz_count[ksz % FSZCNT]++; 395 fp->fsz_sz[ksz % FSZCNT] += sz; 396 #endif /* COMPAT */ 397 } else if (errno) 398 errx(1, "%s", name); 399 } 400 sz = 0; 401 for (fp = fsizes; fp; fp = fp->fsz_next) { 402 for (i = 0; i < FSZCNT; i++) { 403 if (fp->fsz_count[i]) 404 printf("%ld\t%ld\t%lld\n", 405 (long)(fp->fsz_first + i), 406 (long)fp->fsz_count[i], 407 SIZE(sz += fp->fsz_sz[i])); 408 } 409 } 410 } 411 412 static void 413 douser(int fd, struct fs *super, const char *name) 414 { 415 ino_t inode, maxino; 416 struct user *usr, *usrs; 417 union dinode *dp; 418 int n; 419 420 maxino = super->fs_ncg * super->fs_ipg - 1; 421 for (inode = 0; inode < maxino; inode++) { 422 errno = 0; 423 if ((dp = get_inode(fd, super, inode)) 424 && !isfree(super, dp)) 425 uses(DIP(super, dp, uid), 426 estimate ? virtualblocks(super, dp) : 427 actualblocks(super, dp), DIP(super, dp, atime)); 428 else if (errno) 429 errx(1, "%s", name); 430 } 431 if (!(usrs = calloc(nusers, sizeof(*usrs)))) 432 errx(1, "allocate users"); 433 memmove(usrs, users, nusers * sizeof(*usrs)); 434 sortusers(usrs); 435 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 436 printf("%5lld", SIZE(usr->space)); 437 if (count) 438 printf("\t%5ld", usr->count); 439 printf("\t%-8s", usr->name); 440 if (unused) 441 printf("\t%5lld\t%5lld\t%5lld", 442 SIZE(usr->spc30), SIZE(usr->spc60), 443 SIZE(usr->spc90)); 444 printf("\n"); 445 } 446 free(usrs); 447 } 448 449 static void 450 donames(int fd, struct fs *super, const char *name) 451 { 452 int c; 453 ino_t inode; 454 #ifdef COMPAT 455 ino_t inode1 = -1; 456 #endif 457 ino_t maxino; 458 union dinode *dp; 459 460 maxino = super->fs_ncg * super->fs_ipg - 1; 461 /* first skip the name of the filesystem */ 462 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 463 while ((c = getchar()) != EOF && c != '\n'); 464 ungetc(c, stdin); 465 while (scanf("%" SCNu64, &inode) == 1) { 466 if (inode > maxino) { 467 #ifndef COMPAT 468 warnx("invalid inode %" PRIu64, inode); 469 #endif 470 return; 471 } 472 #ifdef COMPAT 473 if (inode < inode1) 474 continue; 475 #endif 476 errno = 0; 477 if ((dp = get_inode(fd, super, inode)) 478 && !isfree(super, dp)) { 479 printf("%s\t", user(DIP(super, dp, uid))->name); 480 /* now skip whitespace */ 481 while ((c = getchar()) == ' ' || c == '\t'); 482 /* and print out the remainder of the input line */ 483 while (c != EOF && c != '\n') { 484 putchar(c); 485 c = getchar(); 486 } 487 putchar('\n'); 488 #ifdef COMPAT 489 inode1 = inode; 490 #endif 491 } else { 492 if (errno) 493 errx(1, "%s", name); 494 /* skip this line */ 495 while ((c = getchar()) != EOF && c != '\n') 496 continue; 497 } 498 if (c == EOF) 499 break; 500 } 501 } 502 503 static void 504 usage(void) 505 { 506 const char *p = getprogname(); 507 #ifdef COMPAT 508 fprintf(stderr, "Usage: %s [-nfcvha] [<filesystem> ...]\n", p); 509 #else /* COMPAT */ 510 fprintf(stderr, "Usage: %s [ -acfhknv ] [<filesystem> ... ]\n", p); 511 #endif /* COMPAT */ 512 exit(1); 513 } 514 515 /* 516 * Sanity checks for old file systems. 517 * Stolen from <sys/lib/libsa/ufs.c> 518 */ 519 static void 520 ffs_oldfscompat(struct fs *fs) 521 { 522 int i; 523 524 if (fs->fs_magic == FS_UFS1_MAGIC && 525 fs->fs_old_inodefmt < FS_44INODEFMT) { 526 quad_t sizepb = fs->fs_bsize; 527 528 fs->fs_maxfilesize = fs->fs_bsize * UFS_NDADDR - 1; 529 for (i = 0; i < UFS_NIADDR; i++) { 530 sizepb *= FFS_NINDIR(fs); 531 fs->fs_maxfilesize += sizepb; 532 } 533 fs->fs_qbmask = ~fs->fs_bmask; 534 fs->fs_qfmask = ~fs->fs_fmask; 535 } 536 } 537 538 /* 539 * Possible superblock locations ordered from most to least likely. 540 */ 541 static int sblock_try[] = SBLOCKSEARCH; 542 static char superblock[SBLOCKSIZE]; 543 544 545 static void 546 quot(const char *name, const char *mp) 547 { 548 int fd, i; 549 struct fs *fs; 550 int sbloc; 551 552 get_inode(-1, 0, 0); /* flush cache */ 553 inituser(); 554 initfsizes(); 555 if ((fd = open(name, 0)) < 0) { 556 warn("%s", name); 557 return; 558 } 559 560 for (i = 0; ; i++) { 561 sbloc = sblock_try[i]; 562 if (sbloc == -1) { 563 warnx("%s: not a BSD filesystem", name); 564 close(fd); 565 return; 566 } 567 if (pread(fd, superblock, SBLOCKSIZE, sbloc) != SBLOCKSIZE) 568 continue; 569 fs = (struct fs *)superblock; 570 571 if (fs->fs_magic != FS_UFS1_MAGIC && 572 fs->fs_magic != FS_UFS2_MAGIC) 573 continue; 574 575 if (fs->fs_magic == FS_UFS2_MAGIC 576 || fs->fs_old_flags & FS_FLAGS_UPDATED) { 577 /* Not the main superblock */ 578 if (fs->fs_sblockloc != sbloc) 579 continue; 580 } else { 581 /* might be a first alt. id blocksize 64k */ 582 if (sbloc == SBLOCK_UFS2) 583 continue; 584 } 585 586 if (fs->fs_bsize > MAXBSIZE || 587 (size_t)fs->fs_bsize < sizeof(struct fs)) 588 continue; 589 break; 590 } 591 592 ffs_oldfscompat((struct fs *)superblock); 593 printf("%s:", name); 594 if (mp) 595 printf(" (%s)", mp); 596 putchar('\n'); 597 (*func)(fd, fs, name); 598 close(fd); 599 } 600 601 int 602 main(int argc, char **argv) 603 { 604 char all = 0; 605 struct statvfs *mp; 606 char dev[MNAMELEN + 1]; 607 char *nm; 608 int cnt; 609 610 func = douser; 611 #ifndef COMPAT 612 header = getbsize(NULL, &blocksize); 613 #endif 614 while (--argc > 0 && **++argv == '-') { 615 while (*++*argv) { 616 switch (**argv) { 617 case 'n': 618 func = donames; 619 break; 620 case 'c': 621 func = dofsizes; 622 break; 623 case 'a': 624 all = 1; 625 break; 626 case 'f': 627 count = 1; 628 break; 629 case 'h': 630 estimate = 1; 631 break; 632 #ifndef COMPAT 633 case 'k': 634 blocksize = 1024; 635 break; 636 #endif /* COMPAT */ 637 case 'v': 638 unused = 1; 639 break; 640 default: 641 usage(); 642 } 643 } 644 } 645 if (all) { 646 cnt = getmntinfo(&mp, MNT_NOWAIT); 647 for (; --cnt >= 0; mp++) { 648 if (!strncmp(mp->f_fstypename, MOUNT_FFS, 649 sizeof(mp->f_fstypename))) { 650 if ((nm = 651 strrchr(mp->f_mntfromname, '/')) != NULL) { 652 snprintf(dev, sizeof(dev), "/dev/r%s", 653 nm + 1); 654 nm = dev; 655 } else 656 nm = mp->f_mntfromname; 657 quot(nm, mp->f_mntonname); 658 } 659 } 660 } 661 while (--argc >= 0) 662 quot(*argv++, 0); 663 return 0; 664 } 665