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