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