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