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