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 ino_t last; 116 117 if (fd < 0) { /* flush cache */ 118 if (ipbuf) { 119 free(ipbuf); 120 ipbuf = 0; 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, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 131 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 132 err(1, "read inodes"); 133 } 134 135 if (super->fs_magic == FS_UFS1_MAGIC) 136 return ((union dinode *) 137 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 138 return ((union dinode *) 139 &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]); 140 } 141 142 #ifdef COMPAT 143 #define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) 144 #else 145 #define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 146 #endif 147 148 static int virtualblocks(super, dp) 149 struct fs *super; 150 union dinode *dp; 151 { 152 register 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 164 #else /* COMPAT */ 165 166 if (lblkno(super,sz) >= NDADDR) { 167 nblk = blkroundup(super,sz); 168 sz = lblkno(super,nblk); 169 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 170 while (sz > 0) { 171 nblk += sz * super->fs_bsize; 172 /* sz - 1 rounded up */ 173 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 174 } 175 } else 176 nblk = fragroundup(super,sz); 177 178 return nblk / 512; 179 #endif /* COMPAT */ 180 } 181 182 static int 183 isfree(super, dp) 184 struct fs *super; 185 union dinode *dp; 186 { 187 #ifdef COMPAT 188 return (DIP(super, dp, di_mode) & IFMT) == 0; 189 #else /* COMPAT */ 190 191 switch (DIP(super, dp, di_mode) & IFMT) { 192 case IFIFO: 193 case IFLNK: /* should check FASTSYMLINK? */ 194 case IFDIR: 195 case IFREG: 196 return 0; 197 default: 198 return 1; 199 } 200 #endif 201 } 202 203 static struct user { 204 uid_t uid; 205 char *name; 206 daddr_t space; 207 long count; 208 daddr_t spc30; 209 daddr_t spc60; 210 daddr_t spc90; 211 } *users; 212 static int nusers; 213 214 static void 215 inituser() 216 { 217 register int i; 218 register struct user *usr; 219 220 if (!nusers) { 221 nusers = 8; 222 if (!(users = 223 (struct user *)calloc(nusers,sizeof(struct user)))) 224 errx(1, "allocate users"); 225 } else { 226 for (usr = users, i = nusers; --i >= 0; usr++) { 227 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 228 usr->count = 0; 229 } 230 } 231 } 232 233 static void 234 usrrehash() 235 { 236 register int i; 237 register struct user *usr, *usrn; 238 struct user *svusr; 239 240 svusr = users; 241 nusers <<= 1; 242 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 243 errx(1, "allocate users"); 244 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 245 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 246 usrn--) { 247 if (usrn <= users) 248 usrn = users + nusers; 249 } 250 *usrn = *usr; 251 } 252 } 253 254 static struct user * 255 user(uid) 256 uid_t uid; 257 { 258 register struct user *usr; 259 register int i; 260 struct passwd *pwd; 261 262 while (1) { 263 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 264 usr--) { 265 if (!usr->name) { 266 usr->uid = uid; 267 268 if (!(pwd = getpwuid(uid))) { 269 if ((usr->name = (char *)malloc(7))) 270 sprintf(usr->name,"#%d",uid); 271 } else { 272 if ((usr->name = (char *) 273 malloc(strlen(pwd->pw_name) + 1))) 274 strcpy(usr->name,pwd->pw_name); 275 } 276 if (!usr->name) 277 errx(1, "allocate users"); 278 279 return usr; 280 281 } else if (usr->uid == uid) 282 return usr; 283 284 if (usr <= users) 285 usr = users + nusers; 286 } 287 usrrehash(); 288 } 289 } 290 291 static int 292 cmpusers(v1,v2) 293 const void *v1, *v2; 294 { 295 const struct user *u1, *u2; 296 u1 = (const struct user *)v1; 297 u2 = (const struct user *)v2; 298 299 return u2->space - 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 register 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 register struct fsizes *fp; 345 register 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 register 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, "allocate 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, di_mode) & IFMT) == IFREG 377 || (DIP(super, dp, di_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); fsp = &fp->fsz_next) { 395 if (ksz < fp->fsz_last) 396 break; 397 } 398 if (!fp || ksz < fp->fsz_first) { 399 if (!(fp = (struct fsizes *) 400 malloc(sizeof(struct fsizes)))) 401 errx(1, "allocate fsize structure"); 402 fp->fsz_next = *fsp; 403 *fsp = fp; 404 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 405 fp->fsz_last = fp->fsz_first + FSZCNT; 406 for (i = FSZCNT; --i >= 0;) { 407 fp->fsz_count[i] = 0; 408 fp->fsz_sz[i] = 0; 409 } 410 } 411 fp->fsz_count[ksz % FSZCNT]++; 412 fp->fsz_sz[ksz % FSZCNT] += sz; 413 #endif /* COMPAT */ 414 } else if (errno) { 415 err(1, "%s", name); 416 } 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("%jd\t%jd\t%d\n", 423 (intmax_t)(fp->fsz_first + i), 424 (intmax_t)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 register 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, di_uid), 447 estimate ? virtualblocks(super, dp) : 448 actualblocks(super, dp), 449 DIP(super, dp, di_atime)); 450 else if (errno) { 451 err(1, "%s", name); 452 } 453 } 454 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 455 errx(1, "allocate users"); 456 bcopy(users,usrs,nusers * sizeof(struct user)); 457 sortusers(usrs); 458 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 459 printf("%5d",SIZE(usr->space)); 460 if (count) 461 printf("\t%5ld",usr->count); 462 printf("\t%-8s",usr->name); 463 if (unused) 464 printf("\t%5d\t%5d\t%5d", 465 SIZE(usr->spc30), 466 SIZE(usr->spc60), 467 SIZE(usr->spc90)); 468 printf("\n"); 469 } 470 free(usrs); 471 } 472 473 static void 474 donames(fd, super, name) 475 int fd; 476 struct fs *super; 477 char *name; 478 { 479 int c; 480 ino_t inode; 481 ino_t maxino; 482 union dinode *dp; 483 484 maxino = super->fs_ncg * super->fs_ipg - 1; 485 /* first skip the name of the filesystem */ 486 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 487 while ((c = getchar()) != EOF && c != '\n'); 488 ungetc(c,stdin); 489 while (scanf("%u",&inode) == 1) { 490 if (inode > maxino) { 491 warnx("illegal inode %d",inode); 492 return; 493 } 494 errno = 0; 495 if ((dp = get_inode(fd,super,inode)) 496 && !isfree(super, dp)) { 497 printf("%s\t",user(DIP(super, dp, di_uid))->name); 498 /* now skip whitespace */ 499 while ((c = getchar()) == ' ' || c == '\t'); 500 /* and print out the remainder of the input line */ 501 while (c != EOF && c != '\n') { 502 putchar(c); 503 c = getchar(); 504 } 505 putchar('\n'); 506 } else { 507 if (errno) { 508 err(1, "%s", name); 509 } 510 /* skip this line */ 511 while ((c = getchar()) != EOF && c != '\n'); 512 } 513 if (c == EOF) 514 break; 515 } 516 } 517 518 static void 519 usage() 520 { 521 #ifdef COMPAT 522 fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); 523 #else /* COMPAT */ 524 fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); 525 #endif /* COMPAT */ 526 exit(1); 527 } 528 529 /* 530 * Possible superblock locations ordered from most to least likely. 531 */ 532 static int sblock_try[] = SBLOCKSEARCH; 533 static char superblock[SBLOCKSIZE]; 534 535 void 536 quot(name,mp) 537 char *name, *mp; 538 { 539 int i, fd; 540 struct fs *fs; 541 542 get_inode(-1, NULL, 0); /* flush cache */ 543 inituser(); 544 initfsizes(); 545 if ((fd = open(name,0)) < 0) { 546 warn("%s", name); 547 close(fd); 548 return; 549 } 550 for (i = 0; sblock_try[i] != -1; i++) { 551 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) { 552 close(fd); 553 return; 554 } 555 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) { 556 close(fd); 557 return; 558 } 559 fs = (struct fs *)superblock; 560 if ((fs->fs_magic == FS_UFS1_MAGIC || 561 (fs->fs_magic == FS_UFS2_MAGIC && 562 fs->fs_sblockloc == sblock_try[i])) && 563 fs->fs_bsize <= MAXBSIZE && 564 fs->fs_bsize >= sizeof(struct fs)) 565 break; 566 } 567 if (sblock_try[i] == -1) { 568 warnx("%s: not a BSD filesystem",name); 569 close(fd); 570 return; 571 } 572 printf("%s:",name); 573 if (mp) 574 printf(" (%s)",mp); 575 putchar('\n'); 576 (*func)(fd, fs, name); 577 close(fd); 578 } 579 580 int 581 main(argc,argv) 582 int argc; 583 char **argv; 584 { 585 char all = 0; 586 struct statfs *mp; 587 struct fstab *fs; 588 char dev[MNAMELEN + 1]; 589 char *nm; 590 int cnt; 591 592 func = douser; 593 #ifndef COMPAT 594 header = getbsize(&headerlen,&blocksize); 595 #endif 596 while (--argc > 0 && **++argv == '-') { 597 while (*++*argv) { 598 switch (**argv) { 599 case 'n': 600 func = donames; 601 break; 602 case 'c': 603 func = dofsizes; 604 break; 605 case 'a': 606 all = 1; 607 break; 608 case 'f': 609 count = 1; 610 break; 611 case 'h': 612 estimate = 1; 613 break; 614 #ifndef COMPAT 615 case 'k': 616 blocksize = 1024; 617 break; 618 #endif /* COMPAT */ 619 case 'v': 620 unused = 1; 621 break; 622 default: 623 usage(); 624 } 625 } 626 } 627 if (all) { 628 cnt = getmntinfo(&mp,MNT_NOWAIT); 629 for (; --cnt >= 0; mp++) { 630 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) { 631 if ((nm = strrchr(mp->f_mntfromname,'/'))) { 632 sprintf(dev,"%s%s",_PATH_DEV,nm + 1); 633 nm = dev; 634 } else 635 nm = mp->f_mntfromname; 636 quot(nm,mp->f_mntonname); 637 } 638 } 639 } 640 while (--argc >= 0) { 641 if ((fs = getfsfile(*argv)) != NULL) 642 quot(fs->fs_spec, 0); 643 else 644 quot(*argv,0); 645 argv++; 646 } 647 return 0; 648 } 649