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/param.h> 35 #include <sys/stdint.h> 36 #include <sys/mount.h> 37 #include <sys/disklabel.h> 38 #include <ufs/ufs/dinode.h> 39 #include <ufs/ffs/fs.h> 40 41 #include <err.h> 42 #include <fcntl.h> 43 #include <fstab.h> 44 #include <errno.h> 45 #include <libufs.h> 46 #include <paths.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <time.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(int fd, struct fs *super, ino_t ino) 110 { 111 static caddr_t ipbuf; 112 static struct cg *cgp; 113 static ino_t last; 114 static int cg; 115 struct ufs2_dinode *di2; 116 117 if (fd < 0) { /* flush cache */ 118 if (ipbuf) { 119 free(ipbuf); 120 ipbuf = 0; 121 if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) { 122 free(cgp); 123 cgp = 0; 124 } 125 } 126 return 0; 127 } 128 129 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 130 if (super->fs_magic == FS_UFS2_MAGIC && 131 (!cgp || cg != ino_to_cg(super, ino))) { 132 cg = ino_to_cg(super, ino); 133 if (!cgp && !(cgp = malloc(super->fs_cgsize))) 134 errx(1, "allocate cg"); 135 if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0) 136 err(1, "lseek cg"); 137 if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) 138 err(1, "read cg"); 139 if (!cg_chkmagic(cgp)) 140 errx(1, "cg has bad magic"); 141 } 142 if (!ipbuf 143 && !(ipbuf = malloc(INOSZ(super)))) 144 errx(1, "allocate inodes"); 145 last = rounddown(ino, INOCNT(super)); 146 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 147 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 148 err(1, "read inodes"); 149 } 150 151 if (super->fs_magic == FS_UFS1_MAGIC) 152 return ((union dinode *) 153 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 154 di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; 155 /* If the inode is unused, it might be unallocated too, so zero it. */ 156 if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) 157 bzero(di2, sizeof (*di2)); 158 return ((union dinode *)di2); 159 } 160 161 #ifdef COMPAT 162 #define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) 163 #else 164 #define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 165 #endif 166 167 static int virtualblocks(struct fs *super, union dinode *dp) 168 { 169 off_t nblk, sz; 170 171 sz = DIP(super, dp, di_size); 172 #ifdef COMPAT 173 if (lblkno(super,sz) >= UFS_NDADDR) { 174 nblk = blkroundup(super,sz); 175 if (sz == nblk) 176 nblk += super->fs_bsize; 177 } 178 179 return sz / 1024; 180 181 #else /* COMPAT */ 182 183 if (lblkno(super,sz) >= UFS_NDADDR) { 184 nblk = blkroundup(super,sz); 185 sz = lblkno(super,nblk); 186 sz = (sz - UFS_NDADDR + NINDIR(super) - 1) / NINDIR(super); 187 while (sz > 0) { 188 nblk += sz * super->fs_bsize; 189 /* sz - 1 rounded up */ 190 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 191 } 192 } else 193 nblk = fragroundup(super,sz); 194 195 return nblk / 512; 196 #endif /* COMPAT */ 197 } 198 199 static int 200 isfree(struct fs *super, union dinode *dp) 201 { 202 #ifdef COMPAT 203 return (DIP(super, dp, di_mode) & IFMT) == 0; 204 #else /* COMPAT */ 205 206 switch (DIP(super, dp, di_mode) & IFMT) { 207 case IFIFO: 208 case IFLNK: /* should check FASTSYMLINK? */ 209 case IFDIR: 210 case IFREG: 211 return 0; 212 case IFCHR: 213 case IFBLK: 214 case IFSOCK: 215 case IFWHT: 216 case 0: 217 return 1; 218 default: 219 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 220 } 221 #endif 222 } 223 224 static struct user { 225 uid_t uid; 226 char *name; 227 daddr_t space; 228 long count; 229 daddr_t spc30; 230 daddr_t spc60; 231 daddr_t spc90; 232 } *users; 233 static int nusers; 234 235 static void 236 inituser(void) 237 { 238 int i; 239 struct user *usr; 240 241 if (!nusers) { 242 nusers = 8; 243 if (!(users = 244 (struct user *)calloc(nusers,sizeof(struct user)))) 245 errx(1, "allocate users"); 246 } else { 247 for (usr = users, i = nusers; --i >= 0; usr++) { 248 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 249 usr->count = 0; 250 } 251 } 252 } 253 254 static void 255 usrrehash(void) 256 { 257 int i; 258 struct user *usr, *usrn; 259 struct user *svusr; 260 261 svusr = users; 262 nusers <<= 1; 263 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 264 errx(1, "allocate users"); 265 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 266 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 267 usrn--) { 268 if (usrn <= users) 269 usrn = users + nusers; 270 } 271 *usrn = *usr; 272 } 273 } 274 275 static struct user * 276 user(uid_t uid) 277 { 278 struct user *usr; 279 int i; 280 struct passwd *pwd; 281 282 while (1) { 283 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 284 usr--) { 285 if (!usr->name) { 286 usr->uid = uid; 287 288 if (!(pwd = getpwuid(uid))) { 289 if ((usr->name = (char *)malloc(7))) 290 sprintf(usr->name,"#%d",uid); 291 } else { 292 if ((usr->name = (char *) 293 malloc(strlen(pwd->pw_name) + 1))) 294 strcpy(usr->name,pwd->pw_name); 295 } 296 if (!usr->name) 297 errx(1, "allocate users"); 298 299 return usr; 300 301 } else if (usr->uid == uid) 302 return usr; 303 304 if (usr <= users) 305 usr = users + nusers; 306 } 307 usrrehash(); 308 } 309 } 310 311 static int 312 cmpusers(const void *v1, const void *v2) 313 { 314 const struct user *u1, *u2; 315 u1 = (const struct user *)v1; 316 u2 = (const struct user *)v2; 317 318 return u2->space - u1->space; 319 } 320 321 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 322 cmpusers)) 323 324 static void 325 uses(uid_t uid, daddr_t blks, time_t act) 326 { 327 static time_t today; 328 struct user *usr; 329 330 if (!today) 331 time(&today); 332 333 usr = user(uid); 334 usr->count++; 335 usr->space += blks; 336 337 if (today - act > 90L * 24L * 60L * 60L) 338 usr->spc90 += blks; 339 if (today - act > 60L * 24L * 60L * 60L) 340 usr->spc60 += blks; 341 if (today - act > 30L * 24L * 60L * 60L) 342 usr->spc30 += blks; 343 } 344 345 #ifdef COMPAT 346 #define FSZCNT 500 347 #else 348 #define FSZCNT 512 349 #endif 350 struct fsizes { 351 struct fsizes *fsz_next; 352 daddr_t fsz_first, fsz_last; 353 ino_t fsz_count[FSZCNT]; 354 daddr_t fsz_sz[FSZCNT]; 355 } *fsizes; 356 357 static void 358 initfsizes(void) 359 { 360 struct fsizes *fp; 361 int i; 362 363 for (fp = fsizes; fp; fp = fp->fsz_next) { 364 for (i = FSZCNT; --i >= 0;) { 365 fp->fsz_count[i] = 0; 366 fp->fsz_sz[i] = 0; 367 } 368 } 369 } 370 371 static void 372 dofsizes(int fd, struct fs *super, char *name) 373 { 374 ino_t inode, maxino; 375 union dinode *dp; 376 daddr_t sz, ksz; 377 struct fsizes *fp, **fsp; 378 int i; 379 380 maxino = super->fs_ncg * super->fs_ipg - 1; 381 #ifdef COMPAT 382 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 383 errx(1, "allocate fsize structure"); 384 #endif /* COMPAT */ 385 for (inode = 0; inode < maxino; inode++) { 386 errno = 0; 387 if ((dp = get_inode(fd,super,inode)) 388 #ifdef COMPAT 389 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 390 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 391 #else /* COMPAT */ 392 && !isfree(super, dp) 393 #endif /* COMPAT */ 394 ) { 395 sz = estimate ? virtualblocks(super, dp) : 396 actualblocks(super, dp); 397 #ifdef COMPAT 398 if (sz >= FSZCNT) { 399 fsizes->fsz_count[FSZCNT-1]++; 400 fsizes->fsz_sz[FSZCNT-1] += sz; 401 } else { 402 fsizes->fsz_count[sz]++; 403 fsizes->fsz_sz[sz] += sz; 404 } 405 #else /* COMPAT */ 406 ksz = SIZE(sz); 407 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 408 if (ksz < fp->fsz_last) 409 break; 410 } 411 if (!fp || ksz < fp->fsz_first) { 412 if (!(fp = (struct fsizes *) 413 malloc(sizeof(struct fsizes)))) 414 errx(1, "allocate fsize structure"); 415 fp->fsz_next = *fsp; 416 *fsp = fp; 417 fp->fsz_first = rounddown(ksz, FSZCNT); 418 fp->fsz_last = fp->fsz_first + FSZCNT; 419 for (i = FSZCNT; --i >= 0;) { 420 fp->fsz_count[i] = 0; 421 fp->fsz_sz[i] = 0; 422 } 423 } 424 fp->fsz_count[ksz % FSZCNT]++; 425 fp->fsz_sz[ksz % FSZCNT] += sz; 426 #endif /* COMPAT */ 427 } else if (errno) { 428 err(1, "%s", name); 429 } 430 } 431 sz = 0; 432 for (fp = fsizes; fp; fp = fp->fsz_next) { 433 for (i = 0; i < FSZCNT; i++) { 434 if (fp->fsz_count[i]) 435 printf("%jd\t%jd\t%d\n", 436 (intmax_t)(fp->fsz_first + i), 437 (intmax_t)fp->fsz_count[i], 438 SIZE(sz += fp->fsz_sz[i])); 439 } 440 } 441 } 442 443 static void 444 douser(int fd, struct fs *super, char *name) 445 { 446 ino_t inode, maxino; 447 struct user *usr, *usrs; 448 union dinode *dp; 449 int n; 450 451 maxino = super->fs_ncg * super->fs_ipg - 1; 452 for (inode = 0; inode < maxino; inode++) { 453 errno = 0; 454 if ((dp = get_inode(fd,super,inode)) 455 && !isfree(super, dp)) 456 uses(DIP(super, dp, di_uid), 457 estimate ? virtualblocks(super, dp) : 458 actualblocks(super, dp), 459 DIP(super, dp, di_atime)); 460 else if (errno) { 461 err(1, "%s", name); 462 } 463 } 464 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 465 errx(1, "allocate users"); 466 bcopy(users,usrs,nusers * sizeof(struct user)); 467 sortusers(usrs); 468 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 469 printf("%5d",SIZE(usr->space)); 470 if (count) 471 printf("\t%5ld",usr->count); 472 printf("\t%-8s",usr->name); 473 if (unused) 474 printf("\t%5d\t%5d\t%5d", 475 SIZE(usr->spc30), 476 SIZE(usr->spc60), 477 SIZE(usr->spc90)); 478 printf("\n"); 479 } 480 free(usrs); 481 } 482 483 static void 484 donames(int fd, struct fs *super, char *name) 485 { 486 int c; 487 ino_t maxino; 488 uintmax_t inode; 489 union dinode *dp; 490 491 maxino = super->fs_ncg * super->fs_ipg - 1; 492 /* first skip the name of the filesystem */ 493 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 494 while ((c = getchar()) != EOF && c != '\n'); 495 ungetc(c,stdin); 496 while (scanf("%ju", &inode) == 1) { 497 if (inode > maxino) { 498 warnx("illegal inode %ju", inode); 499 return; 500 } 501 errno = 0; 502 if ((dp = get_inode(fd,super,inode)) 503 && !isfree(super, dp)) { 504 printf("%s\t",user(DIP(super, dp, di_uid))->name); 505 /* now skip whitespace */ 506 while ((c = getchar()) == ' ' || c == '\t'); 507 /* and print out the remainder of the input line */ 508 while (c != EOF && c != '\n') { 509 putchar(c); 510 c = getchar(); 511 } 512 putchar('\n'); 513 } else { 514 if (errno) { 515 err(1, "%s", name); 516 } 517 /* skip this line */ 518 while ((c = getchar()) != EOF && c != '\n'); 519 } 520 if (c == EOF) 521 break; 522 } 523 } 524 525 static void 526 usage(void) 527 { 528 #ifdef COMPAT 529 fprintf(stderr, "usage: quot [-cfhnv] [-a | filesystem ...]\n"); 530 #else /* COMPAT */ 531 fprintf(stderr, "usage: quot [-cfhknv] [-a | filesystem ...]\n"); 532 #endif /* COMPAT */ 533 exit(1); 534 } 535 536 void 537 quot(char *name, char *mp) 538 { 539 int 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 switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) { 551 case 0: 552 break; 553 case ENOENT: 554 warn("Cannot find file system superblock"); 555 close(fd); 556 return; 557 default: 558 warn("Unable to read file system superblock"); 559 close(fd); 560 return; 561 } 562 printf("%s:",name); 563 if (mp) 564 printf(" (%s)",mp); 565 putchar('\n'); 566 (*func)(fd, fs, name); 567 free(fs); 568 close(fd); 569 } 570 571 int 572 main(int argc, char *argv[]) 573 { 574 char all = 0; 575 struct statfs *mp; 576 struct fstab *fs; 577 int cnt; 578 int ch; 579 580 func = douser; 581 #ifndef COMPAT 582 header = getbsize(&headerlen,&blocksize); 583 #endif 584 while ((ch = getopt(argc, argv, "acfhknv")) != -1) { 585 switch (ch) { 586 case 'a': 587 all = 1; 588 break; 589 case 'c': 590 func = dofsizes; 591 break; 592 case 'f': 593 count = 1; 594 break; 595 case 'h': 596 estimate = 1; 597 break; 598 #ifndef COMPAT 599 case 'k': 600 blocksize = 1024; 601 break; 602 #endif /* COMPAT */ 603 case 'n': 604 func = donames; 605 break; 606 case 'v': 607 unused = 1; 608 break; 609 default: 610 usage(); 611 } 612 } 613 argc -= optind; 614 argv += optind; 615 616 if ((argc == 0 && !all) || (all && argc)) 617 usage(); 618 619 if (all) { 620 cnt = getmntinfo(&mp,MNT_NOWAIT); 621 for (; --cnt >= 0; mp++) { 622 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) 623 quot(mp->f_mntfromname, mp->f_mntonname); 624 } 625 } 626 while (--argc >= 0) { 627 if ((fs = getfsfile(*argv)) != NULL) 628 quot(fs->fs_spec, 0); 629 else 630 quot(*argv,0); 631 argv++; 632 } 633 return 0; 634 } 635