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