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