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