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