1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1991, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)quot.c 8.4 (Berkeley) 01/07/94"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/time.h> 20 #include <sys/stat.h> 21 #include <sys/mount.h> 22 #include <ufs/ufs/dinode.h> 23 #include <ufs/ffs/fs.h> 24 25 #include <fcntl.h> 26 #include <unistd.h> 27 #include <stdio.h> 28 #include <ctype.h> 29 #include <paths.h> 30 31 #define ISIZ (MAXBSIZE/sizeof(struct dinode)) 32 union { 33 struct fs u_sblock; 34 char dummy[SBSIZE]; 35 } sb_un; 36 #define sblock sb_un.u_sblock 37 struct dinode itab[MAXBSIZE/sizeof(struct dinode)]; 38 39 struct du { 40 struct du *next; 41 long blocks; 42 long blocks30; 43 long blocks60; 44 long blocks90; 45 long nfiles; 46 int uid; 47 #define NDU 2048 48 } du[NDU]; 49 int ndu; 50 #define DUHASH 8209 /* smallest prime >= 4 * NDU */ 51 #define HASH(u) ((u) % DUHASH) 52 struct du *duhash[DUHASH]; 53 54 #define TSIZE 500 55 int sizes[TSIZE]; 56 long overflow; 57 58 int nflg; 59 int fflg; 60 int cflg; 61 int vflg; 62 int hflg; 63 struct timeval now; 64 65 unsigned ino; 66 67 char *user_from_uid(); 68 69 main(argc, argv) 70 int argc; 71 char *argv[]; 72 { 73 extern char *optarg; 74 extern int optind; 75 int ch; 76 77 while ((ch = getopt(argc, argv, "cfhnv")) != EOF) 78 switch((char)ch) { 79 case 'c': 80 cflg++; break; 81 case 'f': 82 fflg++; break; 83 case 'h': /* undocumented */ 84 hflg++; break; 85 case 'n': 86 nflg++; break; 87 case 'v': /* undocumented */ 88 vflg++; break; 89 case '?': 90 default: 91 fputs("usage: quot [-cfn] [filesystem ...]\n", stderr); 92 exit(1); 93 } 94 argc -= optind; 95 argv += optind; 96 97 (void)gettimeofday(&now, NULL); 98 setpassent(1); 99 if (argc) 100 for (; *argv; ++argv) { 101 if (check(*argv, (char *)NULL) == 0) 102 report(); 103 } 104 else 105 quotall(); 106 exit(0); 107 } 108 109 #include <sys/dir.h> 110 #include <fstab.h> 111 112 quotall() 113 { 114 register struct fstab *fs; 115 register char *cp; 116 char dev[MAXNAMLEN + 10], *rindex(); 117 118 while (fs = getfsent()) { 119 if (strcmp(fs->fs_vfstype, "ufs") || 120 (strcmp(fs->fs_type, FSTAB_RO) && 121 strcmp(fs->fs_type, FSTAB_RW) && 122 strcmp(fs->fs_type, FSTAB_RQ))) 123 continue; 124 cp = rindex(fs->fs_spec, '/'); 125 if (cp == 0) 126 continue; 127 (void)snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV, cp + 1); 128 if (check(dev, fs->fs_file) == 0) 129 report(); 130 } 131 } 132 133 check(file, fsdir) 134 char *file; 135 char *fsdir; 136 { 137 register int i, j, nfiles; 138 register struct du **dp; 139 daddr_t iblk; 140 long dev_bsize; 141 int c, fd; 142 struct stat sb; 143 struct statfs sfs; 144 #define FNBSZ 128 145 char filebuf[FNBSZ]; 146 147 /* 148 * Initialize tables between checks; because of the qsort done in 149 * report() the hash tables must be rebuilt each time. 150 */ 151 for (i = 0; i < TSIZE; i++) 152 sizes[i] = 0; 153 overflow = 0; 154 for (dp = duhash; dp < &duhash[DUHASH]; dp++) 155 *dp = 0; 156 ndu = 0; 157 /* 158 * Make sure "file" refers to a char special file. 159 * Convert if necessary. Also make sure we have an fsdir. 160 */ 161 if (stat(file, &sb) < 0) { 162 fprintf(stderr, "quot: "); 163 perror(file); 164 return (-1); 165 } 166 /* 167 * User specified "file". 168 * Attempt to come up with the appropriate char special file 169 * and a mount point. 170 */ 171 if (fsdir == NULL) { 172 register struct fstab *fs; 173 int len = strlen(_PATH_DEV); 174 175 strcpy(filebuf, _PATH_DEV); 176 if (S_ISCHR(sb.st_mode)) { 177 if (strncmp(_PATH_DEV, file, len) == 0 && 178 file[len] == 'r') { 179 strncpy(&filebuf[len], &file[len+1], FNBSZ-6); 180 fs = getfsspec(filebuf); 181 if (fs != NULL) 182 fsdir = fs->fs_file; 183 } 184 } else { 185 if (S_ISBLK(sb.st_mode)) { 186 fs = getfsspec(file); 187 if (fs != NULL) 188 fsdir = fs->fs_file; 189 } else { 190 if (statfs(file, &sfs) == 0) { 191 file = sfs.f_mntfromname; 192 fsdir = sfs.f_mntonname; 193 } 194 } 195 if (strncmp(_PATH_DEV, file, len) == 0) { 196 filebuf[len] = 'r'; 197 strncpy(&filebuf[len+1], &file[len], FNBSZ-7); 198 file = filebuf; 199 } 200 } 201 } 202 fd = open(file, O_RDONLY); 203 if (fd < 0) { 204 fprintf(stderr, "quot: "); 205 perror(file); 206 return (-1); 207 } 208 printf("%s", file); 209 if (fsdir == NULL) { 210 register struct fstab *fs = getfsspec(file); 211 if (fs != NULL) 212 fsdir = fs->fs_file; 213 } 214 if (fsdir != NULL && *fsdir != '\0') 215 printf(" (%s)", fsdir); 216 printf(":\n"); 217 sync(); 218 bread(fd, (off_t)SBOFF, (char *)&sblock, SBSIZE); 219 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 220 if (nflg) { 221 if (isdigit(c = getchar())) 222 (void)ungetc(c, stdin); 223 else while (c != '\n' && c != EOF) 224 c = getchar(); 225 } 226 nfiles = sblock.fs_ipg * sblock.fs_ncg; 227 for (ino = 0; ino < nfiles; ) { 228 iblk = fsbtodb(&sblock, ino_to_fsba(&sblock, ino)); 229 bread(fd, (off_t)iblk * dev_bsize, itab, (int)sblock.fs_bsize); 230 for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) { 231 if (ino < ROOTINO) 232 continue; 233 qacct(&itab[j]); 234 } 235 } 236 close(fd); 237 return (0); 238 } 239 240 qacct(ip) 241 register struct dinode *ip; 242 { 243 register struct du *dp; 244 struct du **hp; 245 long blks, frags, size; 246 int n; 247 static fino; 248 249 if ((ip->di_mode & IFMT) == 0) 250 return; 251 /* 252 * By default, take block count in inode. Otherwise (-h), 253 * take the size field and estimate the blocks allocated. 254 * The latter does not account for holes in files. 255 */ 256 if (!hflg) 257 size = ip->di_blocks / 2; 258 else { 259 blks = lblkno(&sblock, ip->di_size); 260 frags = blks * sblock.fs_frag + 261 numfrags(&sblock, dblksize(&sblock, ip, blks)); 262 size = frags * sblock.fs_fsize / 1024; 263 } 264 if (cflg) { 265 if ((ip->di_mode&IFMT) != IFDIR && (ip->di_mode&IFMT) != IFREG) 266 return; 267 if (size >= TSIZE) { 268 overflow += size; 269 size = TSIZE-1; 270 } 271 sizes[size]++; 272 return; 273 } 274 hp = &duhash[HASH(ip->di_uid)]; 275 for (dp = *hp; dp; dp = dp->next) 276 if (dp->uid == ip->di_uid) 277 break; 278 if (dp == 0) { 279 if (ndu >= NDU) 280 return; 281 dp = &du[ndu++]; 282 dp->next = *hp; 283 *hp = dp; 284 dp->uid = ip->di_uid; 285 dp->nfiles = 0; 286 dp->blocks = 0; 287 dp->blocks30 = 0; 288 dp->blocks60 = 0; 289 dp->blocks90 = 0; 290 } 291 dp->blocks += size; 292 #define DAY (60 * 60 * 24) /* seconds per day */ 293 if (now.tv_sec - ip->di_atime.ts_sec > 30 * DAY) 294 dp->blocks30 += size; 295 if (now.tv_sec - ip->di_atime.ts_sec > 60 * DAY) 296 dp->blocks60 += size; 297 if (now.tv_sec - ip->di_atime.ts_sec > 90 * DAY) 298 dp->blocks90 += size; 299 dp->nfiles++; 300 while (nflg) { 301 register char *np; 302 303 if (fino == 0) 304 if (scanf("%d", &fino) <= 0) 305 return; 306 if (fino > ino) 307 return; 308 if (fino < ino) { 309 while ((n = getchar()) != '\n' && n != EOF) 310 ; 311 fino = 0; 312 continue; 313 } 314 if (np = user_from_uid(dp->uid, 1)) 315 printf("%.7s\t", np); 316 else 317 printf("%u\t", ip->di_uid); 318 while ((n = getchar()) == ' ' || n == '\t') 319 ; 320 putchar(n); 321 while (n != EOF && n != '\n') { 322 n = getchar(); 323 putchar(n); 324 } 325 fino = 0; 326 break; 327 } 328 } 329 330 bread(fd, bno, buf, cnt) 331 int fd, cnt; 332 off_t bno; 333 void *buf; 334 { 335 (void)lseek(fd, bno, L_SET); 336 if (read(fd, buf, cnt) != cnt) { 337 fprintf(stderr, "quot: read error at block %ld\n", bno); 338 exit(1); 339 } 340 } 341 342 qcmp(p1, p2) 343 register struct du *p1, *p2; 344 { 345 char *s1, *s2; 346 347 if (p1->blocks > p2->blocks) 348 return (-1); 349 if (p1->blocks < p2->blocks) 350 return (1); 351 s1 = user_from_uid(p1->uid, 1); 352 if (s1 == 0) 353 return (0); 354 s2 = user_from_uid(p2->uid, 1); 355 if (s2 == 0) 356 return (0); 357 return (strcmp(s1, s2)); 358 } 359 360 report() 361 { 362 register i; 363 register struct du *dp; 364 365 if (nflg) 366 return; 367 if (cflg) { 368 register long t = 0; 369 370 for (i = 0; i < TSIZE - 1; i++) 371 if (sizes[i]) { 372 t += i*sizes[i]; 373 printf("%d\t%d\t%ld\n", i, sizes[i], t); 374 } 375 printf("%d\t%d\t%ld\n", 376 TSIZE - 1, sizes[TSIZE - 1], overflow + t); 377 return; 378 } 379 qsort(du, ndu, sizeof (du[0]), qcmp); 380 for (dp = du; dp < &du[ndu]; dp++) { 381 register char *cp; 382 383 if (dp->blocks == 0) 384 return; 385 printf("%5D\t", dp->blocks); 386 if (fflg) 387 printf("%5D\t", dp->nfiles); 388 if (cp = user_from_uid(dp->uid, 1)) 389 printf("%-8.8s", cp); 390 else 391 printf("#%-8d", dp->uid); 392 if (vflg) 393 printf("\t%5D\t%5D\t%5D", 394 dp->blocks30, dp->blocks60, dp->blocks90); 395 printf("\n"); 396 } 397 } 398