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