1 /* $OpenBSD: quot.c,v 1.10 2000/11/21 04:23:09 millert Exp $ */ 2 /* $NetBSD: quot.c,v 1.7.4.1 1996/05/31 18:06:36 jtc Exp $ */ 3 4 /* 5 * Copyright (C) 1991, 1994 Wolfgang Solfrank. 6 * Copyright (C) 1991, 1994 TooLs GmbH. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static char rcsid[] = "$Id: quot.c,v 1.10 2000/11/21 04:23:09 millert Exp $"; 37 #endif /* not lint */ 38 39 #include <sys/param.h> 40 #include <sys/mount.h> 41 #include <sys/time.h> 42 #include <ufs/ffs/fs.h> 43 #include <ufs/ufs/quota.h> 44 #include <ufs/ufs/inode.h> 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <paths.h> 53 #include <pwd.h> 54 #include <unistd.h> 55 56 /* some flags of what to do: */ 57 static char estimate; 58 static char count; 59 static char unused; 60 static void (*func)(); 61 static long blocksize; 62 static char *header; 63 static int headerlen; 64 65 /* 66 * Original BSD quot doesn't round to number of frags/blocks, 67 * doesn't account for indirection blocks and gets it totally 68 * wrong if the size is a multiple of the blocksize. 69 * The new code always counts the number of DEV_BSIZE byte blocks 70 * instead of the number of kilobytes and converts them to 71 * KByte when done (on request). 72 */ 73 #ifdef COMPAT 74 #define SIZE(n) (n) 75 #else 76 #define SIZE(n) (howmany(((off_t)(n)) * DEV_BSIZE, blocksize)) 77 #endif 78 79 #define INOCNT(fs) ((fs)->fs_ipg) 80 #define INOSZ(fs) (sizeof(struct dinode) * INOCNT(fs)) 81 82 static struct dinode * 83 get_inode(fd, super, ino) 84 int fd; 85 struct fs *super; 86 ino_t ino; 87 { 88 static struct dinode *ip; 89 static ino_t last; 90 91 if (fd < 0) { /* flush cache */ 92 if (ip) { 93 free(ip); 94 ip = 0; 95 } 96 return 0; 97 } 98 99 if (!ip || ino < last || ino >= last + INOCNT(super)) { 100 if (!ip && !(ip = (struct dinode *)malloc(INOSZ(super)))) 101 err(1, "allocate inodes"); 102 last = (ino / INOCNT(super)) * INOCNT(super); 103 if (lseek(fd, 104 (off_t)ino_to_fsba(super, last) << super->fs_fshift, 105 0) < 0 106 || read(fd, ip, INOSZ(super)) != INOSZ(super)) { 107 err(1, "read inodes"); 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 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 int i; 188 struct user *usr; 189 190 if (!nusers) { 191 nusers = 8; 192 if (!(users = 193 (struct user *)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() 206 { 207 int i; 208 struct user *usr, *usrn; 209 struct user *svusr; 210 211 svusr = users; 212 nusers <<= 1; 213 if (!(users = (struct user *)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) 228 uid_t uid; 229 { 230 int i; 231 struct passwd *pwd; 232 struct user *usr; 233 234 while (1) { 235 for (usr = users + (uid&(nusers - 1)), i = nusers; 236 --i >= 0; 237 usr--) { 238 if (!usr->name) { 239 usr->uid = uid; 240 241 if (!(pwd = getpwuid(uid))) 242 asprintf(&usr->name, "#%u", uid); 243 else 244 usr->name = strdup(pwd->pw_name); 245 if (!usr->name) 246 err(1, "allocate users"); 247 return usr; 248 } else if (usr->uid == uid) 249 return usr; 250 251 if (usr <= users) 252 usr = users + nusers; 253 } 254 usrrehash(); 255 } 256 } 257 258 static int 259 cmpusers(u1, u2) 260 struct user *u1, *u2; 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, blks, act) 270 uid_t uid; 271 daddr_t blks; 272 time_t act; 273 { 274 static time_t today; 275 struct user *usr; 276 277 if (!today) 278 time(&today); 279 280 usr = user(uid); 281 usr->count++; 282 usr->space += blks; 283 284 if (today - act > 90L * 24L * 60L * 60L) 285 usr->spc90 += blks; 286 if (today - act > 60L * 24L * 60L * 60L) 287 usr->spc60 += blks; 288 if (today - act > 30L * 24L * 60L * 60L) 289 usr->spc30 += blks; 290 } 291 292 #ifdef COMPAT 293 #define FSZCNT 500 294 #else 295 #define FSZCNT 512 296 #endif 297 struct fsizes { 298 struct fsizes *fsz_next; 299 daddr_t fsz_first, fsz_last; 300 ino_t fsz_count[FSZCNT]; 301 daddr_t fsz_sz[FSZCNT]; 302 } *fsizes; 303 304 static void 305 initfsizes() 306 { 307 struct fsizes *fp; 308 int i; 309 310 for (fp = fsizes; fp; fp = fp->fsz_next) { 311 for (i = FSZCNT; --i >= 0;) { 312 fp->fsz_count[i] = 0; 313 fp->fsz_sz[i] = 0; 314 } 315 } 316 } 317 318 static void 319 dofsizes(fd, super, name) 320 int fd; 321 struct fs *super; 322 char *name; 323 { 324 ino_t inode, maxino; 325 struct dinode *ip; 326 daddr_t sz, ksz; 327 struct fsizes *fp, **fsp; 328 int i; 329 330 maxino = super->fs_ncg * super->fs_ipg - 1; 331 #ifdef COMPAT 332 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 333 err(1, "alloc fsize structure"); 334 #endif /* COMPAT */ 335 for (inode = 0; inode < maxino; inode++) { 336 errno = 0; 337 if ((ip = get_inode(fd, super, inode)) 338 #ifdef COMPAT 339 && ((ip->di_mode&IFMT) == IFREG 340 || (ip->di_mode&IFMT) == IFDIR) 341 #else /* COMPAT */ 342 && !isfree(ip) 343 #endif /* COMPAT */ 344 ) { 345 sz = estimate ? virtualblocks(super, ip) : 346 actualblocks(super, ip); 347 #ifdef COMPAT 348 if (sz >= FSZCNT) { 349 fsizes->fsz_count[FSZCNT-1]++; 350 fsizes->fsz_sz[FSZCNT-1] += sz; 351 } else { 352 fsizes->fsz_count[sz]++; 353 fsizes->fsz_sz[sz] += sz; 354 } 355 #else /* COMPAT */ 356 ksz = SIZE(sz); 357 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 358 if (ksz < fp->fsz_last) 359 break; 360 } 361 if (!fp || ksz < fp->fsz_first) { 362 if (!(fp = (struct fsizes *) 363 malloc(sizeof(struct fsizes)))) { 364 err(1, "alloc fsize structure"); 365 } 366 fp->fsz_next = *fsp; 367 *fsp = fp; 368 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 369 fp->fsz_last = fp->fsz_first + FSZCNT; 370 for (i = FSZCNT; --i >= 0;) { 371 fp->fsz_count[i] = 0; 372 fp->fsz_sz[i] = 0; 373 } 374 } 375 fp->fsz_count[ksz % FSZCNT]++; 376 fp->fsz_sz[ksz % FSZCNT] += sz; 377 #endif /* COMPAT */ 378 } else if (errno) 379 err(1, "%s", name); 380 } 381 sz = 0; 382 for (fp = fsizes; fp; fp = fp->fsz_next) { 383 for (i = 0; i < FSZCNT; i++) { 384 if (fp->fsz_count[i]) 385 printf("%d\t%d\t%qd\n", 386 fp->fsz_first + i, fp->fsz_count[i], 387 (quad_t) SIZE(sz += fp->fsz_sz[i])); 388 } 389 } 390 } 391 392 static void 393 douser(fd, super, name) 394 int fd; 395 struct fs *super; 396 char *name; 397 { 398 ino_t inode, maxino; 399 struct user *usr, *usrs; 400 struct dinode *ip; 401 int n; 402 403 maxino = super->fs_ncg * super->fs_ipg - 1; 404 for (inode = 0; inode < maxino; inode++) { 405 errno = 0; 406 if ((ip = get_inode(fd, super, inode)) 407 && !isfree(ip)) 408 uses(ip->di_uid, 409 estimate ? virtualblocks(super, ip) 410 : actualblocks(super, ip), 411 ip->di_atime); 412 else if (errno) 413 err(1, "%s", name); 414 } 415 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 416 err(1, "allocate users"); 417 memcpy(usrs, users, nusers * sizeof(struct user)); 418 sortusers(usrs); 419 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 420 printf("%5qd", (quad_t) SIZE(usr->space)); 421 if (count) 422 printf("\t%5ld", usr->count); 423 printf("\t%-8s", usr->name); 424 if (unused) 425 printf("\t%5qd\t%5qd\t%5qd", 426 (quad_t) SIZE(usr->spc30), 427 (quad_t) SIZE(usr->spc60), 428 (quad_t) SIZE(usr->spc90)); 429 printf("\n"); 430 } 431 free(usrs); 432 } 433 434 static void 435 donames(fd, super, name) 436 int fd; 437 struct fs *super; 438 char *name; 439 { 440 int c; 441 ino_t inode, inode1; 442 ino_t maxino; 443 struct dinode *ip; 444 445 maxino = super->fs_ncg * super->fs_ipg - 1; 446 /* first skip the name of the filesystem */ 447 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 448 while ((c = getchar()) != EOF && c != '\n'); 449 ungetc(c, stdin); 450 inode1 = -1; 451 while (scanf("%d", &inode) == 1) { 452 if (inode < 0 || inode > maxino) { 453 #ifndef COMPAT 454 fprintf(stderr, "invalid inode %d\n", inode); 455 #endif 456 return; 457 } 458 #ifdef COMPAT 459 if (inode < inode1) 460 continue; 461 #endif 462 errno = 0; 463 if ((ip = get_inode(fd, super, inode)) 464 && !isfree(ip)) { 465 printf("%s\t", user(ip->di_uid)->name); 466 /* now skip whitespace */ 467 while ((c = getchar()) == ' ' || c == '\t'); 468 /* and print out the remainder of the input line */ 469 while (c != EOF && c != '\n') { 470 putchar(c); 471 c = getchar(); 472 } 473 putchar('\n'); 474 inode1 = inode; 475 } else { 476 if (errno) 477 err(1, "%s", name); 478 /* skip this line */ 479 while ((c = getchar()) != EOF && c != '\n') 480 ; 481 } 482 if (c == EOF) 483 break; 484 } 485 } 486 487 static void 488 usage() 489 { 490 #ifdef COMPAT 491 fprintf(stderr, "Usage: quot [-nfcvha] [filesystem ...]\n"); 492 #else /* COMPAT */ 493 fprintf(stderr, "Usage: quot [ -acfhknv ] [ filesystem ... ]\n"); 494 #endif /* COMPAT */ 495 exit(1); 496 } 497 498 static char superblock[SBSIZE]; 499 500 #define max(a,b) MAX((a),(b)) 501 /* 502 * Sanity checks for old file systems. 503 * Stolen from <sys/lib/libsa/ufs.c> 504 */ 505 static void 506 ffs_oldfscompat(fs) 507 struct fs *fs; 508 { 509 int i; 510 511 fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ 512 fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ 513 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ 514 fs->fs_nrpos = 8; /* XXX */ 515 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ 516 quad_t sizepb = fs->fs_bsize; /* XXX */ 517 /* XXX */ 518 fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */ 519 for (i = 0; i < NIADDR; i++) { /* XXX */ 520 sizepb *= NINDIR(fs); /* XXX */ 521 fs->fs_maxfilesize += sizepb; /* XXX */ 522 } /* XXX */ 523 fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ 524 fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ 525 } /* XXX */ 526 } 527 528 void 529 quot(name, mp) 530 char *name, *mp; 531 { 532 int fd; 533 534 get_inode(-1); /* flush cache */ 535 inituser(); 536 initfsizes(); 537 /* 538 * XXX this is completely broken. Of course you can't read a 539 * directory, well, not anymore. How to fix this, though... 540 */ 541 if ((fd = open(name, 0)) < 0) { 542 warn("%s", name); 543 return; 544 } 545 if (lseek(fd, SBOFF, 0) != SBOFF 546 || read(fd, superblock, SBSIZE) != SBSIZE 547 || ((struct fs *)superblock)->fs_magic != FS_MAGIC 548 || ((struct fs *)superblock)->fs_bsize > MAXBSIZE 549 || ((struct fs *)superblock)->fs_bsize < sizeof(struct fs)) { 550 warnx("%s: not a BSD filesystem", name); 551 close(fd); 552 return; 553 } 554 ffs_oldfscompat(superblock); 555 printf("%s:", name); 556 if (mp) 557 printf(" (%s)", mp); 558 putchar('\n'); 559 (*func)(fd, superblock, name); 560 close(fd); 561 } 562 563 int 564 main(argc, argv) 565 int argc; 566 char **argv; 567 { 568 int cnt, all, i; 569 char dev[MNAMELEN], *nm, *mountpoint, *cp; 570 struct statfs *mp; 571 572 all = 0; 573 func = douser; 574 #ifndef COMPAT 575 header = getbsize(&headerlen, &blocksize); 576 #endif 577 while (--argc > 0 && **++argv == '-') { 578 while (*++*argv) { 579 switch (**argv) { 580 case 'n': 581 func = donames; 582 break; 583 case 'c': 584 func = dofsizes; 585 break; 586 case 'a': 587 all = 1; 588 break; 589 case 'f': 590 count = 1; 591 break; 592 case 'h': 593 estimate = 1; 594 break; 595 #ifndef COMPAT 596 case 'k': 597 blocksize = 1024; 598 break; 599 #endif /* COMPAT */ 600 case 'v': 601 unused = 1; 602 break; 603 default: 604 usage(); 605 } 606 } 607 } 608 cnt = getmntinfo(&mp, MNT_NOWAIT); 609 if (all) { 610 for (; --cnt >= 0; mp++) { 611 if (strcmp(mp->f_fstypename, MOUNT_FFS) == 0 || 612 strcmp(mp->f_fstypename, "ufs") == 0) { 613 if ((nm = strrchr(mp->f_mntfromname, '/'))) { 614 snprintf(dev, sizeof(dev), "%sr%s", 615 _PATH_DEV, nm + 1); 616 nm = dev; 617 } else 618 nm = mp->f_mntfromname; 619 quot(nm, mp->f_mntonname); 620 } 621 } 622 } 623 for (; --argc >= 0; argv++) { 624 mountpoint = NULL; 625 nm = *argv; 626 627 /* Remove trailing slashes from name. */ 628 cp = nm + strlen(nm); 629 while (*(--cp) == '/' && cp != nm) 630 *cp = '\0'; 631 632 /* Look up the name in the mount table. */ 633 for (i = 0; i < cnt; i++) { 634 /* Remove trailing slashes from name. */ 635 cp = mp[i].f_mntonname + strlen(mp[i].f_mntonname); 636 while (*(--cp) == '/' && cp != mp[i].f_mntonname) 637 *cp = '\0'; 638 639 if ((!strcmp(mp->f_fstypename, MOUNT_FFS) || 640 !strcmp(mp->f_fstypename, MOUNT_MFS) || 641 !strcmp(mp->f_fstypename, "ufs")) && 642 strcmp(nm, mp[i].f_mntonname) == 0) { 643 nm = mp[i].f_mntfromname; 644 mountpoint = mp[i].f_mntonname; 645 break; 646 } 647 } 648 649 /* Make sure we have the raw device... */ 650 if (strncmp(nm, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0 && 651 nm[sizeof(_PATH_DEV) - 1] != 'r') { 652 snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV, 653 nm + sizeof(_PATH_DEV) - 1); 654 nm = dev; 655 } 656 quot(nm, mountpoint); 657 } 658 exit(0); 659 } 660