1 /* 2 * Copyright (C) 1991, 1994 Wolfgang Solfrank. 3 * Copyright (C) 1991, 1994 TooLs GmbH. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by TooLs GmbH. 17 * 4. The name of TooLs GmbH may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/stdint.h> 37 #include <sys/mount.h> 38 #include <sys/disklabel.h> 39 #include <sys/time.h> 40 #include <ufs/ufs/dinode.h> 41 #include <ufs/ffs/fs.h> 42 43 #include <err.h> 44 #include <fcntl.h> 45 #include <fstab.h> 46 #include <errno.h> 47 #include <paths.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 *, char *); 59 static long blocksize; 60 static char *header; 61 static int headerlen; 62 63 static union dinode *get_inode(int, struct fs *, ino_t); 64 static int virtualblocks(struct fs *, union dinode *); 65 static int isfree(struct fs *, union dinode *); 66 static void inituser(void); 67 static void usrrehash(void); 68 static struct user *user(uid_t); 69 static int cmpusers(const void *, const void *); 70 static void uses(uid_t, daddr_t, time_t); 71 static void initfsizes(void); 72 static void dofsizes(int, struct fs *, char *); 73 static void douser(int, struct fs *, char *); 74 static void donames(int, struct fs *, char *); 75 static void usage(void); 76 static void quot(char *, char *); 77 78 /* 79 * Original BSD quot doesn't round to number of frags/blocks, 80 * doesn't account for indirection blocks and gets it totally 81 * wrong if the size is a multiple of the blocksize. 82 * The new code always counts the number of 512 byte blocks 83 * instead of the number of kilobytes and converts them to 84 * kByte when done (on request). 85 * 86 * Due to the size of modern disks, we must cast intermediate 87 * values to 64 bits to prevent potential overflows. 88 */ 89 #ifdef COMPAT 90 #define SIZE(n) (n) 91 #else 92 #define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) 93 #endif 94 95 #define INOCNT(fs) ((fs)->fs_ipg) 96 #define INOSZ(fs) \ 97 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 98 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 99 100 union dinode { 101 struct ufs1_dinode dp1; 102 struct ufs2_dinode dp2; 103 }; 104 #define DIP(fs, dp, field) \ 105 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 106 (dp)->dp1.field : (dp)->dp2.field) 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 caddr_t ipbuf; 115 static struct cg *cgp; 116 static ino_t last; 117 static int cg; 118 struct ufs2_dinode *di2; 119 120 if (fd < 0) { /* flush cache */ 121 if (ipbuf) { 122 free(ipbuf); 123 ipbuf = 0; 124 if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) { 125 free(cgp); 126 cgp = 0; 127 } 128 } 129 return 0; 130 } 131 132 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 133 if (super->fs_magic == FS_UFS2_MAGIC && 134 (!cgp || cg != ino_to_cg(super, ino))) { 135 cg = ino_to_cg(super, ino); 136 if (!cgp && !(cgp = malloc(super->fs_cgsize))) 137 errx(1, "allocate cg"); 138 if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0) 139 err(1, "lseek cg"); 140 if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) 141 err(1, "read cg"); 142 if (!cg_chkmagic(cgp)) 143 errx(1, "cg has bad magic"); 144 } 145 if (!ipbuf 146 && !(ipbuf = malloc(INOSZ(super)))) 147 errx(1, "allocate inodes"); 148 last = (ino / INOCNT(super)) * INOCNT(super); 149 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 150 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 151 err(1, "read inodes"); 152 } 153 154 if (super->fs_magic == FS_UFS1_MAGIC) 155 return ((union dinode *) 156 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 157 di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; 158 /* If the inode is unused, it might be unallocated too, so zero it. */ 159 if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) 160 bzero(di2, sizeof (*di2)); 161 return ((union dinode *)di2); 162 } 163 164 #ifdef COMPAT 165 #define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) 166 #else 167 #define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 168 #endif 169 170 static int virtualblocks(super, dp) 171 struct fs *super; 172 union dinode *dp; 173 { 174 register off_t nblk, sz; 175 176 sz = DIP(super, dp, di_size); 177 #ifdef COMPAT 178 if (lblkno(super,sz) >= NDADDR) { 179 nblk = blkroundup(super,sz); 180 if (sz == nblk) 181 nblk += super->fs_bsize; 182 } 183 184 return sz / 1024; 185 186 #else /* COMPAT */ 187 188 if (lblkno(super,sz) >= NDADDR) { 189 nblk = blkroundup(super,sz); 190 sz = lblkno(super,nblk); 191 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 192 while (sz > 0) { 193 nblk += sz * super->fs_bsize; 194 /* sz - 1 rounded up */ 195 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 196 } 197 } else 198 nblk = fragroundup(super,sz); 199 200 return nblk / 512; 201 #endif /* COMPAT */ 202 } 203 204 static int 205 isfree(super, dp) 206 struct fs *super; 207 union dinode *dp; 208 { 209 #ifdef COMPAT 210 return (DIP(super, dp, di_mode) & IFMT) == 0; 211 #else /* COMPAT */ 212 213 switch (DIP(super, dp, di_mode) & IFMT) { 214 case IFIFO: 215 case IFLNK: /* should check FASTSYMLINK? */ 216 case IFDIR: 217 case IFREG: 218 return 0; 219 case IFCHR: 220 case IFBLK: 221 case IFSOCK: 222 case IFWHT: 223 case 0: 224 return 1; 225 default: 226 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 227 } 228 #endif 229 } 230 231 static struct user { 232 uid_t uid; 233 char *name; 234 daddr_t space; 235 long count; 236 daddr_t spc30; 237 daddr_t spc60; 238 daddr_t spc90; 239 } *users; 240 static int nusers; 241 242 static void 243 inituser() 244 { 245 register int i; 246 register struct user *usr; 247 248 if (!nusers) { 249 nusers = 8; 250 if (!(users = 251 (struct user *)calloc(nusers,sizeof(struct user)))) 252 errx(1, "allocate users"); 253 } else { 254 for (usr = users, i = nusers; --i >= 0; usr++) { 255 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 256 usr->count = 0; 257 } 258 } 259 } 260 261 static void 262 usrrehash() 263 { 264 register int i; 265 register struct user *usr, *usrn; 266 struct user *svusr; 267 268 svusr = users; 269 nusers <<= 1; 270 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 271 errx(1, "allocate users"); 272 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 273 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 274 usrn--) { 275 if (usrn <= users) 276 usrn = users + nusers; 277 } 278 *usrn = *usr; 279 } 280 } 281 282 static struct user * 283 user(uid) 284 uid_t uid; 285 { 286 register struct user *usr; 287 register int i; 288 struct passwd *pwd; 289 290 while (1) { 291 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 292 usr--) { 293 if (!usr->name) { 294 usr->uid = uid; 295 296 if (!(pwd = getpwuid(uid))) { 297 if ((usr->name = (char *)malloc(7))) 298 sprintf(usr->name,"#%d",uid); 299 } else { 300 if ((usr->name = (char *) 301 malloc(strlen(pwd->pw_name) + 1))) 302 strcpy(usr->name,pwd->pw_name); 303 } 304 if (!usr->name) 305 errx(1, "allocate users"); 306 307 return usr; 308 309 } else if (usr->uid == uid) 310 return usr; 311 312 if (usr <= users) 313 usr = users + nusers; 314 } 315 usrrehash(); 316 } 317 } 318 319 static int 320 cmpusers(v1,v2) 321 const void *v1, *v2; 322 { 323 const struct user *u1, *u2; 324 u1 = (const struct user *)v1; 325 u2 = (const struct user *)v2; 326 327 return u2->space - u1->space; 328 } 329 330 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 331 cmpusers)) 332 333 static void 334 uses(uid,blks,act) 335 uid_t uid; 336 daddr_t blks; 337 time_t act; 338 { 339 static time_t today; 340 register struct user *usr; 341 342 if (!today) 343 time(&today); 344 345 usr = user(uid); 346 usr->count++; 347 usr->space += blks; 348 349 if (today - act > 90L * 24L * 60L * 60L) 350 usr->spc90 += blks; 351 if (today - act > 60L * 24L * 60L * 60L) 352 usr->spc60 += blks; 353 if (today - act > 30L * 24L * 60L * 60L) 354 usr->spc30 += blks; 355 } 356 357 #ifdef COMPAT 358 #define FSZCNT 500 359 #else 360 #define FSZCNT 512 361 #endif 362 struct fsizes { 363 struct fsizes *fsz_next; 364 daddr_t fsz_first, fsz_last; 365 ino_t fsz_count[FSZCNT]; 366 daddr_t fsz_sz[FSZCNT]; 367 } *fsizes; 368 369 static void 370 initfsizes() 371 { 372 register struct fsizes *fp; 373 register int i; 374 375 for (fp = fsizes; fp; fp = fp->fsz_next) { 376 for (i = FSZCNT; --i >= 0;) { 377 fp->fsz_count[i] = 0; 378 fp->fsz_sz[i] = 0; 379 } 380 } 381 } 382 383 static void 384 dofsizes(fd, super, name) 385 int fd; 386 struct fs *super; 387 char *name; 388 { 389 ino_t inode, maxino; 390 union dinode *dp; 391 daddr_t sz, ksz; 392 struct fsizes *fp, **fsp; 393 register int i; 394 395 maxino = super->fs_ncg * super->fs_ipg - 1; 396 #ifdef COMPAT 397 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 398 errx(1, "allocate fsize structure"); 399 #endif /* COMPAT */ 400 for (inode = 0; inode < maxino; inode++) { 401 errno = 0; 402 if ((dp = get_inode(fd,super,inode)) 403 #ifdef COMPAT 404 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 405 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 406 #else /* COMPAT */ 407 && !isfree(super, dp) 408 #endif /* COMPAT */ 409 ) { 410 sz = estimate ? virtualblocks(super, dp) : 411 actualblocks(super, dp); 412 #ifdef COMPAT 413 if (sz >= FSZCNT) { 414 fsizes->fsz_count[FSZCNT-1]++; 415 fsizes->fsz_sz[FSZCNT-1] += sz; 416 } else { 417 fsizes->fsz_count[sz]++; 418 fsizes->fsz_sz[sz] += sz; 419 } 420 #else /* COMPAT */ 421 ksz = SIZE(sz); 422 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 423 if (ksz < fp->fsz_last) 424 break; 425 } 426 if (!fp || ksz < fp->fsz_first) { 427 if (!(fp = (struct fsizes *) 428 malloc(sizeof(struct fsizes)))) 429 errx(1, "allocate fsize structure"); 430 fp->fsz_next = *fsp; 431 *fsp = fp; 432 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 433 fp->fsz_last = fp->fsz_first + FSZCNT; 434 for (i = FSZCNT; --i >= 0;) { 435 fp->fsz_count[i] = 0; 436 fp->fsz_sz[i] = 0; 437 } 438 } 439 fp->fsz_count[ksz % FSZCNT]++; 440 fp->fsz_sz[ksz % FSZCNT] += sz; 441 #endif /* COMPAT */ 442 } else if (errno) { 443 err(1, "%s", name); 444 } 445 } 446 sz = 0; 447 for (fp = fsizes; fp; fp = fp->fsz_next) { 448 for (i = 0; i < FSZCNT; i++) { 449 if (fp->fsz_count[i]) 450 printf("%jd\t%jd\t%d\n", 451 (intmax_t)(fp->fsz_first + i), 452 (intmax_t)fp->fsz_count[i], 453 SIZE(sz += fp->fsz_sz[i])); 454 } 455 } 456 } 457 458 static void 459 douser(fd, super, name) 460 int fd; 461 struct fs *super; 462 char *name; 463 { 464 ino_t inode, maxino; 465 struct user *usr, *usrs; 466 union dinode *dp; 467 register int n; 468 469 maxino = super->fs_ncg * super->fs_ipg - 1; 470 for (inode = 0; inode < maxino; inode++) { 471 errno = 0; 472 if ((dp = get_inode(fd,super,inode)) 473 && !isfree(super, dp)) 474 uses(DIP(super, dp, di_uid), 475 estimate ? virtualblocks(super, dp) : 476 actualblocks(super, dp), 477 DIP(super, dp, di_atime)); 478 else if (errno) { 479 err(1, "%s", name); 480 } 481 } 482 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 483 errx(1, "allocate users"); 484 bcopy(users,usrs,nusers * sizeof(struct user)); 485 sortusers(usrs); 486 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 487 printf("%5d",SIZE(usr->space)); 488 if (count) 489 printf("\t%5ld",usr->count); 490 printf("\t%-8s",usr->name); 491 if (unused) 492 printf("\t%5d\t%5d\t%5d", 493 SIZE(usr->spc30), 494 SIZE(usr->spc60), 495 SIZE(usr->spc90)); 496 printf("\n"); 497 } 498 free(usrs); 499 } 500 501 static void 502 donames(fd, super, name) 503 int fd; 504 struct fs *super; 505 char *name; 506 { 507 int c; 508 ino_t inode; 509 ino_t maxino; 510 union dinode *dp; 511 512 maxino = super->fs_ncg * super->fs_ipg - 1; 513 /* first skip the name of the filesystem */ 514 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 515 while ((c = getchar()) != EOF && c != '\n'); 516 ungetc(c,stdin); 517 while (scanf("%u",&inode) == 1) { 518 if (inode > maxino) { 519 warnx("illegal inode %d",inode); 520 return; 521 } 522 errno = 0; 523 if ((dp = get_inode(fd,super,inode)) 524 && !isfree(super, dp)) { 525 printf("%s\t",user(DIP(super, dp, di_uid))->name); 526 /* now skip whitespace */ 527 while ((c = getchar()) == ' ' || c == '\t'); 528 /* and print out the remainder of the input line */ 529 while (c != EOF && c != '\n') { 530 putchar(c); 531 c = getchar(); 532 } 533 putchar('\n'); 534 } else { 535 if (errno) { 536 err(1, "%s", name); 537 } 538 /* skip this line */ 539 while ((c = getchar()) != EOF && c != '\n'); 540 } 541 if (c == EOF) 542 break; 543 } 544 } 545 546 static void 547 usage() 548 { 549 #ifdef COMPAT 550 fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); 551 #else /* COMPAT */ 552 fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); 553 #endif /* COMPAT */ 554 exit(1); 555 } 556 557 /* 558 * Possible superblock locations ordered from most to least likely. 559 */ 560 static int sblock_try[] = SBLOCKSEARCH; 561 static char superblock[SBLOCKSIZE]; 562 563 void 564 quot(name,mp) 565 char *name, *mp; 566 { 567 int i, fd; 568 struct fs *fs; 569 570 get_inode(-1, NULL, 0); /* flush cache */ 571 inituser(); 572 initfsizes(); 573 if ((fd = open(name,0)) < 0) { 574 warn("%s", name); 575 close(fd); 576 return; 577 } 578 for (i = 0; sblock_try[i] != -1; i++) { 579 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) { 580 close(fd); 581 return; 582 } 583 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) { 584 close(fd); 585 return; 586 } 587 fs = (struct fs *)superblock; 588 if ((fs->fs_magic == FS_UFS1_MAGIC || 589 (fs->fs_magic == FS_UFS2_MAGIC && 590 fs->fs_sblockloc == sblock_try[i])) && 591 fs->fs_bsize <= MAXBSIZE && 592 fs->fs_bsize >= sizeof(struct fs)) 593 break; 594 } 595 if (sblock_try[i] == -1) { 596 warnx("%s: not a BSD filesystem",name); 597 close(fd); 598 return; 599 } 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 struct fstab *fs; 616 char dev[MNAMELEN + 1]; 617 char *nm; 618 int cnt; 619 620 func = douser; 621 #ifndef COMPAT 622 header = getbsize(&headerlen,&blocksize); 623 #endif 624 while (--argc > 0 && **++argv == '-') { 625 while (*++*argv) { 626 switch (**argv) { 627 case 'n': 628 func = donames; 629 break; 630 case 'c': 631 func = dofsizes; 632 break; 633 case 'a': 634 all = 1; 635 break; 636 case 'f': 637 count = 1; 638 break; 639 case 'h': 640 estimate = 1; 641 break; 642 #ifndef COMPAT 643 case 'k': 644 blocksize = 1024; 645 break; 646 #endif /* COMPAT */ 647 case 'v': 648 unused = 1; 649 break; 650 default: 651 usage(); 652 } 653 } 654 } 655 if (all) { 656 cnt = getmntinfo(&mp,MNT_NOWAIT); 657 for (; --cnt >= 0; mp++) { 658 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) { 659 if ((nm = strrchr(mp->f_mntfromname,'/'))) { 660 sprintf(dev,"%s%s",_PATH_DEV,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 if ((fs = getfsfile(*argv)) != NULL) 670 quot(fs->fs_spec, 0); 671 else 672 quot(*argv,0); 673 argv++; 674 } 675 return 0; 676 } 677