1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * quot 44 */ 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <ctype.h> 49 #include <string.h> 50 #include <limits.h> 51 #include <pwd.h> 52 #include <sys/mnttab.h> 53 #include <sys/param.h> 54 #include <sys/types.h> 55 #include <unistd.h> 56 #include <sys/mntent.h> 57 #include <sys/vnode.h> 58 #include <sys/fs/ufs_inode.h> 59 #include <sys/fs/ufs_fs.h> 60 #include <sys/file.h> 61 #include <sys/stat.h> 62 #include <fcntl.h> 63 64 #define ISIZ (MAXBSIZE/sizeof (struct dinode)) 65 static union { 66 struct fs u_sblock; 67 char dummy[SBSIZE]; 68 } sb_un; 69 #define sblock sb_un.u_sblock 70 static struct dinode *itab; 71 72 struct du { 73 struct du *next; 74 long blocks; 75 long blocks30; 76 long blocks60; 77 long blocks90; 78 long nfiles; 79 uid_t uid; 80 char *u_name; 81 }; 82 static struct du **du; 83 84 #define UHASH 8209 85 static int ndu; 86 #define HASH(u) ((uint_t)(u) % UHASH) 87 static struct du *duhashtbl[UHASH]; 88 89 #define TSIZE 2048 90 static int sizes[TSIZE]; 91 static offset_t overflow; 92 93 static int nflg; 94 static int fflg; 95 static int cflg; 96 static int vflg; 97 static int hflg; 98 static int aflg; 99 static long now; 100 101 static unsigned ino; 102 103 static void usage(void); 104 static void quotall(void); 105 static void qacct(struct dinode *); 106 static void bread(int, diskaddr_t, char *, int); 107 static void report(void); 108 static int getdev(char **); 109 static int check(char *, char *); 110 static struct du *adduid(uid_t); 111 static struct du *lookup(uid_t); 112 static void sortprep(void); 113 static void cleanup(void); 114 115 static void 116 usage() 117 { 118 (void) fprintf(stderr, "ufs usage: quot [-nfcvha] [filesystem ...]\n"); 119 } 120 121 int 122 main(int argc, char *argv[]) 123 { 124 int opt; 125 int i; 126 127 if (argc == 1) { 128 (void) fprintf(stderr, 129 "ufs Usage: quot [-nfcvha] [filesystem ...]\n"); 130 return (32); 131 } 132 133 now = time(0); 134 while ((opt = getopt(argc, argv, "nfcvhaV")) != EOF) { 135 switch (opt) { 136 case 'n': 137 nflg++; 138 break; 139 case 'f': 140 fflg++; 141 break; 142 case 'c': 143 cflg++; 144 break; 145 case 'v': 146 vflg++; 147 break; 148 case 'h': 149 hflg++; 150 break; 151 case 'a': 152 aflg++; 153 break; 154 case 'V': /* Print command line */ 155 { 156 char *opt_text; 157 int opt_count; 158 159 (void) fprintf(stdout, "quot -F UFS "); 160 for (opt_count = 1; opt_count < argc; 161 opt_count++) { 162 opt_text = argv[opt_count]; 163 if (opt_text) 164 (void) fprintf(stdout, " %s ", 165 opt_text); 166 } 167 (void) fprintf(stdout, "\n"); 168 } 169 break; 170 case '?': 171 usage(); 172 return (32); 173 } 174 } 175 176 if (aflg) { 177 quotall(); 178 } 179 180 for (i = optind; i < argc; i++) { 181 if ((getdev(&argv[i]) == 0) && 182 (check(argv[i], (char *)NULL) == 0)) { 183 report(); 184 cleanup(); 185 } 186 } 187 return (0); 188 } 189 190 static void 191 quotall() 192 { 193 FILE *fstab; 194 struct mnttab mntp; 195 char *cp; 196 197 extern char *getfullrawname(); 198 199 fstab = fopen(MNTTAB, "r"); 200 if (fstab == NULL) { 201 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB); 202 exit(32); 203 } 204 while (getmntent(fstab, &mntp) == NULL) { 205 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0) 206 continue; 207 208 if ((cp = getfullrawname(mntp.mnt_special)) == NULL) 209 continue; 210 211 if (*cp == '\0') 212 continue; 213 214 if (check(cp, mntp.mnt_mountp) == 0) { 215 report(); 216 cleanup(); 217 } 218 219 free(cp); 220 } 221 (void) fclose(fstab); 222 } 223 224 static int 225 check(char *file, char *fsdir) 226 { 227 FILE *fstab; 228 int i, j; 229 int c, fd; 230 231 232 /* 233 * Initialize tables between checks; 234 * because of the qsort done in report() 235 * the hash tables must be rebuilt each time. 236 */ 237 for (i = 0; i < TSIZE; i++) 238 sizes[i] = 0; 239 overflow = 0LL; 240 ndu = 0; 241 fd = open64(file, O_RDONLY); 242 if (fd < 0) { 243 (void) fprintf(stderr, "quot: "); 244 perror(file); 245 exit(32); 246 } 247 (void) printf("%s", file); 248 if (fsdir == NULL) { 249 struct mnttab mntp; 250 251 fstab = fopen(MNTTAB, "r"); 252 if (fstab == NULL) { 253 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB); 254 exit(32); 255 } 256 while (getmntent(fstab, &mntp) == NULL) { 257 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0) 258 continue; 259 if (strcmp(mntp.mnt_special, file) == 0) { 260 fsdir = mntp.mnt_mountp; 261 break; 262 } 263 } 264 } 265 if (fsdir != NULL && *fsdir != '\0') 266 (void) printf(" (%s)", fsdir); 267 (void) printf(":\n"); 268 sync(); 269 bread(fd, (diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE); 270 if (nflg) { 271 if (isdigit(c = getchar())) 272 (void) ungetc(c, stdin); 273 else while (c != '\n' && c != EOF) 274 c = getchar(); 275 } 276 277 itab = (struct dinode *)calloc(sblock.fs_ipg, sizeof (struct dinode)); 278 if (itab == NULL) { 279 (void) fprintf(stderr, 280 "not enough memory to allocate tables\n"); 281 return (1); 282 } 283 284 ino = 0; 285 for (c = 0; c < sblock.fs_ncg; c++) { 286 bread(fd, (diskaddr_t)fsbtodb(&sblock, cgimin(&sblock, c)), 287 (char *)itab, 288 (int)(sblock.fs_ipg * sizeof (struct dinode))); 289 for (j = 0; j < sblock.fs_ipg; j++, ino++) { 290 if (ino < UFSROOTINO) 291 continue; 292 qacct(&itab[j]); 293 } 294 } 295 (void) close(fd); 296 return (0); 297 } 298 299 static void 300 qacct(struct dinode *ip) 301 { 302 struct du *dp; 303 long blks, frags, size; 304 int n; 305 static fino; 306 307 ip->di_mode = ip->di_smode; 308 if (ip->di_suid != UID_LONG) { 309 ip->di_uid = ip->di_suid; 310 } 311 if ((ip->di_mode & IFMT) == 0) 312 return; 313 /* 314 * By default, take block count in inode. Otherwise (-h), 315 * take the size field and estimate the blocks allocated. 316 * The latter does not account for holes in files. 317 */ 318 if (!hflg) 319 size = ip->di_blocks / 2; 320 else { 321 blks = lblkno(&sblock, ip->di_size); 322 frags = blks * sblock.fs_frag + 323 numfrags(&sblock, dblksize(&sblock, ip, blks)); 324 /* 325 * Must cast to offset_t because for a large file, 326 * frags multiplied by sblock.fs_fsize will not fit in a long. 327 * However, when divided by 1024, the end result will fit in 328 * the 32 bit size variable (40 bit UFS). 329 */ 330 size = (long)((offset_t)frags * (offset_t)sblock.fs_fsize / 1024); 331 } 332 if (cflg) { 333 if ((ip->di_mode&IFMT) != IFDIR && (ip->di_mode&IFMT) != IFREG) 334 return; 335 if (size >= TSIZE) { 336 overflow += (offset_t)size; 337 size = TSIZE-1; 338 } 339 sizes[size]++; 340 return; 341 } 342 dp = lookup(ip->di_uid); 343 if (dp == NULL) 344 return; 345 dp->blocks += size; 346 #define DAY (60 * 60 * 24) /* seconds per day */ 347 if (now - ip->di_atime > 30 * DAY) 348 dp->blocks30 += size; 349 if (now - ip->di_atime > 60 * DAY) 350 dp->blocks60 += size; 351 if (now - ip->di_atime > 90 * DAY) 352 dp->blocks90 += size; 353 dp->nfiles++; 354 while (nflg) { 355 if (fino == 0) 356 if (scanf("%d", &fino) <= 0) 357 return; 358 if (fino > ino) 359 return; 360 if (fino < ino) { 361 while ((n = getchar()) != '\n' && n != EOF) 362 ; 363 fino = 0; 364 continue; 365 } 366 if (dp->u_name) 367 (void) printf("%.7s ", dp->u_name); 368 else 369 (void) printf("%ld ", (long)ip->di_uid); 370 while ((n = getchar()) == ' ' || n == '\t') 371 ; 372 (void) putchar(n); 373 while (n != EOF && n != '\n') { 374 n = getchar(); 375 (void) putchar(n); 376 } 377 fino = 0; 378 break; 379 } 380 } 381 382 static void 383 bread(int fd, diskaddr_t bno, char *buf, int cnt) 384 { 385 int ret; 386 387 if (llseek(fd, (offset_t)(bno * DEV_BSIZE), SEEK_SET) < 0) { 388 perror("llseek"); 389 exit(32); 390 } 391 392 if ((ret = read(fd, buf, cnt)) != cnt) { 393 (void) fprintf(stderr, "quot: read returns %d (cnt = %d)\n", 394 ret, cnt); 395 (void) fprintf(stderr, "quot: read error at block %lld\n", bno); 396 perror("read"); 397 exit(32); 398 } 399 } 400 401 static int 402 qcmp(const void *arg1, const void *arg2) 403 { 404 struct du **p1 = (struct du **)arg1; 405 struct du **p2 = (struct du **)arg2; 406 char *s1, *s2; 407 408 if ((*p1)->blocks > (*p2)->blocks) 409 return (-1); 410 if ((*p1)->blocks < (*p2)->blocks) 411 return (1); 412 s1 = (*p1)->u_name; 413 if (s1 == NULL) 414 return (0); 415 s2 = (*p2)->u_name; 416 if (s2 == NULL) 417 return (0); 418 return (strcmp(s1, s2)); 419 } 420 421 static void 422 report() 423 { 424 int i; 425 struct du **dp; 426 int cnt; 427 428 if (nflg) 429 return; 430 if (cflg) { 431 long t = 0; 432 433 for (i = 0; i < TSIZE - 1; i++) 434 if (sizes[i]) { 435 t += i*sizes[i]; 436 (void) printf("%d %d %ld\n", 437 i, sizes[i], t); 438 } 439 if (sizes[TSIZE -1 ]) 440 (void) printf("%d %d %lld\n", TSIZE - 1, 441 sizes[TSIZE - 1], overflow + (offset_t)t); 442 return; 443 } 444 sortprep(); 445 qsort(du, ndu, sizeof (du[0]), qcmp); 446 for (cnt = 0, dp = &du[0]; dp && cnt != ndu; dp++, cnt++) { 447 if ((*dp)->blocks == 0) 448 return; 449 (void) printf("%5ld\t", (*dp)->blocks); 450 if (fflg) 451 (void) printf("%5ld\t", (*dp)->nfiles); 452 453 if ((*dp)->u_name) 454 (void) printf("%-8s", (*dp)->u_name); 455 else 456 (void) printf("#%-8ld", (long)(*dp)->uid); 457 if (vflg) 458 (void) printf("\t%5ld\t%5ld\t%5ld", 459 (*dp)->blocks30, (*dp)->blocks60, (*dp)->blocks90); 460 (void) printf("\n"); 461 } 462 } 463 464 465 466 static int 467 getdev(char **devpp) 468 { 469 struct stat64 statb; 470 FILE *fstab; 471 struct mnttab mntp; 472 char *cp; /* Pointer to raw device name */ 473 474 extern char *getfullrawname(); 475 476 if (stat64(*devpp, &statb) < 0) { 477 perror(*devpp); 478 exit(32); 479 } 480 if ((statb.st_mode & S_IFMT) == S_IFCHR) 481 return (0); 482 if ((statb.st_mode & S_IFMT) == S_IFBLK) { 483 /* If we can't get the raw name, keep the block name */ 484 if ((cp = getfullrawname(*devpp)) != NULL) 485 *devpp = strdup(cp); 486 return (0); 487 } 488 fstab = fopen(MNTTAB, "r"); 489 if (fstab == NULL) { 490 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB); 491 exit(32); 492 } 493 while (getmntent(fstab, &mntp) == NULL) { 494 if (strcmp(mntp.mnt_mountp, *devpp) == 0) { 495 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0) { 496 (void) fprintf(stderr, 497 "quot: %s not ufs filesystem\n", 498 *devpp); 499 exit(32); 500 } 501 /* If we can't get the raw name, use the block name */ 502 if ((cp = getfullrawname(mntp.mnt_special)) == NULL) 503 cp = mntp.mnt_special; 504 *devpp = strdup(cp); 505 (void) fclose(fstab); 506 return (0); 507 } 508 } 509 (void) fclose(fstab); 510 (void) fprintf(stderr, "quot: %s doesn't appear to be a filesystem.\n", 511 *devpp); 512 usage(); 513 exit(32); 514 /* NOTREACHED */ 515 } 516 517 static struct du * 518 lookup(uid_t uid) 519 { 520 struct passwd *pwp; 521 struct du *up; 522 523 for (up = duhashtbl[HASH(uid)]; up != NULL; up = up->next) { 524 if (up->uid == uid) 525 return (up); 526 } 527 528 pwp = getpwuid(uid); 529 530 up = adduid(uid); 531 if (up && pwp) { 532 up->u_name = strdup(pwp->pw_name); 533 } 534 return (up); 535 } 536 537 static struct du * 538 adduid(uid_t uid) 539 { 540 struct du *up, **uhp; 541 542 up = (struct du *)calloc(1, sizeof (struct du)); 543 if (up == NULL) { 544 (void) fprintf(stderr, 545 "out of memory for du structures\n"); 546 exit(32); 547 } 548 549 uhp = &duhashtbl[HASH(uid)]; 550 up->next = *uhp; 551 *uhp = up; 552 up->uid = uid; 553 up->u_name = NULL; 554 ndu++; 555 return (up); 556 } 557 558 static void 559 sortprep() 560 { 561 struct du **dp, *ep; 562 struct du **hp; 563 int i, cnt = 0; 564 565 dp = NULL; 566 567 dp = (struct du **)calloc(ndu, sizeof (struct du **)); 568 if (dp == NULL) { 569 (void) fprintf(stderr, 570 "out of memory for du structures\n"); 571 exit(32); 572 } 573 574 for (hp = duhashtbl, i = 0; i != UHASH; i++) { 575 if (hp[i] == NULL) 576 continue; 577 578 for (ep = hp[i]; ep; ep = ep->next) { 579 dp[cnt++] = ep; 580 } 581 } 582 du = dp; 583 } 584 585 static void 586 cleanup() 587 { 588 int i; 589 struct du *ep, *next; 590 591 /* 592 * Release memory from hash table and du 593 */ 594 595 if (du) { 596 free(du); 597 du = NULL; 598 } 599 600 601 for (i = 0; i != UHASH; i++) { 602 if (duhashtbl[i] == NULL) 603 continue; 604 ep = duhashtbl[i]; 605 while (ep) { 606 next = ep->next; 607 if (ep->u_name) { 608 free(ep->u_name); 609 } 610 free(ep); 611 ep = next; 612 } 613 duhashtbl[i] = NULL; 614 } 615 } 616