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