1 /* $OpenBSD: quot.c,v 1.28 2015/11/12 22:33:07 deraadt 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/param.h> /* DEV_BSIZE MAXBSIZE */ 35 #include <sys/mount.h> 36 #include <sys/time.h> 37 #include <ufs/ufs/dinode.h> 38 #include <ufs/ffs/fs.h> 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <paths.h> 47 #include <pwd.h> 48 #include <unistd.h> 49 50 /* some flags of what to do: */ 51 static char estimate; 52 static char count; 53 static char unused; 54 static void (*func)(int, struct fs *, char *); 55 static int cmpusers(const void *, const void *); 56 void quot(char *, char *); 57 static long blocksize; 58 static char *header; 59 static int headerlen; 60 61 /* 62 * Original BSD quot doesn't round to number of frags/blocks, 63 * doesn't account for indirection blocks and gets it totally 64 * wrong if the size is a multiple of the blocksize. 65 * The new code always counts the number of DEV_BSIZE byte blocks 66 * instead of the number of kilobytes and converts them to 67 * KByte when done (on request). 68 */ 69 #ifdef COMPAT 70 #define SIZE(n) (n) 71 #else 72 #define SIZE(n) (howmany(((off_t)(n)) * DEV_BSIZE, blocksize)) 73 #endif 74 75 #define INOCNT(fs) ((fs)->fs_ipg) 76 #define INOSZ(fs) (((fs)->fs_magic == FS_UFS1_MAGIC ? \ 77 sizeof(struct ufs1_dinode) : \ 78 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 79 80 union dinode { 81 struct ufs1_dinode dp1; 82 struct ufs2_dinode dp2; 83 }; 84 #define DIP(fs, dp, field) \ 85 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 86 (dp)->dp1.field : (dp)->dp2.field) 87 88 static union dinode * 89 get_inode(int fd, struct fs *super, ino_t ino) 90 { 91 static caddr_t ipbuf; 92 static struct cg *cgp; 93 static ino_t last; 94 static int cg; 95 struct ufs2_dinode *di2; 96 97 if (fd < 0) { /* flush cache */ 98 if (ipbuf) { 99 free(ipbuf); 100 ipbuf = NULL; 101 if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) { 102 free(cgp); 103 cgp = NULL; 104 } 105 } 106 return 0; 107 } 108 109 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 110 if (super->fs_magic == FS_UFS2_MAGIC && 111 (!cgp || cg != ino_to_cg(super, ino))) { 112 cg = ino_to_cg(super, ino); 113 if (!cgp && !(cgp = malloc(super->fs_cgsize))) 114 errx(1, "allocate cg"); 115 if (pread(fd, cgp, super->fs_cgsize, 116 (off_t)cgtod(super, cg) << super->fs_fshift) 117 != super->fs_cgsize) 118 if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) 119 err(1, "read cg"); 120 if (!cg_chkmagic(cgp)) 121 errx(1, "cg has bad magic"); 122 } 123 if (!ipbuf && !(ipbuf = malloc(INOSZ(super)))) 124 err(1, "allocate inodes"); 125 last = (ino / INOCNT(super)) * INOCNT(super); 126 if (lseek(fd, (off_t)ino_to_fsba(super, last) 127 << super->fs_fshift, SEEK_SET) < 0 || 128 read(fd, ipbuf, INOSZ(super)) != INOSZ(super)) { 129 err(1, "read inodes"); 130 } 131 } 132 133 if (super->fs_magic == FS_UFS1_MAGIC) 134 return ((union dinode *) 135 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 136 di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; 137 /* If the inode is unused, it might be unallocated too, so zero it. */ 138 if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) 139 memset(di2, 0, sizeof(*di2)); 140 return ((union dinode *)di2); 141 } 142 143 #ifdef COMPAT 144 #define actualblocks(fs, ip) (DIP(fs, dp, di_blocks) / 2) 145 #else 146 #define actualblocks(fs, ip) DIP(fs, dp, di_blocks) 147 #endif 148 149 static int 150 virtualblocks(struct fs *super, union dinode *dp) 151 { 152 off_t nblk, sz; 153 154 sz = DIP(super, dp, di_size); 155 #ifdef COMPAT 156 if (lblkno(super, sz) >= NDADDR) { 157 nblk = blkroundup(super, sz); 158 if (sz == nblk) 159 nblk += super->fs_bsize; 160 } 161 162 return sz / 1024; 163 #else /* COMPAT */ 164 165 if (lblkno(super, sz) >= NDADDR) { 166 nblk = blkroundup(super, sz); 167 sz = lblkno(super, nblk); 168 sz = howmany(sz - NDADDR, NINDIR(super)); 169 while (sz > 0) { 170 nblk += sz * super->fs_bsize; 171 /* One block on this level is in the inode itself */ 172 sz = howmany(sz - 1, NINDIR(super)); 173 } 174 } else 175 nblk = fragroundup(super, sz); 176 177 return nblk / DEV_BSIZE; 178 #endif /* COMPAT */ 179 } 180 181 static int 182 isfree(struct fs *super, union dinode *dp) 183 { 184 #ifdef COMPAT 185 return (DIP(super, dp, di_mode) & IFMT) == 0; 186 #else /* COMPAT */ 187 switch (DIP(super, dp, di_mode) & IFMT) { 188 case IFIFO: 189 case IFLNK: /* should check FASTSYMLINK? */ 190 case IFDIR: 191 case IFREG: 192 return 0; 193 case IFCHR: 194 case IFBLK: 195 case IFSOCK: 196 case 0: 197 return 1; 198 default: 199 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 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(void) 217 { 218 int i; 219 struct user *usr; 220 221 if (!nusers) { 222 nusers = 8; 223 if (!(users = calloc(nusers, sizeof(struct user)))) { 224 err(1, "allocate users"); 225 } 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(void) 236 { 237 int i; 238 struct user *usr, *usrn; 239 struct user *svusr; 240 241 svusr = users; 242 nusers <<= 1; 243 if (!(users = calloc(nusers, sizeof(struct user)))) 244 err(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_t uid) 258 { 259 int i; 260 struct passwd *pwd; 261 struct user *usr; 262 263 while (1) { 264 for (usr = users + (uid&(nusers - 1)), i = nusers; 265 --i >= 0; 266 usr--) { 267 if (!usr->name) { 268 usr->uid = uid; 269 270 if (!(pwd = getpwuid(uid))) 271 asprintf(&usr->name, "#%u", uid); 272 else 273 usr->name = strdup(pwd->pw_name); 274 if (!usr->name) 275 err(1, "allocate users"); 276 return usr; 277 } else if (usr->uid == uid) 278 return usr; 279 280 if (usr <= users) 281 usr = users + nusers; 282 } 283 usrrehash(); 284 } 285 } 286 287 static int 288 cmpusers(const void *v1, const void *v2) 289 { 290 const struct user *u1 = v1, *u2 = v2; 291 292 return u2->space - u1->space; 293 } 294 295 #define sortusers(users) (qsort((users), nusers, sizeof(struct user), \ 296 cmpusers)) 297 298 static void 299 uses(uid_t uid, daddr_t blks, time_t act) 300 { 301 static time_t today; 302 struct user *usr; 303 304 if (!today) 305 time(&today); 306 307 usr = user(uid); 308 usr->count++; 309 usr->space += blks; 310 311 if (today - act > 90L * 24L * 60L * 60L) 312 usr->spc90 += blks; 313 if (today - act > 60L * 24L * 60L * 60L) 314 usr->spc60 += blks; 315 if (today - act > 30L * 24L * 60L * 60L) 316 usr->spc30 += blks; 317 } 318 319 #ifdef COMPAT 320 #define FSZCNT 500 321 #else 322 #define FSZCNT 512 323 #endif 324 struct fsizes { 325 struct fsizes *fsz_next; 326 daddr_t fsz_first, fsz_last; 327 ino_t fsz_count[FSZCNT]; 328 daddr_t fsz_sz[FSZCNT]; 329 } *fsizes; 330 331 static void 332 initfsizes(void) 333 { 334 struct fsizes *fp; 335 int i; 336 337 for (fp = fsizes; fp; fp = fp->fsz_next) { 338 for (i = FSZCNT; --i >= 0;) { 339 fp->fsz_count[i] = 0; 340 fp->fsz_sz[i] = 0; 341 } 342 } 343 } 344 345 static void 346 dofsizes(int fd, struct fs *super, char *name) 347 { 348 ino_t inode, maxino; 349 union dinode *dp; 350 daddr_t sz, ksz; 351 struct fsizes *fp, **fsp; 352 int i; 353 354 maxino = super->fs_ncg * super->fs_ipg - 1; 355 #ifdef COMPAT 356 if (!(fsizes = malloc(sizeof(struct fsizes)))) 357 err(1, "alloc fsize structure"); 358 #endif /* COMPAT */ 359 for (inode = 0; inode < maxino; inode++) { 360 errno = 0; 361 if ((dp = get_inode(fd, super, inode)) 362 #ifdef COMPAT 363 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 364 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 365 #else /* COMPAT */ 366 && !isfree(super, dp) 367 #endif /* COMPAT */ 368 ) { 369 sz = estimate ? virtualblocks(super, dp) : 370 actualblocks(super, dp); 371 #ifdef COMPAT 372 if (sz >= FSZCNT) { 373 fsizes->fsz_count[FSZCNT-1]++; 374 fsizes->fsz_sz[FSZCNT-1] += sz; 375 } else { 376 fsizes->fsz_count[sz]++; 377 fsizes->fsz_sz[sz] += sz; 378 } 379 #else /* COMPAT */ 380 ksz = SIZE(sz); 381 for (fsp = &fsizes; (fp = *fsp); 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 err(1, "alloc fsize structure"); 389 } 390 fp->fsz_next = *fsp; 391 *fsp = fp; 392 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 393 fp->fsz_last = fp->fsz_first + FSZCNT; 394 for (i = FSZCNT; --i >= 0;) { 395 fp->fsz_count[i] = 0; 396 fp->fsz_sz[i] = 0; 397 } 398 } 399 fp->fsz_count[ksz % FSZCNT]++; 400 fp->fsz_sz[ksz % FSZCNT] += sz; 401 #endif /* COMPAT */ 402 } else if (errno) 403 err(1, "%s", name); 404 } 405 sz = 0; 406 for (fp = fsizes; fp; fp = fp->fsz_next) { 407 for (i = 0; i < FSZCNT; i++) { 408 if (fp->fsz_count[i]) 409 printf("%lld\t%llu\t%lld\n", 410 (long long)fp->fsz_first + i, 411 (unsigned long long)fp->fsz_count[i], 412 SIZE(sz += fp->fsz_sz[i])); 413 } 414 } 415 } 416 417 static void 418 douser(int fd, struct fs *super, char *name) 419 { 420 ino_t inode, maxino; 421 struct user *usr, *usrs; 422 union dinode *dp; 423 int n; 424 425 maxino = super->fs_ncg * super->fs_ipg - 1; 426 for (inode = 0; inode < maxino; inode++) { 427 errno = 0; 428 if ((dp = get_inode(fd,super,inode)) 429 && !isfree(super, dp)) 430 uses(DIP(super, dp, di_uid), 431 estimate ? virtualblocks(super, dp) : 432 actualblocks(super, dp), 433 DIP(super, dp, di_atime)); 434 else if (errno) 435 err(1, "%s", name); 436 } 437 if (!(usrs = calloc(nusers, sizeof(struct user)))) 438 err(1, "allocate users"); 439 memcpy(usrs, users, nusers * sizeof(struct user)); 440 sortusers(usrs); 441 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 442 printf("%14lld", SIZE(usr->space)); 443 if (count) 444 printf("\t%5ld", usr->count); 445 printf("\t%-8s", usr->name); 446 if (unused) 447 printf("\t%14lld\t%14lld\t%14lld", 448 SIZE(usr->spc30), 449 SIZE(usr->spc60), 450 SIZE(usr->spc90)); 451 printf("\n"); 452 } 453 free(usrs); 454 } 455 456 static void 457 donames(int fd, struct fs *super, char *name) 458 { 459 int c; 460 unsigned long long inode; 461 ino_t inode1; 462 ino_t maxino; 463 union dinode *dp; 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("%llu", &inode) == 1) { 472 if (inode < 0 || inode > maxino) { 473 #ifndef COMPAT 474 fprintf(stderr, "invalid inode %llu\n", 475 (unsigned long long)inode); 476 #endif 477 return; 478 } 479 #ifdef COMPAT 480 if (inode < inode1) 481 continue; 482 #endif 483 errno = 0; 484 if ((dp = get_inode(fd, super, inode)) && !isfree(super, dp)) { 485 printf("%s\t", user(DIP(super, dp, 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 err(1, "%s", name); 498 /* skip this line */ 499 while ((c = getchar()) != EOF && c != '\n') 500 ; 501 } 502 if (c == EOF) 503 break; 504 } 505 } 506 507 static void 508 usage(void) 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 /* 519 * Possible superblock locations ordered from most to least likely. 520 */ 521 static int sblock_try[] = SBLOCKSEARCH; 522 static char superblock[SBLOCKSIZE]; 523 524 #define max(a,b) MAX((a),(b)) 525 /* 526 * Sanity checks for old file systems. 527 * Stolen from <sys/lib/libsa/ufs.c> 528 */ 529 static void 530 ffs_oldfscompat(struct fs *fs) 531 { 532 int i; 533 534 fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ 535 fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ 536 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ 537 fs->fs_nrpos = 8; /* XXX */ 538 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ 539 quad_t sizepb = fs->fs_bsize; /* XXX */ 540 /* XXX */ 541 fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */ 542 for (i = 0; i < NIADDR; i++) { /* XXX */ 543 sizepb *= NINDIR(fs); /* XXX */ 544 fs->fs_maxfilesize += sizepb; /* XXX */ 545 } /* XXX */ 546 fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ 547 fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ 548 } /* XXX */ 549 } 550 551 void 552 quot(char *name, char *mp) 553 { 554 int i, fd; 555 struct fs *fs; 556 557 get_inode(-1, NULL, 0); /* flush cache */ 558 inituser(); 559 initfsizes(); 560 /* 561 * XXX this is completely broken. Of course you can't read a 562 * directory, well, not anymore. How to fix this, though... 563 */ 564 if ((fd = open(name, O_RDONLY)) < 0) { 565 warn("%s", name); 566 return; 567 } 568 for (i = 0; sblock_try[i] != -1; i++) { 569 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) { 570 close(fd); 571 return; 572 } 573 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) { 574 close(fd); 575 return; 576 } 577 fs = (struct fs *)superblock; 578 if ((fs->fs_magic == FS_UFS1_MAGIC || 579 (fs->fs_magic == FS_UFS2_MAGIC && 580 fs->fs_sblockloc == sblock_try[i])) && 581 fs->fs_bsize <= MAXBSIZE && 582 fs->fs_bsize >= sizeof(struct fs)) 583 break; 584 } 585 if (sblock_try[i] == -1) { 586 warnx("%s: not a BSD filesystem", name); 587 close(fd); 588 return; 589 } 590 ffs_oldfscompat(fs); 591 printf("%s:", name); 592 if (mp) 593 printf(" (%s)", mp); 594 putchar('\n'); 595 (*func)(fd, fs, name); 596 close(fd); 597 } 598 599 int 600 main(int argc, char *argv[]) 601 { 602 int cnt, all, i; 603 char dev[MNAMELEN], *nm, *mountpoint, *cp; 604 struct statfs *mp; 605 606 all = 0; 607 func = douser; 608 #ifndef COMPAT 609 header = getbsize(&headerlen, &blocksize); 610 #endif 611 while (--argc > 0 && **++argv == '-') { 612 while (*++*argv) { 613 switch (**argv) { 614 case 'n': 615 func = donames; 616 break; 617 case 'c': 618 func = dofsizes; 619 break; 620 case 'a': 621 all = 1; 622 break; 623 case 'f': 624 count = 1; 625 break; 626 case 'h': 627 estimate = 1; 628 break; 629 #ifndef COMPAT 630 case 'k': 631 blocksize = 1024; 632 break; 633 #endif /* COMPAT */ 634 case 'v': 635 unused = 1; 636 break; 637 default: 638 usage(); 639 } 640 } 641 } 642 cnt = getmntinfo(&mp, MNT_NOWAIT); 643 if (all) { 644 for (; --cnt >= 0; mp++) { 645 if (strcmp(mp->f_fstypename, MOUNT_FFS) == 0 || 646 strcmp(mp->f_fstypename, "ufs") == 0) { 647 if ((nm = strrchr(mp->f_mntfromname, '/'))) { 648 snprintf(dev, sizeof(dev), "%sr%s", 649 _PATH_DEV, nm + 1); 650 nm = dev; 651 } else 652 nm = mp->f_mntfromname; 653 quot(nm, mp->f_mntonname); 654 } 655 } 656 } 657 for (; --argc >= 0; argv++) { 658 mountpoint = NULL; 659 nm = *argv; 660 661 /* Remove trailing slashes from name. */ 662 cp = nm + strlen(nm); 663 while (*(--cp) == '/' && cp != nm) 664 *cp = '\0'; 665 666 /* Look up the name in the mount table. */ 667 for (i = 0; i < cnt; i++) { 668 /* Remove trailing slashes from name. */ 669 cp = mp[i].f_mntonname + strlen(mp[i].f_mntonname); 670 while (*(--cp) == '/' && cp != mp[i].f_mntonname) 671 *cp = '\0'; 672 673 if ((!strcmp(mp->f_fstypename, MOUNT_FFS) || 674 !strcmp(mp->f_fstypename, MOUNT_MFS) || 675 !strcmp(mp->f_fstypename, "ufs")) && 676 strcmp(nm, mp[i].f_mntonname) == 0) { 677 nm = mp[i].f_mntfromname; 678 mountpoint = mp[i].f_mntonname; 679 break; 680 } 681 } 682 683 /* Make sure we have the raw device... */ 684 if (strncmp(nm, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0 && 685 nm[sizeof(_PATH_DEV) - 1] != 'r') { 686 snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV, 687 nm + sizeof(_PATH_DEV) - 1); 688 nm = dev; 689 } 690 quot(nm, mountpoint); 691 } 692 exit(0); 693 } 694