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