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