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