quot.c revision 303975
1/* 2 * Copyright (C) 1991, 1994 Wolfgang Solfrank. 3 * Copyright (C) 1991, 1994 TooLs GmbH. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by TooLs GmbH. 17 * 4. The name of TooLs GmbH may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: releng/11.0/usr.sbin/quot/quot.c 298913 2016-05-02 02:13:22Z pfg $"); 34 35#include <sys/param.h> 36#include <sys/stdint.h> 37#include <sys/mount.h> 38#include <sys/disklabel.h> 39#include <ufs/ufs/dinode.h> 40#include <ufs/ffs/fs.h> 41 42#include <err.h> 43#include <fcntl.h> 44#include <fstab.h> 45#include <errno.h> 46#include <paths.h> 47#include <pwd.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <time.h> 52#include <unistd.h> 53 54/* some flags of what to do: */ 55static char estimate; 56static char count; 57static char unused; 58static void (*func)(int, struct fs *, char *); 59static long blocksize; 60static char *header; 61static int headerlen; 62 63static union dinode *get_inode(int, struct fs *, ino_t); 64static int virtualblocks(struct fs *, union dinode *); 65static int isfree(struct fs *, union dinode *); 66static void inituser(void); 67static void usrrehash(void); 68static struct user *user(uid_t); 69static int cmpusers(const void *, const void *); 70static void uses(uid_t, daddr_t, time_t); 71static void initfsizes(void); 72static void dofsizes(int, struct fs *, char *); 73static void douser(int, struct fs *, char *); 74static void donames(int, struct fs *, char *); 75static void usage(void); 76static void quot(char *, char *); 77 78/* 79 * Original BSD quot doesn't round to number of frags/blocks, 80 * doesn't account for indirection blocks and gets it totally 81 * wrong if the size is a multiple of the blocksize. 82 * The new code always counts the number of 512 byte blocks 83 * instead of the number of kilobytes and converts them to 84 * kByte when done (on request). 85 * 86 * Due to the size of modern disks, we must cast intermediate 87 * values to 64 bits to prevent potential overflows. 88 */ 89#ifdef COMPAT 90#define SIZE(n) (n) 91#else 92#define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) 93#endif 94 95#define INOCNT(fs) ((fs)->fs_ipg) 96#define INOSZ(fs) \ 97 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 98 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 99 100union dinode { 101 struct ufs1_dinode dp1; 102 struct ufs2_dinode dp2; 103}; 104#define DIP(fs, dp, field) \ 105 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 106 (dp)->dp1.field : (dp)->dp2.field) 107 108static union dinode * 109get_inode(int fd, struct fs *super, ino_t ino) 110{ 111 static caddr_t ipbuf; 112 static struct cg *cgp; 113 static ino_t last; 114 static int cg; 115 struct ufs2_dinode *di2; 116 117 if (fd < 0) { /* flush cache */ 118 if (ipbuf) { 119 free(ipbuf); 120 ipbuf = 0; 121 if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) { 122 free(cgp); 123 cgp = 0; 124 } 125 } 126 return 0; 127 } 128 129 if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 130 if (super->fs_magic == FS_UFS2_MAGIC && 131 (!cgp || cg != ino_to_cg(super, ino))) { 132 cg = ino_to_cg(super, ino); 133 if (!cgp && !(cgp = malloc(super->fs_cgsize))) 134 errx(1, "allocate cg"); 135 if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0) 136 err(1, "lseek cg"); 137 if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) 138 err(1, "read cg"); 139 if (!cg_chkmagic(cgp)) 140 errx(1, "cg has bad magic"); 141 } 142 if (!ipbuf 143 && !(ipbuf = malloc(INOSZ(super)))) 144 errx(1, "allocate inodes"); 145 last = rounddown(ino, INOCNT(super)); 146 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 147 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 148 err(1, "read inodes"); 149 } 150 151 if (super->fs_magic == FS_UFS1_MAGIC) 152 return ((union dinode *) 153 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 154 di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; 155 /* If the inode is unused, it might be unallocated too, so zero it. */ 156 if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) 157 bzero(di2, sizeof (*di2)); 158 return ((union dinode *)di2); 159} 160 161#ifdef COMPAT 162#define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) 163#else 164#define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 165#endif 166 167static int virtualblocks(struct fs *super, union dinode *dp) 168{ 169 off_t nblk, sz; 170 171 sz = DIP(super, dp, di_size); 172#ifdef COMPAT 173 if (lblkno(super,sz) >= NDADDR) { 174 nblk = blkroundup(super,sz); 175 if (sz == nblk) 176 nblk += super->fs_bsize; 177 } 178 179 return sz / 1024; 180 181#else /* COMPAT */ 182 183 if (lblkno(super,sz) >= NDADDR) { 184 nblk = blkroundup(super,sz); 185 sz = lblkno(super,nblk); 186 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 187 while (sz > 0) { 188 nblk += sz * super->fs_bsize; 189 /* sz - 1 rounded up */ 190 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 191 } 192 } else 193 nblk = fragroundup(super,sz); 194 195 return nblk / 512; 196#endif /* COMPAT */ 197} 198 199static int 200isfree(struct fs *super, union dinode *dp) 201{ 202#ifdef COMPAT 203 return (DIP(super, dp, di_mode) & IFMT) == 0; 204#else /* COMPAT */ 205 206 switch (DIP(super, dp, di_mode) & IFMT) { 207 case IFIFO: 208 case IFLNK: /* should check FASTSYMLINK? */ 209 case IFDIR: 210 case IFREG: 211 return 0; 212 case IFCHR: 213 case IFBLK: 214 case IFSOCK: 215 case IFWHT: 216 case 0: 217 return 1; 218 default: 219 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 220 } 221#endif 222} 223 224static struct user { 225 uid_t uid; 226 char *name; 227 daddr_t space; 228 long count; 229 daddr_t spc30; 230 daddr_t spc60; 231 daddr_t spc90; 232} *users; 233static int nusers; 234 235static void 236inituser(void) 237{ 238 int i; 239 struct user *usr; 240 241 if (!nusers) { 242 nusers = 8; 243 if (!(users = 244 (struct user *)calloc(nusers,sizeof(struct user)))) 245 errx(1, "allocate users"); 246 } else { 247 for (usr = users, i = nusers; --i >= 0; usr++) { 248 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 249 usr->count = 0; 250 } 251 } 252} 253 254static void 255usrrehash(void) 256{ 257 int i; 258 struct user *usr, *usrn; 259 struct user *svusr; 260 261 svusr = users; 262 nusers <<= 1; 263 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 264 errx(1, "allocate users"); 265 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 266 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 267 usrn--) { 268 if (usrn <= users) 269 usrn = users + nusers; 270 } 271 *usrn = *usr; 272 } 273} 274 275static struct user * 276user(uid_t uid) 277{ 278 struct user *usr; 279 int i; 280 struct passwd *pwd; 281 282 while (1) { 283 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 284 usr--) { 285 if (!usr->name) { 286 usr->uid = uid; 287 288 if (!(pwd = getpwuid(uid))) { 289 if ((usr->name = (char *)malloc(7))) 290 sprintf(usr->name,"#%d",uid); 291 } else { 292 if ((usr->name = (char *) 293 malloc(strlen(pwd->pw_name) + 1))) 294 strcpy(usr->name,pwd->pw_name); 295 } 296 if (!usr->name) 297 errx(1, "allocate users"); 298 299 return usr; 300 301 } else if (usr->uid == uid) 302 return usr; 303 304 if (usr <= users) 305 usr = users + nusers; 306 } 307 usrrehash(); 308 } 309} 310 311static int 312cmpusers(const void *v1, const void *v2) 313{ 314 const struct user *u1, *u2; 315 u1 = (const struct user *)v1; 316 u2 = (const struct user *)v2; 317 318 return u2->space - u1->space; 319} 320 321#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 322 cmpusers)) 323 324static void 325uses(uid_t uid, daddr_t blks, time_t act) 326{ 327 static time_t today; 328 struct user *usr; 329 330 if (!today) 331 time(&today); 332 333 usr = user(uid); 334 usr->count++; 335 usr->space += blks; 336 337 if (today - act > 90L * 24L * 60L * 60L) 338 usr->spc90 += blks; 339 if (today - act > 60L * 24L * 60L * 60L) 340 usr->spc60 += blks; 341 if (today - act > 30L * 24L * 60L * 60L) 342 usr->spc30 += blks; 343} 344 345#ifdef COMPAT 346#define FSZCNT 500 347#else 348#define FSZCNT 512 349#endif 350struct fsizes { 351 struct fsizes *fsz_next; 352 daddr_t fsz_first, fsz_last; 353 ino_t fsz_count[FSZCNT]; 354 daddr_t fsz_sz[FSZCNT]; 355} *fsizes; 356 357static void 358initfsizes(void) 359{ 360 struct fsizes *fp; 361 int i; 362 363 for (fp = fsizes; fp; fp = fp->fsz_next) { 364 for (i = FSZCNT; --i >= 0;) { 365 fp->fsz_count[i] = 0; 366 fp->fsz_sz[i] = 0; 367 } 368 } 369} 370 371static void 372dofsizes(int fd, struct fs *super, char *name) 373{ 374 ino_t inode, maxino; 375 union dinode *dp; 376 daddr_t sz, ksz; 377 struct fsizes *fp, **fsp; 378 int i; 379 380 maxino = super->fs_ncg * super->fs_ipg - 1; 381#ifdef COMPAT 382 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 383 errx(1, "allocate fsize structure"); 384#endif /* COMPAT */ 385 for (inode = 0; inode < maxino; inode++) { 386 errno = 0; 387 if ((dp = get_inode(fd,super,inode)) 388#ifdef COMPAT 389 && ((DIP(super, dp, di_mode) & IFMT) == IFREG 390 || (DIP(super, dp, di_mode) & IFMT) == IFDIR) 391#else /* COMPAT */ 392 && !isfree(super, dp) 393#endif /* COMPAT */ 394 ) { 395 sz = estimate ? virtualblocks(super, dp) : 396 actualblocks(super, dp); 397#ifdef COMPAT 398 if (sz >= FSZCNT) { 399 fsizes->fsz_count[FSZCNT-1]++; 400 fsizes->fsz_sz[FSZCNT-1] += sz; 401 } else { 402 fsizes->fsz_count[sz]++; 403 fsizes->fsz_sz[sz] += sz; 404 } 405#else /* COMPAT */ 406 ksz = SIZE(sz); 407 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 408 if (ksz < fp->fsz_last) 409 break; 410 } 411 if (!fp || ksz < fp->fsz_first) { 412 if (!(fp = (struct fsizes *) 413 malloc(sizeof(struct fsizes)))) 414 errx(1, "allocate fsize structure"); 415 fp->fsz_next = *fsp; 416 *fsp = fp; 417 fp->fsz_first = rounddown(ksz, FSZCNT); 418 fp->fsz_last = fp->fsz_first + FSZCNT; 419 for (i = FSZCNT; --i >= 0;) { 420 fp->fsz_count[i] = 0; 421 fp->fsz_sz[i] = 0; 422 } 423 } 424 fp->fsz_count[ksz % FSZCNT]++; 425 fp->fsz_sz[ksz % FSZCNT] += sz; 426#endif /* COMPAT */ 427 } else if (errno) { 428 err(1, "%s", name); 429 } 430 } 431 sz = 0; 432 for (fp = fsizes; fp; fp = fp->fsz_next) { 433 for (i = 0; i < FSZCNT; i++) { 434 if (fp->fsz_count[i]) 435 printf("%jd\t%jd\t%d\n", 436 (intmax_t)(fp->fsz_first + i), 437 (intmax_t)fp->fsz_count[i], 438 SIZE(sz += fp->fsz_sz[i])); 439 } 440 } 441} 442 443static void 444douser(int fd, struct fs *super, char *name) 445{ 446 ino_t inode, maxino; 447 struct user *usr, *usrs; 448 union dinode *dp; 449 int n; 450 451 maxino = super->fs_ncg * super->fs_ipg - 1; 452 for (inode = 0; inode < maxino; inode++) { 453 errno = 0; 454 if ((dp = get_inode(fd,super,inode)) 455 && !isfree(super, dp)) 456 uses(DIP(super, dp, di_uid), 457 estimate ? virtualblocks(super, dp) : 458 actualblocks(super, dp), 459 DIP(super, dp, di_atime)); 460 else if (errno) { 461 err(1, "%s", name); 462 } 463 } 464 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 465 errx(1, "allocate users"); 466 bcopy(users,usrs,nusers * sizeof(struct user)); 467 sortusers(usrs); 468 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 469 printf("%5d",SIZE(usr->space)); 470 if (count) 471 printf("\t%5ld",usr->count); 472 printf("\t%-8s",usr->name); 473 if (unused) 474 printf("\t%5d\t%5d\t%5d", 475 SIZE(usr->spc30), 476 SIZE(usr->spc60), 477 SIZE(usr->spc90)); 478 printf("\n"); 479 } 480 free(usrs); 481} 482 483static void 484donames(int fd, struct fs *super, char *name) 485{ 486 int c; 487 ino_t maxino; 488 uintmax_t inode; 489 union dinode *dp; 490 491 maxino = super->fs_ncg * super->fs_ipg - 1; 492 /* first skip the name of the filesystem */ 493 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 494 while ((c = getchar()) != EOF && c != '\n'); 495 ungetc(c,stdin); 496 while (scanf("%ju", &inode) == 1) { 497 if (inode > maxino) { 498 warnx("illegal inode %ju", inode); 499 return; 500 } 501 errno = 0; 502 if ((dp = get_inode(fd,super,inode)) 503 && !isfree(super, dp)) { 504 printf("%s\t",user(DIP(super, dp, di_uid))->name); 505 /* now skip whitespace */ 506 while ((c = getchar()) == ' ' || c == '\t'); 507 /* and print out the remainder of the input line */ 508 while (c != EOF && c != '\n') { 509 putchar(c); 510 c = getchar(); 511 } 512 putchar('\n'); 513 } else { 514 if (errno) { 515 err(1, "%s", name); 516 } 517 /* skip this line */ 518 while ((c = getchar()) != EOF && c != '\n'); 519 } 520 if (c == EOF) 521 break; 522 } 523} 524 525static void 526usage(void) 527{ 528#ifdef COMPAT 529 fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); 530#else /* COMPAT */ 531 fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); 532#endif /* COMPAT */ 533 exit(1); 534} 535 536/* 537 * Possible superblock locations ordered from most to least likely. 538 */ 539static int sblock_try[] = SBLOCKSEARCH; 540static char superblock[SBLOCKSIZE]; 541 542void 543quot(char *name, char *mp) 544{ 545 int i, fd; 546 struct fs *fs; 547 548 get_inode(-1, NULL, 0); /* flush cache */ 549 inituser(); 550 initfsizes(); 551 if ((fd = open(name,0)) < 0) { 552 warn("%s", name); 553 close(fd); 554 return; 555 } 556 for (i = 0; sblock_try[i] != -1; i++) { 557 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) { 558 close(fd); 559 return; 560 } 561 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) { 562 close(fd); 563 return; 564 } 565 fs = (struct fs *)superblock; 566 if ((fs->fs_magic == FS_UFS1_MAGIC || 567 (fs->fs_magic == FS_UFS2_MAGIC && 568 fs->fs_sblockloc == sblock_try[i])) && 569 fs->fs_bsize <= MAXBSIZE && 570 fs->fs_bsize >= sizeof(struct fs)) 571 break; 572 } 573 if (sblock_try[i] == -1) { 574 warnx("%s: not a BSD filesystem",name); 575 close(fd); 576 return; 577 } 578 printf("%s:",name); 579 if (mp) 580 printf(" (%s)",mp); 581 putchar('\n'); 582 (*func)(fd, fs, name); 583 close(fd); 584} 585 586int 587main(int argc, char *argv[]) 588{ 589 char all = 0; 590 struct statfs *mp; 591 struct fstab *fs; 592 int cnt; 593 594 func = douser; 595#ifndef COMPAT 596 header = getbsize(&headerlen,&blocksize); 597#endif 598 while (--argc > 0 && **++argv == '-') { 599 while (*++*argv) { 600 switch (**argv) { 601 case 'n': 602 func = donames; 603 break; 604 case 'c': 605 func = dofsizes; 606 break; 607 case 'a': 608 all = 1; 609 break; 610 case 'f': 611 count = 1; 612 break; 613 case 'h': 614 estimate = 1; 615 break; 616#ifndef COMPAT 617 case 'k': 618 blocksize = 1024; 619 break; 620#endif /* COMPAT */ 621 case 'v': 622 unused = 1; 623 break; 624 default: 625 usage(); 626 } 627 } 628 } 629 if (all) { 630 cnt = getmntinfo(&mp,MNT_NOWAIT); 631 for (; --cnt >= 0; mp++) { 632 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) 633 quot(mp->f_mntfromname, mp->f_mntonname); 634 } 635 } 636 while (--argc >= 0) { 637 if ((fs = getfsfile(*argv)) != NULL) 638 quot(fs->fs_spec, 0); 639 else 640 quot(*argv,0); 641 argv++; 642 } 643 return 0; 644} 645