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