quot.c revision 98542
1139749Simp/* 266550Snyan * Copyright (C) 1991, 1994 Wolfgang Solfrank. 366550Snyan * Copyright (C) 1991, 1994 TooLs GmbH. 466550Snyan * All rights reserved. 566550Snyan * 666550Snyan * Redistribution and use in source and binary forms, with or without 766550Snyan * modification, are permitted provided that the following conditions 866550Snyan * are met: 966550Snyan * 1. Redistributions of source code must retain the above copyright 1066550Snyan * notice, this list of conditions and the following disclaimer. 1166550Snyan * 2. Redistributions in binary form must reproduce the above copyright 1266550Snyan * notice, this list of conditions and the following disclaimer in the 1366550Snyan * documentation and/or other materials provided with the distribution. 1466550Snyan * 3. All advertising materials mentioning features or use of this software 1566550Snyan * must display the following acknowledgement: 1666550Snyan * This product includes software developed by TooLs GmbH. 1766550Snyan * 4. The name of TooLs GmbH may not be used to endorse or promote products 1866550Snyan * derived from this software without specific prior written permission. 1966550Snyan * 2066550Snyan * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2166550Snyan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2266550Snyan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2366550Snyan * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2466550Snyan * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2566550Snyan * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 2666550Snyan * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2766550Snyan * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 2866550Snyan * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29119419Sobrien * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30119419Sobrien */ 31119419Sobrien 3266550Snyan#ifndef lint 3366550Snyanstatic const char rcsid[] = 3466550Snyan "$FreeBSD: head/usr.sbin/quot/quot.c 98542 2002-06-21 06:18:05Z mckusick $"; 3566550Snyan#endif /* not lint */ 3666550Snyan 3766550Snyan#include <sys/param.h> 3866550Snyan#include <sys/stdint.h> 3966550Snyan#include <sys/mount.h> 4066550Snyan#include <sys/disklabel.h> 4166550Snyan#include <sys/time.h> 4266550Snyan#include <ufs/ufs/dinode.h> 4366550Snyan#include <ufs/ffs/fs.h> 4466550Snyan 4566550Snyan#include <err.h> 4666550Snyan#include <fcntl.h> 4766550Snyan#include <fstab.h> 4866550Snyan#include <errno.h> 4966550Snyan#include <paths.h> 5066550Snyan#include <pwd.h> 5166550Snyan#include <stdio.h> 5266550Snyan#include <stdlib.h> 5366550Snyan#include <string.h> 5466550Snyan#include <unistd.h> 5566550Snyan 5666550Snyan/* some flags of what to do: */ 5766550Snyanstatic char estimate; 5866550Snyanstatic char count; 5966550Snyanstatic char unused; 6066550Snyanstatic void (*func)(int, struct fs *, char *); 6166550Snyanstatic long blocksize; 6266550Snyanstatic char *header; 6366550Snyanstatic int headerlen; 6466550Snyan 6566550Snyanstatic union dinode *get_inode(int, struct fs *, ino_t); 6666550Snyanstatic int virtualblocks(struct fs *, union dinode *); 6766550Snyanstatic int isfree(struct fs *, union dinode *); 6866550Snyanstatic void inituser(void); 69242871Snyanstatic void usrrehash(void); 7066550Snyanstatic struct user *user(uid_t); 7166550Snyanstatic int cmpusers(const void *, const void *); 7266550Snyanstatic void uses(uid_t, daddr_t, time_t); 7366550Snyanstatic void initfsizes(void); 7466550Snyanstatic void dofsizes(int, struct fs *, char *); 7566550Snyanstatic void douser(int, struct fs *, char *); 7666550Snyanstatic void donames(int, struct fs *, char *); 7766550Snyanstatic void usage(void); 7866550Snyanstatic void quot(char *, char *); 7966550Snyan 8066550Snyan/* 8166550Snyan * Original BSD quot doesn't round to number of frags/blocks, 8266550Snyan * doesn't account for indirection blocks and gets it totally 8366581Snyan * wrong if the size is a multiple of the blocksize. 8466550Snyan * The new code always counts the number of 512 byte blocks 8566550Snyan * instead of the number of kilobytes and converts them to 8666550Snyan * kByte when done (on request). 8766550Snyan * 8866550Snyan * Due to the size of modern disks, we must cast intermediate 8966550Snyan * values to 64 bits to prevent potential overflows. 9066550Snyan */ 9166550Snyan#ifdef COMPAT 92242871Snyan#define SIZE(n) (n) 9366550Snyan#else 9466550Snyan#define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) 9566550Snyan#endif 9666550Snyan 9766550Snyan#define INOCNT(fs) ((fs)->fs_ipg) 9866550Snyan#define INOSZ(fs) \ 9966550Snyan (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 10066550Snyan sizeof(struct ufs2_dinode)) * INOCNT(fs)) 10166550Snyan 10266550Snyanunion dinode { 10366550Snyan struct ufs1_dinode dp1; 10466550Snyan struct ufs2_dinode dp2; 10566550Snyan}; 10666581Snyan#define DIP(fs, dp, field) \ 10766550Snyan (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 10866550Snyan (dp)->dp1.field : (dp)->dp2.field) 10966550Snyan 11066550Snyanstatic union dinode * 11166550Snyanget_inode(fd,super,ino) 11266550Snyan int fd; 11366550Snyan struct fs *super; 11466550Snyan ino_t ino; 115242871Snyan{ 11666550Snyan static caddr_t ipbuf; 11766550Snyan static ino_t last; 11866550Snyan 11966550Snyan if (fd < 0) { /* flush cache */ 120127135Snjl if (ipbuf) { 12166550Snyan free(ipbuf); 12266550Snyan ipbuf = 0; 12366550Snyan } 12466550Snyan return 0; 12566550Snyan } 12666581Snyan 12766550Snyan if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { 12866550Snyan if (!ipbuf 12966550Snyan && !(ipbuf = malloc(INOSZ(super)))) 13066550Snyan errx(1, "allocate inodes"); 13166550Snyan last = (ino / INOCNT(super)) * INOCNT(super); 13266550Snyan if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 13366550Snyan || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 13466550Snyan err(1, "read inodes"); 135242871Snyan } 13666550Snyan 13766550Snyan if (super->fs_magic == FS_UFS1_MAGIC) 13866550Snyan return ((union dinode *) 13966550Snyan &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 14066550Snyan return ((union dinode *) 14166550Snyan &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]); 14266550Snyan} 14366550Snyan 14466550Snyan#ifdef COMPAT 14566550Snyan#define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) 14666550Snyan#else 14766550Snyan#define actualblocks(fs, dp) DIP(fs, dp, di_blocks) 14866550Snyan#endif 14966550Snyan 15066550Snyanstatic int virtualblocks(super, dp) 15166550Snyan struct fs *super; 15266550Snyan union dinode *dp; 15366550Snyan{ 154181298Sjhb register off_t nblk, sz; 155181298Sjhb 156181298Sjhb sz = DIP(super, dp, di_size); 157181298Sjhb#ifdef COMPAT 15866550Snyan if (lblkno(super,sz) >= NDADDR) { 15966550Snyan nblk = blkroundup(super,sz); 16066550Snyan if (sz == nblk) 16166550Snyan nblk += super->fs_bsize; 16266550Snyan } 16366550Snyan 16466550Snyan return sz / 1024; 165242871Snyan 16666550Snyan#else /* COMPAT */ 16766550Snyan 16866550Snyan if (lblkno(super,sz) >= NDADDR) { 16966550Snyan nblk = blkroundup(super,sz); 17066550Snyan sz = lblkno(super,nblk); 17166550Snyan sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 17266550Snyan while (sz > 0) { 17366550Snyan nblk += sz * super->fs_bsize; 17466550Snyan /* sz - 1 rounded up */ 17566550Snyan sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 17666550Snyan } 17766550Snyan } else 17866550Snyan nblk = fragroundup(super,sz); 17966550Snyan 18066550Snyan return nblk / 512; 181242871Snyan#endif /* COMPAT */ 18266550Snyan} 18366550Snyan 18466550Snyanstatic int 185181298Sjhbisfree(super, dp) 18666550Snyan struct fs *super; 18766550Snyan union dinode *dp; 18866550Snyan{ 18966550Snyan#ifdef COMPAT 19066550Snyan return (DIP(super, dp, di_mode) & IFMT) == 0; 19166550Snyan#else /* COMPAT */ 19266550Snyan 19366550Snyan switch (DIP(super, dp, di_mode) & IFMT) { 19466550Snyan case IFIFO: 19566550Snyan case IFLNK: /* should check FASTSYMLINK? */ 19666550Snyan case IFDIR: 19766550Snyan case IFREG: 19866550Snyan return 0; 19966550Snyan default: 20066550Snyan return 1; 20166550Snyan } 20266550Snyan#endif 20366550Snyan} 20466550Snyan 20566550Snyanstatic struct user { 20666550Snyan uid_t uid; 20766550Snyan char *name; 20866550Snyan daddr_t space; 20966550Snyan long count; 21066550Snyan daddr_t spc30; 21166550Snyan daddr_t spc60; 21266550Snyan daddr_t spc90; 21366550Snyan} *users; 21466550Snyanstatic int nusers; 21566550Snyan 21666550Snyanstatic void 217181298Sjhbinituser() 218181298Sjhb{ 219181298Sjhb register int i; 220181298Sjhb register struct user *usr; 221181298Sjhb 222181298Sjhb if (!nusers) { 223181298Sjhb nusers = 8; 224181298Sjhb if (!(users = 225181298Sjhb (struct user *)calloc(nusers,sizeof(struct user)))) 22666550Snyan errx(1, "allocate users"); 227181298Sjhb } else { 228181298Sjhb for (usr = users, i = nusers; --i >= 0; usr++) { 229181298Sjhb usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 230181298Sjhb usr->count = 0; 231181298Sjhb } 232181298Sjhb } 233181298Sjhb} 234181298Sjhb 235181298Sjhbstatic void 236181298Sjhbusrrehash() 23766550Snyan{ 23866550Snyan register int i; 23966550Snyan register struct user *usr, *usrn; 24066550Snyan struct user *svusr; 24166550Snyan 24266550Snyan svusr = users; 24366550Snyan nusers <<= 1; 244194023Savg if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 245242871Snyan errx(1, "allocate users"); 24666550Snyan for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 247181298Sjhb for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 248181298Sjhb usrn--) { 249181298Sjhb if (usrn <= users) 250181298Sjhb usrn = users + nusers; 251181298Sjhb } 252194023Savg *usrn = *usr; 253194023Savg } 25466550Snyan} 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 == numfrags(fs, 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