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 "$Id: quot.c,v 1.7 1997/10/10 06:31:07 charnier 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: */ 54 static char estimate; 55 static char count; 56 static char unused; 57 static void (*func)(); 58 static long blocksize; 59 static char *header; 60 static 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 79 static struct dinode * 80 get_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 114 static 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 148 static int 149 isfree(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 168 static 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; 177 static int nusers; 178 179 static void 180 inituser() 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 198 static void 199 usrrehash() 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 219 static struct user * 220 user(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 256 static int 257 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 void 267 uses(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 295 struct 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 302 static void 303 initfsizes() 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 316 static void 317 dofsizes(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 389 static void 390 douser(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 431 static void 432 donames(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 477 static void 478 usage() 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 488 static char superblock[SBSIZE]; 489 490 void 491 quot(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 519 int 520 main(argc,argv) 521 char **argv; 522 { 523 char all = 0; 524 struct statfs *mp; 525 char dev[MNAMELEN + 1]; 526 char *nm; 527 int cnt; 528 529 func = douser; 530 #ifndef COMPAT 531 header = getbsize(&headerlen,&blocksize); 532 #endif 533 while (--argc > 0 && **++argv == '-') { 534 while (*++*argv) { 535 switch (**argv) { 536 case 'n': 537 func = donames; 538 break; 539 case 'c': 540 func = dofsizes; 541 break; 542 case 'a': 543 all = 1; 544 break; 545 case 'f': 546 count = 1; 547 break; 548 case 'h': 549 estimate = 1; 550 break; 551 #ifndef COMPAT 552 case 'k': 553 blocksize = 1024; 554 break; 555 #endif /* COMPAT */ 556 case 'v': 557 unused = 1; 558 break; 559 default: 560 usage(); 561 } 562 } 563 } 564 if (all) { 565 cnt = getmntinfo(&mp,MNT_NOWAIT); 566 for (; --cnt >= 0; mp++) { 567 if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) { 568 if ((nm = strrchr(mp->f_mntfromname,'/'))) { 569 sprintf(dev,"/dev/r%s",nm + 1); 570 nm = dev; 571 } else 572 nm = mp->f_mntfromname; 573 quot(nm,mp->f_mntonname); 574 } 575 } 576 } 577 while (--argc >= 0) 578 quot(*argv++,0); 579 return 0; 580 } 581