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.5 2005/12/05 02:40:28 swildner 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(int fd, struct fs *super, ino_t ino) 99 { 100 static struct dinode *ip; 101 static ino_t last; 102 103 if (fd < 0) { /* flush cache */ 104 if (ip) { 105 free(ip); 106 ip = 0; 107 } 108 return 0; 109 } 110 111 if (!ip || ino < last || ino >= last + INOCNT(super)) { 112 if (!ip 113 && !(ip = (struct dinode *)malloc(INOSZ(super)))) 114 errx(1, "allocate inodes"); 115 last = (ino / INOCNT(super)) * INOCNT(super); 116 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 117 || read(fd,ip,INOSZ(super)) != (ssize_t)INOSZ(super)) 118 err(1, "read inodes"); 119 } 120 121 return ip + ino % INOCNT(super); 122 } 123 124 #ifdef COMPAT 125 #define actualblocks(super,ip) ((ip)->di_blocks/2) 126 #else 127 #define actualblocks(super,ip) ((ip)->di_blocks) 128 #endif 129 130 static int 131 virtualblocks(struct fs *super, struct dinode *ip) 132 { 133 off_t nblk, sz; 134 135 sz = ip->di_size; 136 #ifdef COMPAT 137 if (lblkno(super,sz) >= NDADDR) { 138 nblk = blkroundup(super,sz); 139 if (sz == nblk) 140 nblk += super->fs_bsize; 141 } 142 143 return sz / 1024; 144 145 #else /* COMPAT */ 146 147 if (lblkno(super,sz) >= NDADDR) { 148 nblk = blkroundup(super,sz); 149 sz = lblkno(super,nblk); 150 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); 151 while (sz > 0) { 152 nblk += sz * super->fs_bsize; 153 /* sz - 1 rounded up */ 154 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); 155 } 156 } else 157 nblk = fragroundup(super,sz); 158 159 return nblk / 512; 160 #endif /* COMPAT */ 161 } 162 163 static int 164 isfree(struct dinode *ip) 165 { 166 #ifdef COMPAT 167 return (ip->di_mode&IFMT) == 0; 168 #else /* COMPAT */ 169 170 switch (ip->di_mode&IFMT) { 171 case IFIFO: 172 case IFLNK: /* should check FASTSYMLINK? */ 173 case IFDIR: 174 case IFREG: 175 return 0; 176 default: 177 return 1; 178 } 179 #endif 180 } 181 182 static struct user { 183 uid_t uid; 184 char *name; 185 daddr_t space; 186 long count; 187 daddr_t spc30; 188 daddr_t spc60; 189 daddr_t spc90; 190 } *users; 191 static int nusers; 192 193 static void 194 inituser(void) 195 { 196 int i; 197 struct user *usr; 198 199 if (!nusers) { 200 nusers = 8; 201 if (!(users = 202 (struct user *)calloc(nusers,sizeof(struct user)))) 203 errx(1, "allocate users"); 204 } else { 205 for (usr = users, i = nusers; --i >= 0; usr++) { 206 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 207 usr->count = 0; 208 } 209 } 210 } 211 212 static void 213 usrrehash(void) 214 { 215 int i; 216 struct user *usr, *usrn; 217 struct user *svusr; 218 219 svusr = users; 220 nusers <<= 1; 221 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) 222 errx(1, "allocate users"); 223 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { 224 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; 225 usrn--) { 226 if (usrn <= users) 227 usrn = users + nusers; 228 } 229 *usrn = *usr; 230 } 231 } 232 233 static struct user * 234 user(uid_t uid) 235 { 236 struct user *usr; 237 int i; 238 struct passwd *pwd; 239 240 while (1) { 241 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; 242 usr--) { 243 if (!usr->name) { 244 usr->uid = uid; 245 246 if (!(pwd = getpwuid(uid))) { 247 if ((usr->name = (char *)malloc(7))) 248 sprintf(usr->name,"#%d",uid); 249 } else { 250 if ((usr->name = (char *) 251 malloc(strlen(pwd->pw_name) + 1))) 252 strcpy(usr->name,pwd->pw_name); 253 } 254 if (!usr->name) 255 errx(1, "allocate users"); 256 257 return usr; 258 259 } else if (usr->uid == uid) 260 return usr; 261 262 if (usr <= users) 263 usr = users + nusers; 264 } 265 usrrehash(); 266 } 267 } 268 269 static int 270 cmpusers(const void *v1, const void *v2) 271 { 272 const struct user *u1, *u2; 273 u1 = (const struct user *)v1; 274 u2 = (const struct user *)v2; 275 276 return u2->space - u1->space; 277 } 278 279 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ 280 cmpusers)) 281 282 static void 283 uses(uid_t uid, daddr_t blks, time_t act) 284 { 285 static time_t today; 286 struct user *usr; 287 288 if (!today) 289 time(&today); 290 291 usr = user(uid); 292 usr->count++; 293 usr->space += blks; 294 295 if (today - act > 90L * 24L * 60L * 60L) 296 usr->spc90 += blks; 297 if (today - act > 60L * 24L * 60L * 60L) 298 usr->spc60 += blks; 299 if (today - act > 30L * 24L * 60L * 60L) 300 usr->spc30 += blks; 301 } 302 303 #ifdef COMPAT 304 #define FSZCNT 500 305 #else 306 #define FSZCNT 512 307 #endif 308 struct fsizes { 309 struct fsizes *fsz_next; 310 daddr_t fsz_first, fsz_last; 311 ino_t fsz_count[FSZCNT]; 312 daddr_t fsz_sz[FSZCNT]; 313 } *fsizes; 314 315 static void 316 initfsizes(void) 317 { 318 struct fsizes *fp; 319 int i; 320 321 for (fp = fsizes; fp; fp = fp->fsz_next) { 322 for (i = FSZCNT; --i >= 0;) { 323 fp->fsz_count[i] = 0; 324 fp->fsz_sz[i] = 0; 325 } 326 } 327 } 328 329 static void 330 dofsizes(int fd, struct fs *super, char *name) 331 { 332 ino_t inode, maxino; 333 struct dinode *ip; 334 daddr_t sz, ksz; 335 struct fsizes *fp, **fsp; 336 int i; 337 338 maxino = super->fs_ncg * super->fs_ipg - 1; 339 #ifdef COMPAT 340 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) 341 errx(1, "allocate fsize structure"); 342 #endif /* COMPAT */ 343 for (inode = 0; inode < maxino; inode++) { 344 errno = 0; 345 if ((ip = get_inode(fd,super,inode)) 346 #ifdef COMPAT 347 && ((ip->di_mode&IFMT) == IFREG 348 || (ip->di_mode&IFMT) == IFDIR) 349 #else /* COMPAT */ 350 && !isfree(ip) 351 #endif /* COMPAT */ 352 ) { 353 sz = estimate ? virtualblocks(super,ip) : 354 actualblocks(super,ip); 355 #ifdef COMPAT 356 if (sz >= FSZCNT) { 357 fsizes->fsz_count[FSZCNT-1]++; 358 fsizes->fsz_sz[FSZCNT-1] += sz; 359 } else { 360 fsizes->fsz_count[sz]++; 361 fsizes->fsz_sz[sz] += sz; 362 } 363 #else /* COMPAT */ 364 ksz = SIZE(sz); 365 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 366 if (ksz < fp->fsz_last) 367 break; 368 } 369 if (!fp || ksz < fp->fsz_first) { 370 if (!(fp = (struct fsizes *) 371 malloc(sizeof(struct fsizes)))) 372 errx(1, "allocate fsize structure"); 373 fp->fsz_next = *fsp; 374 *fsp = fp; 375 fp->fsz_first = (ksz / FSZCNT) * FSZCNT; 376 fp->fsz_last = fp->fsz_first + FSZCNT; 377 for (i = FSZCNT; --i >= 0;) { 378 fp->fsz_count[i] = 0; 379 fp->fsz_sz[i] = 0; 380 } 381 } 382 fp->fsz_count[ksz % FSZCNT]++; 383 fp->fsz_sz[ksz % FSZCNT] += sz; 384 #endif /* COMPAT */ 385 } else if (errno) { 386 err(1, "%s", name); 387 } 388 } 389 sz = 0; 390 for (fp = fsizes; fp; fp = fp->fsz_next) { 391 for (i = 0; i < FSZCNT; i++) { 392 if (fp->fsz_count[i]) 393 printf("%d\t%d\t%d\n",fp->fsz_first + i, 394 fp->fsz_count[i], 395 SIZE(sz += fp->fsz_sz[i])); 396 } 397 } 398 } 399 400 static void 401 douser(int fd, struct fs *super, char *name) 402 { 403 ino_t inode, maxino; 404 struct user *usr, *usrs; 405 struct dinode *ip; 406 int n; 407 408 maxino = super->fs_ncg * super->fs_ipg - 1; 409 for (inode = 0; inode < maxino; inode++) { 410 errno = 0; 411 if ((ip = get_inode(fd,super,inode)) 412 && !isfree(ip)) 413 uses(ip->di_uid, 414 estimate ? virtualblocks(super,ip) : 415 actualblocks(super,ip), 416 ip->di_atime); 417 else if (errno) { 418 err(1, "%s", name); 419 } 420 } 421 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) 422 errx(1, "allocate users"); 423 bcopy(users,usrs,nusers * sizeof(struct user)); 424 sortusers(usrs); 425 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 426 printf("%5d",SIZE(usr->space)); 427 if (count) 428 printf("\t%5ld",usr->count); 429 printf("\t%-8s",usr->name); 430 if (unused) 431 printf("\t%5d\t%5d\t%5d", 432 SIZE(usr->spc30), 433 SIZE(usr->spc60), 434 SIZE(usr->spc90)); 435 printf("\n"); 436 } 437 free(usrs); 438 } 439 440 static void 441 donames(int fd, struct fs *super, char *name) 442 { 443 int c; 444 ino_t inode, inode1; 445 ino_t maxino; 446 struct dinode *ip; 447 448 maxino = super->fs_ncg * super->fs_ipg - 1; 449 /* first skip the name of the filesystem */ 450 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 451 while ((c = getchar()) != EOF && c != '\n'); 452 ungetc(c,stdin); 453 inode1 = -1; 454 while (scanf("%u",&inode) == 1) { 455 if (inode > maxino) { 456 warnx("illegal inode %d",inode); 457 return; 458 } 459 errno = 0; 460 if ((ip = get_inode(fd,super,inode)) 461 && !isfree(ip)) { 462 printf("%s\t",user(ip->di_uid)->name); 463 /* now skip whitespace */ 464 while ((c = getchar()) == ' ' || c == '\t'); 465 /* and print out the remainder of the input line */ 466 while (c != EOF && c != '\n') { 467 putchar(c); 468 c = getchar(); 469 } 470 putchar('\n'); 471 inode1 = inode; 472 } else { 473 if (errno) { 474 err(1, "%s", name); 475 } 476 /* skip this line */ 477 while ((c = getchar()) != EOF && c != '\n'); 478 } 479 if (c == EOF) 480 break; 481 } 482 } 483 484 static void 485 usage(void) 486 { 487 #ifdef COMPAT 488 fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); 489 #else /* COMPAT */ 490 fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); 491 #endif /* COMPAT */ 492 exit(1); 493 } 494 495 static char superblock[SBSIZE]; 496 497 void 498 quot(char *name, char *mp) 499 { 500 int fd; 501 502 get_inode(-1, NULL, 0); /* flush cache */ 503 inituser(); 504 initfsizes(); 505 if ((fd = open(name,0)) < 0 506 || lseek(fd,SBOFF,0) != SBOFF 507 || read(fd,superblock,SBSIZE) != SBSIZE) { 508 warn("%s", name); 509 close(fd); 510 return; 511 } 512 if (((struct fs *)superblock)->fs_magic != FS_MAGIC) { 513 warnx("%s: not a BSD filesystem",name); 514 close(fd); 515 return; 516 } 517 printf("%s:",name); 518 if (mp) 519 printf(" (%s)",mp); 520 putchar('\n'); 521 (*func)(fd,(struct fs *)superblock,name); 522 close(fd); 523 } 524 525 int 526 main(int argc, 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