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