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