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