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