1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 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 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/stdint.h> 39 #include <sys/mount.h> 40 #include <sys/disklabel.h> 41 #include <ufs/ufs/dinode.h> 42 #include <ufs/ffs/fs.h> 43 44 #include <err.h> 45 #include <fcntl.h> 46 #include <fstab.h> 47 #include <errno.h> 48 #include <libufs.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <time.h> 55 #include <unistd.h> 56 57 /* some flags of what to do: */ 58 static char estimate; 59 static char count; 60 static char unused; 61 static void (*func)(int, struct fs *, char *); 62 static long blocksize; 63 static char *header; 64 static int headerlen; 65 66 static union dinode *get_inode(int, struct fs *, ino_t); 67 static int virtualblocks(struct fs *, union dinode *); 68 static int isfree(struct fs *, union dinode *); 69 static void inituser(void); 70 static void usrrehash(void); 71 static struct user *user(uid_t); 72 static int cmpusers(const void *, const void *); 73 static void uses(uid_t, daddr_t, time_t); 74 static void initfsizes(void); 75 static void dofsizes(int, struct fs *, char *); 76 static void douser(int, struct fs *, char *); 77 static void donames(int, struct fs *, char *); 78 static void usage(void); 79 static void quot(char *, char *); 80 81 /* 82 * Original BSD quot doesn't round to number of frags/blocks, 83 * doesn't account for indirection blocks and gets it totally 84 * wrong if the size is a multiple of the blocksize. 85 * The new code always counts the number of 512 byte blocks 86 * instead of the number of kilobytes and converts them to 87 * kByte when done (on request). 88 * 89 * Due to the size of modern disks, we must cast intermediate 90 * values to 64 bits to prevent potential overflows. 91 */ 92 #ifdef COMPAT 93 #define SIZE(n) (n) 94 #else 95 #define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) 96 #endif 97 98 #define INOCNT(fs) ((fs)->fs_ipg) 99 #define INOSZ(fs) \ 100 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 101 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 102 103 union dinode { 104 struct ufs1_dinode dp1; 105 struct ufs2_dinode dp2; 106 }; 107 #define DIP(fs, dp, field) \ 108 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 109 (dp)->dp1.field : (dp)->dp2.field) 110 111 static union dinode * 112 get_inode(int fd, struct fs *super, 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 = rounddown(ino, 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(struct fs *super, union dinode *dp) 171 { 172 off_t nblk, sz; 173 174 sz = DIP(super, dp, di_size); 175 #ifdef COMPAT 176 if (lblkno(super,sz) >= UFS_NDADDR) { 177 nblk = blkroundup(super,sz); 178 if (sz == nblk) 179 nblk += super->fs_bsize; 180 } 181 182 return sz / 1024; 183 184 #else /* COMPAT */ 185 186 if (lblkno(super,sz) >= UFS_NDADDR) { 187 nblk = blkroundup(super,sz); 188 sz = lblkno(super,nblk); 189 sz = (sz - UFS_NDADDR + NINDIR(super) - 1) / NINDIR(super); 190 while (sz > 0) { 191 nblk += sz * super->fs_bsize; 192 /* sz - 1 rounded up */ 193 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 194 } 195 } else 196 nblk = fragroundup(super,sz); 197 198 return nblk / 512; 199 #endif /* COMPAT */ 200 } 201 202 static int 203 isfree(struct fs *super, union dinode *dp) 204 { 205 #ifdef COMPAT 206 return (DIP(super, dp, di_mode) & IFMT) == 0; 207 #else /* COMPAT */ 208 209 switch (DIP(super, dp, di_mode) & IFMT) { 210 case IFIFO: 211 case IFLNK: /* should check FASTSYMLINK? */ 212 case IFDIR: 213 case IFREG: 214 return 0; 215 case IFCHR: 216 case IFBLK: 217 case IFSOCK: 218 case IFWHT: 219 case 0: 220 return 1; 221 default: 222 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 223 } 224 #endif 225 } 226 227 static struct user { 228 uid_t uid; 229 char *name; 230 daddr_t space; 231 long count; 232 daddr_t spc30; 233 daddr_t spc60; 234 daddr_t spc90; 235 } *users; 236 static int nusers; 237 238 static void 239 inituser(void) 240 { 241 int i; 242 struct user *usr; 243 244 if (!nusers) { 245 nusers = 8; 246 if (!(users = 247 (struct user *)calloc(nusers,sizeof(struct user)))) 248 errx(1, "allocate users"); 249 } else { 250 for (usr = users, i = nusers; --i >= 0; usr++) { 251 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 252 usr->count = 0; 253 } 254 } 255 } 256 257 static void 258 usrrehash(void) 259 { 260 int i; 261 struct user *usr, *usrn; 262 struct user *svusr; 263 264 svusr = users; 265 nusers <<= 1; 266 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 267 errx(1, "allocate users"); 268 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 269 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 270 usrn--) { 271 if (usrn <= users) 272 usrn = users + nusers; 273 } 274 *usrn = *usr; 275 } 276 } 277 278 static struct user * 279 user(uid_t uid) 280 { 281 struct user *usr; 282 int i; 283 struct passwd *pwd; 284 285 while (1) { 286 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 287 usr--) { 288 if (!usr->name) { 289 usr->uid = uid; 290 291 if (!(pwd = getpwuid(uid))) { 292 if ((usr->name = (char *)malloc(7))) 293 sprintf(usr->name,"#%d",uid); 294 } else { 295 if ((usr->name = (char *) 296 malloc(strlen(pwd->pw_name) + 1))) 297 strcpy(usr->name,pwd->pw_name); 298 } 299 if (!usr->name) 300 errx(1, "allocate users"); 301 302 return usr; 303 304 } else if (usr->uid == uid) 305 return usr; 306 307 if (usr <= users) 308 usr = users + nusers; 309 } 310 usrrehash(); 311 } 312 } 313 314 static int 315 cmpusers(const void *v1, const void *v2) 316 { 317 const struct user *u1, *u2; 318 u1 = (const struct user *)v1; 319 u2 = (const struct user *)v2; 320 321 return u2->space - u1->space; 322 } 323 324 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 325 cmpusers)) 326 327 static void 328 uses(uid_t uid, daddr_t blks, time_t act) 329 { 330 static time_t today; 331 struct user *usr; 332 333 if (!today) 334 time(&today); 335 336 usr = user(uid); 337 usr->count++; 338 usr->space += blks; 339 340 if (today - act > 90L * 24L * 60L * 60L) 341 usr->spc90 += blks; 342 if (today - act > 60L * 24L * 60L * 60L) 343 usr->spc60 += blks; 344 if (today - act > 30L * 24L * 60L * 60L) 345 usr->spc30 += blks; 346 } 347 348 #ifdef COMPAT 349 #define FSZCNT 500 350 #else 351 #define FSZCNT 512 352 #endif 353 struct fsizes { 354 struct fsizes *fsz_next; 355 daddr_t fsz_first, fsz_last; 356 ino_t fsz_count[FSZCNT]; 357 daddr_t fsz_sz[FSZCNT]; 358 } *fsizes; 359 360 static void 361 initfsizes(void) 362 { 363 struct fsizes *fp; 364 int i; 365 366 for (fp = fsizes; fp; fp = fp->fsz_next) { 367 for (i = FSZCNT; --i >= 0;) { 368 fp->fsz_count[i] = 0; 369 fp->fsz_sz[i] = 0; 370 } 371 } 372 } 373 374 static void 375 dofsizes(int fd, struct fs *super, char *name) 376 { 377 ino_t inode, maxino; 378 union dinode *dp; 379 daddr_t sz, ksz; 380 struct fsizes *fp, **fsp; 381 int i; 382 383 maxino = super->fs_ncg * super->fs_ipg - 1; 384 #ifdef COMPAT 385 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 386 errx(1, "allocate fsize structure"); 387 #endif /* COMPAT */ 388 for (inode = 0; inode < maxino; inode++) { 389 errno = 0; 390 if ((dp = get_inode(fd,super,inode)) 391 #ifdef COMPAT 392 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 393 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 394 #else /* COMPAT */ 395 && !isfree(super, dp) 396 #endif /* COMPAT */ 397 ) { 398 sz = estimate ? virtualblocks(super, dp) : 399 actualblocks(super, dp); 400 #ifdef COMPAT 401 if (sz >= FSZCNT) { 402 fsizes->fsz_count[FSZCNT-1]++; 403 fsizes->fsz_sz[FSZCNT-1] += sz; 404 } else { 405 fsizes->fsz_count[sz]++; 406 fsizes->fsz_sz[sz] += sz; 407 } 408 #else /* COMPAT */ 409 ksz = SIZE(sz); 410 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 411 if (ksz < fp->fsz_last) 412 break; 413 } 414 if (!fp || ksz < fp->fsz_first) { 415 if (!(fp = (struct fsizes *) 416 malloc(sizeof(struct fsizes)))) 417 errx(1, "allocate fsize structure"); 418 fp->fsz_next = *fsp; 419 *fsp = fp; 420 fp->fsz_first = rounddown(ksz, FSZCNT); 421 fp->fsz_last = fp->fsz_first + FSZCNT; 422 for (i = FSZCNT; --i >= 0;) { 423 fp->fsz_count[i] = 0; 424 fp->fsz_sz[i] = 0; 425 } 426 } 427 fp->fsz_count[ksz % FSZCNT]++; 428 fp->fsz_sz[ksz % FSZCNT] += sz; 429 #endif /* COMPAT */ 430 } else if (errno) { 431 err(1, "%s", name); 432 } 433 } 434 sz = 0; 435 for (fp = fsizes; fp; fp = fp->fsz_next) { 436 for (i = 0; i < FSZCNT; i++) { 437 if (fp->fsz_count[i]) 438 printf("%jd\t%jd\t%d\n", 439 (intmax_t)(fp->fsz_first + i), 440 (intmax_t)fp->fsz_count[i], 441 SIZE(sz += fp->fsz_sz[i])); 442 } 443 } 444 } 445 446 static void 447 douser(int fd, struct fs *super, char *name) 448 { 449 ino_t inode, maxino; 450 struct user *usr, *usrs; 451 union dinode *dp; 452 int n; 453 454 maxino = super->fs_ncg * super->fs_ipg - 1; 455 for (inode = 0; inode < maxino; inode++) { 456 errno = 0; 457 if ((dp = get_inode(fd,super,inode)) 458 && !isfree(super, dp)) 459 uses(DIP(super, dp, di_uid), 460 estimate ? virtualblocks(super, dp) : 461 actualblocks(super, dp), 462 DIP(super, dp, di_atime)); 463 else if (errno) { 464 err(1, "%s", name); 465 } 466 } 467 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 468 errx(1, "allocate users"); 469 bcopy(users,usrs,nusers * sizeof(struct user)); 470 sortusers(usrs); 471 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 472 printf("%5d",SIZE(usr->space)); 473 if (count) 474 printf("\t%5ld",usr->count); 475 printf("\t%-8s",usr->name); 476 if (unused) 477 printf("\t%5d\t%5d\t%5d", 478 SIZE(usr->spc30), 479 SIZE(usr->spc60), 480 SIZE(usr->spc90)); 481 printf("\n"); 482 } 483 free(usrs); 484 } 485 486 static void 487 donames(int fd, struct fs *super, char *name) 488 { 489 int c; 490 ino_t maxino; 491 uintmax_t inode; 492 union dinode *dp; 493 494 maxino = super->fs_ncg * super->fs_ipg - 1; 495 /* first skip the name of the filesystem */ 496 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 497 while ((c = getchar()) != EOF && c != '\n'); 498 ungetc(c,stdin); 499 while (scanf("%ju", &inode) == 1) { 500 if (inode > maxino) { 501 warnx("illegal inode %ju", inode); 502 return; 503 } 504 errno = 0; 505 if ((dp = get_inode(fd,super,inode)) 506 && !isfree(super, dp)) { 507 printf("%s\t",user(DIP(super, dp, di_uid))->name); 508 /* now skip whitespace */ 509 while ((c = getchar()) == ' ' || c == '\t'); 510 /* and print out the remainder of the input line */ 511 while (c != EOF && c != '\n') { 512 putchar(c); 513 c = getchar(); 514 } 515 putchar('\n'); 516 } else { 517 if (errno) { 518 err(1, "%s", name); 519 } 520 /* skip this line */ 521 while ((c = getchar()) != EOF && c != '\n'); 522 } 523 if (c == EOF) 524 break; 525 } 526 } 527 528 static void 529 usage(void) 530 { 531 #ifdef COMPAT 532 fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); 533 #else /* COMPAT */ 534 fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); 535 #endif /* COMPAT */ 536 exit(1); 537 } 538 539 void 540 quot(char *name, char *mp) 541 { 542 int fd; 543 struct fs *fs; 544 545 get_inode(-1, NULL, 0); /* flush cache */ 546 inituser(); 547 initfsizes(); 548 if ((fd = open(name,0)) < 0) { 549 warn("%s", name); 550 close(fd); 551 return; 552 } 553 switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) { 554 case 0: 555 break; 556 case ENOENT: 557 warn("Cannot find file system superblock"); 558 close(fd); 559 return; 560 default: 561 warn("Unable to read file system superblock"); 562 close(fd); 563 return; 564 } 565 printf("%s:",name); 566 if (mp) 567 printf(" (%s)",mp); 568 putchar('\n'); 569 (*func)(fd, fs, name); 570 free(fs); 571 close(fd); 572 } 573 574 int 575 main(int argc, char *argv[]) 576 { 577 char all = 0; 578 struct statfs *mp; 579 struct fstab *fs; 580 int cnt; 581 582 func = douser; 583 #ifndef COMPAT 584 header = getbsize(&headerlen,&blocksize); 585 #endif 586 while (--argc > 0 && **++argv == '-') { 587 while (*++*argv) { 588 switch (**argv) { 589 case 'n': 590 func = donames; 591 break; 592 case 'c': 593 func = dofsizes; 594 break; 595 case 'a': 596 all = 1; 597 break; 598 case 'f': 599 count = 1; 600 break; 601 case 'h': 602 estimate = 1; 603 break; 604 #ifndef COMPAT 605 case 'k': 606 blocksize = 1024; 607 break; 608 #endif /* COMPAT */ 609 case 'v': 610 unused = 1; 611 break; 612 default: 613 usage(); 614 } 615 } 616 } 617 if (all) { 618 cnt = getmntinfo(&mp,MNT_NOWAIT); 619 for (; --cnt >= 0; mp++) { 620 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) 621 quot(mp->f_mntfromname, mp->f_mntonname); 622 } 623 } 624 while (--argc >= 0) { 625 if ((fs = getfsfile(*argv)) != NULL) 626 quot(fs->fs_spec, 0); 627 else 628 quot(*argv,0); 629 argv++; 630 } 631 return 0; 632 } 633