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