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