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 #ifndef lint 33 static const char rcsid[] = 34 "$FreeBSD$"; 35 #endif /* not lint */ 36 37 #include <sys/param.h> 38 #include <sys/stdint.h> 39 #include <sys/mount.h> 40 #include <sys/disklabel.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 <fcntl.h> 47 #include <fstab.h> 48 #include <errno.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 /* some flags of what to do: */ 57 static char estimate; 58 static char count; 59 static char unused; 60 static void (*func)(int, struct fs *, char *); 61 static long blocksize; 62 static char *header; 63 static size_t headerlen; 64 65 static union dinode *get_inode(int, struct fs *, ino_t); 66 static int virtualblocks(struct fs *, union dinode *); 67 static int isfree(struct fs *, union dinode *); 68 static void inituser(void); 69 static void usrrehash(void); 70 static struct user *user(uid_t); 71 static int cmpusers(const void *, const void *); 72 static void uses(uid_t, daddr_t, time_t); 73 static void initfsizes(void); 74 static void dofsizes(int, struct fs *, char *); 75 static void douser(int, struct fs *, char *); 76 static void donames(int, struct fs *, char *); 77 static void usage(void); 78 static void quot(char *, char *); 79 80 /* 81 * Original BSD quot doesn't round to number of frags/blocks, 82 * doesn't account for indirection blocks and gets it totally 83 * wrong if the size is a multiple of the blocksize. 84 * The new code always counts the number of 512 byte blocks 85 * instead of the number of kilobytes and converts them to 86 * kByte when done (on request). 87 * 88 * Due to the size of modern disks, we must cast intermediate 89 * values to 64 bits to prevent potential overflows. 90 */ 91 #ifdef COMPAT 92 #define SIZE(n) (n) 93 #else 94 #define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) 95 #endif 96 97 #define INOCNT(fs) ((fs)->fs_ipg) 98 #define INOSZ(fs) \ 99 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 100 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 101 102 union dinode { 103 struct ufs1_dinode dp1; 104 struct ufs2_dinode dp2; 105 }; 106 #define DIP(fs, dp, field) \ 107 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 108 (dp)->dp1.field : (dp)->dp2.field) 109 110 static union dinode * 111 get_inode(fd,super,ino) 112 int fd; 113 struct fs *super; 114 ino_t ino; 115 { 116 static caddr_t ipbuf; 117 static ino_t last; 118 119 if (fd < 0) { /* flush cache */ 120 if (ipbuf) { 121 free(ipbuf); 122 ipbuf = 0; 123 } 124 return 0; 125 } 126 127 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 128 if (!ipbuf 129 && !(ipbuf = malloc(INOSZ(super)))) 130 errx(1, "allocate inodes"); 131 last = (ino / INOCNT(super)) * INOCNT(super); 132 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 133 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 134 err(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, di_blocks) / 2) 146 #else 147 #define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 148 #endif 149 150 static int virtualblocks(super, dp) 151 struct fs *super; 152 union dinode *dp; 153 { 154 register off_t nblk, sz; 155 156 sz = DIP(super, dp, di_size); 157 #ifdef COMPAT 158 if (lblkno(super,sz) >= NDADDR) { 159 nblk = blkroundup(super,sz); 160 if (sz == nblk) 161 nblk += super->fs_bsize; 162 } 163 164 return sz / 1024; 165 166 #else /* COMPAT */ 167 168 if (lblkno(super,sz) >= NDADDR) { 169 nblk = blkroundup(super,sz); 170 sz = lblkno(super,nblk); 171 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 172 while (sz > 0) { 173 nblk += sz * super->fs_bsize; 174 /* sz - 1 rounded up */ 175 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 176 } 177 } else 178 nblk = fragroundup(super,sz); 179 180 return nblk / 512; 181 #endif /* COMPAT */ 182 } 183 184 static int 185 isfree(super, dp) 186 struct fs *super; 187 union dinode *dp; 188 { 189 #ifdef COMPAT 190 return (DIP(super, dp, di_mode) & IFMT) == 0; 191 #else /* COMPAT */ 192 193 switch (DIP(super, dp, di_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 register int i; 220 register 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 register int i; 239 register 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)); 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 register struct user *usr; 261 register int i; 262 struct passwd *pwd; 263 264 while (1) { 265 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 266 usr--) { 267 if (!usr->name) { 268 usr->uid = uid; 269 270 if (!(pwd = getpwuid(uid))) { 271 if ((usr->name = (char *)malloc(7))) 272 sprintf(usr->name,"#%d",uid); 273 } else { 274 if ((usr->name = (char *) 275 malloc(strlen(pwd->pw_name) + 1))) 276 strcpy(usr->name,pwd->pw_name); 277 } 278 if (!usr->name) 279 errx(1, "allocate users"); 280 281 return usr; 282 283 } else if (usr->uid == uid) 284 return usr; 285 286 if (usr <= users) 287 usr = users + nusers; 288 } 289 usrrehash(); 290 } 291 } 292 293 static int 294 cmpusers(v1,v2) 295 const void *v1, *v2; 296 { 297 const struct user *u1, *u2; 298 u1 = (const struct user *)v1; 299 u2 = (const struct user *)v2; 300 301 return u2->space - u1->space; 302 } 303 304 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 305 cmpusers)) 306 307 static void 308 uses(uid,blks,act) 309 uid_t uid; 310 daddr_t blks; 311 time_t act; 312 { 313 static time_t today; 314 register struct user *usr; 315 316 if (!today) 317 time(&today); 318 319 usr = user(uid); 320 usr->count++; 321 usr->space += blks; 322 323 if (today - act > 90L * 24L * 60L * 60L) 324 usr->spc90 += blks; 325 if (today - act > 60L * 24L * 60L * 60L) 326 usr->spc60 += blks; 327 if (today - act > 30L * 24L * 60L * 60L) 328 usr->spc30 += blks; 329 } 330 331 #ifdef COMPAT 332 #define FSZCNT 500 333 #else 334 #define FSZCNT 512 335 #endif 336 struct fsizes { 337 struct fsizes *fsz_next; 338 daddr_t fsz_first, fsz_last; 339 ino_t fsz_count[FSZCNT]; 340 daddr_t fsz_sz[FSZCNT]; 341 } *fsizes; 342 343 static void 344 initfsizes() 345 { 346 register struct fsizes *fp; 347 register int i; 348 349 for (fp = fsizes; fp; fp = fp->fsz_next) { 350 for (i = FSZCNT; --i >= 0;) { 351 fp->fsz_count[i] = 0; 352 fp->fsz_sz[i] = 0; 353 } 354 } 355 } 356 357 static void 358 dofsizes(fd, super, name) 359 int fd; 360 struct fs *super; 361 char *name; 362 { 363 ino_t inode, maxino; 364 union dinode *dp; 365 daddr_t sz, ksz; 366 struct fsizes *fp, **fsp; 367 register int i; 368 369 maxino = super->fs_ncg * super->fs_ipg - 1; 370 #ifdef COMPAT 371 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 372 errx(1, "allocate fsize structure"); 373 #endif /* COMPAT */ 374 for (inode = 0; inode < maxino; inode++) { 375 errno = 0; 376 if ((dp = get_inode(fd,super,inode)) 377 #ifdef COMPAT 378 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 379 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 380 #else /* COMPAT */ 381 && !isfree(super, dp) 382 #endif /* COMPAT */ 383 ) { 384 sz = estimate ? virtualblocks(super, dp) : 385 actualblocks(super, dp); 386 #ifdef COMPAT 387 if (sz >= FSZCNT) { 388 fsizes->fsz_count[FSZCNT-1]++; 389 fsizes->fsz_sz[FSZCNT-1] += sz; 390 } else { 391 fsizes->fsz_count[sz]++; 392 fsizes->fsz_sz[sz] += sz; 393 } 394 #else /* COMPAT */ 395 ksz = SIZE(sz); 396 for (fsp = &fsizes; (fp = *fsp); 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, "allocate 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 err(1, "%s", name); 418 } 419 } 420 sz = 0; 421 for (fp = fsizes; fp; fp = fp->fsz_next) { 422 for (i = 0; i < FSZCNT; i++) { 423 if (fp->fsz_count[i]) 424 printf("%jd\t%jd\t%d\n", 425 (intmax_t)(fp->fsz_first + i), 426 (intmax_t)fp->fsz_count[i], 427 SIZE(sz += fp->fsz_sz[i])); 428 } 429 } 430 } 431 432 static void 433 douser(fd, super, name) 434 int fd; 435 struct fs *super; 436 char *name; 437 { 438 ino_t inode, maxino; 439 struct user *usr, *usrs; 440 union dinode *dp; 441 register int n; 442 443 maxino = super->fs_ncg * super->fs_ipg - 1; 444 for (inode = 0; inode < maxino; inode++) { 445 errno = 0; 446 if ((dp = get_inode(fd,super,inode)) 447 && !isfree(super, dp)) 448 uses(DIP(super, dp, di_uid), 449 estimate ? virtualblocks(super, dp) : 450 actualblocks(super, dp), 451 DIP(super, dp, di_atime)); 452 else if (errno) { 453 err(1, "%s", name); 454 } 455 } 456 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 457 errx(1, "allocate users"); 458 bcopy(users,usrs,nusers * sizeof(struct user)); 459 sortusers(usrs); 460 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 461 printf("%5d",SIZE(usr->space)); 462 if (count) 463 printf("\t%5ld",usr->count); 464 printf("\t%-8s",usr->name); 465 if (unused) 466 printf("\t%5d\t%5d\t%5d", 467 SIZE(usr->spc30), 468 SIZE(usr->spc60), 469 SIZE(usr->spc90)); 470 printf("\n"); 471 } 472 free(usrs); 473 } 474 475 static void 476 donames(fd, super, name) 477 int fd; 478 struct fs *super; 479 char *name; 480 { 481 int c; 482 ino_t inode, inode1; 483 ino_t maxino; 484 union dinode *dp; 485 486 maxino = super->fs_ncg * super->fs_ipg - 1; 487 /* first skip the name of the filesystem */ 488 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 489 while ((c = getchar()) != EOF && c != '\n'); 490 ungetc(c,stdin); 491 inode1 = -1; 492 while (scanf("%u",&inode) == 1) { 493 if (inode > maxino) { 494 warnx("illegal inode %d",inode); 495 return; 496 } 497 errno = 0; 498 if ((dp = get_inode(fd,super,inode)) 499 && !isfree(super, dp)) { 500 printf("%s\t",user(DIP(super, dp, di_uid))->name); 501 /* now skip whitespace */ 502 while ((c = getchar()) == ' ' || c == '\t'); 503 /* and print out the remainder of the input line */ 504 while (c != EOF && c != '\n') { 505 putchar(c); 506 c = getchar(); 507 } 508 putchar('\n'); 509 inode1 = inode; 510 } else { 511 if (errno) { 512 err(1, "%s", name); 513 } 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 * Possible superblock locations ordered from most to least likely. 535 */ 536 static int sblock_try[] = SBLOCKSEARCH; 537 static char superblock[SBLOCKSIZE]; 538 539 void 540 quot(name,mp) 541 char *name, *mp; 542 { 543 int i, fd; 544 struct fs *fs; 545 546 get_inode(-1, NULL, 0); /* flush cache */ 547 inituser(); 548 initfsizes(); 549 if ((fd = open(name,0)) < 0) { 550 warn("%s", name); 551 close(fd); 552 return; 553 } 554 for (i = 0; sblock_try[i] != -1; i++) { 555 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) { 556 close(fd); 557 return; 558 } 559 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) { 560 close(fd); 561 return; 562 } 563 fs = (struct fs *)superblock; 564 if ((fs->fs_magic == FS_UFS1_MAGIC || 565 (fs->fs_magic == FS_UFS2_MAGIC && 566 fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) && 567 fs->fs_bsize <= MAXBSIZE && 568 fs->fs_bsize >= sizeof(struct fs)) 569 break; 570 } 571 if (sblock_try[i] == -1) { 572 warnx("%s: not a BSD filesystem",name); 573 close(fd); 574 return; 575 } 576 printf("%s:",name); 577 if (mp) 578 printf(" (%s)",mp); 579 putchar('\n'); 580 (*func)(fd, fs, name); 581 close(fd); 582 } 583 584 int 585 main(argc,argv) 586 int argc; 587 char **argv; 588 { 589 char all = 0; 590 struct statfs *mp; 591 struct fstab *fs; 592 char dev[MNAMELEN + 1]; 593 char *nm; 594 int cnt; 595 596 func = douser; 597 #ifndef COMPAT 598 header = getbsize(&headerlen,&blocksize); 599 #endif 600 while (--argc > 0 && **++argv == '-') { 601 while (*++*argv) { 602 switch (**argv) { 603 case 'n': 604 func = donames; 605 break; 606 case 'c': 607 func = dofsizes; 608 break; 609 case 'a': 610 all = 1; 611 break; 612 case 'f': 613 count = 1; 614 break; 615 case 'h': 616 estimate = 1; 617 break; 618 #ifndef COMPAT 619 case 'k': 620 blocksize = 1024; 621 break; 622 #endif /* COMPAT */ 623 case 'v': 624 unused = 1; 625 break; 626 default: 627 usage(); 628 } 629 } 630 } 631 if (all) { 632 cnt = getmntinfo(&mp,MNT_NOWAIT); 633 for (; --cnt >= 0; mp++) { 634 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) { 635 if ((nm = strrchr(mp->f_mntfromname,'/'))) { 636 sprintf(dev,"%s%s",_PATH_DEV,nm + 1); 637 nm = dev; 638 } else 639 nm = mp->f_mntfromname; 640 quot(nm,mp->f_mntonname); 641 } 642 } 643 } 644 while (--argc >= 0) { 645 if ((fs = getfsfile(*argv)) != NULL) 646 quot(fs->fs_spec, 0); 647 else 648 quot(*argv,0); 649 argv++; 650 } 651 return 0; 652 } 653