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