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