|
|
1.1 root 1: /*
2: * Produce various information about
3: * filesystem space usage and owners.
4: * The options of this command seem a
5: * little too disjoint in their functionality.
6: */
7:
8: #include <stdio.h>
9: #include <sys/ino.h>
10: #include <sys/filsys.h>
11: #include <canon.h>
12: #include <pwd.h>
13: #include <ctype.h>
14:
15: #define NHASH 64 /* Hash names table (power of 2) */
16: #define NIREAD (20*BUFSIZ) /* Inode bytes per read */
17: #define NCUM 2047 /* Number of different size entries */
18: #define BADSIZE (-1) /* not valid - for hash synonym checking */
19: #define BADUID (-1) /* not valid - for hash synonyms */
20:
21: /*
22: * Hash table of names
23: */
24: typedef struct NAME {
25: struct NAME *n_next;
26: unsigned short n_uid;
27: char n_name[];
28: } NAME;
29:
30: /*
31: * Entry of hash table of
32: * -f and ordinary options.
33: */
34: typedef struct FENTRY {
35: unsigned short f_uid;
36: long f_nfiles;
37: long f_nblocks;
38: } FENTRY;
39:
40: /*
41: * Entry of hash table for
42: * -c (cumulative sizes)
43: */
44: typedef struct CENTRY {
45: unsigned long c_nblocks; /* Size of file in blocks */
46: long c_nfiles; /* #files of that size */
47: } CENTRY;
48:
49: NAME *names[NHASH];
50: FENTRY *fentries; /* File count and size entries */
51: FENTRY *efp; /* Pointer to end of `fentries' */
52: CENTRY *centries; /* Cumulative size entries */
53: CENTRY *ecp; /* Pointer to end of `centries' */
54: CENTRY *lastcep; /* Last for sorting */
55: char ibuf[NIREAD]; /* I-node read buffer */
56: char incopt[] = "incompatible options given";
57:
58: int cflag; /* cumulative sizes list */
59: int fflag; /* #files as well as space */
60: int nflag; /* List of files and owners (via ncheck) */
61: int tflag; /* Print a total */
62: int nuids; /* Number of uids found in /etc/passwd */
63: ino_t readino; /* Last i-number read from input (`-n') */
64: char *readfile; /* Last filename read as above */
65: char *line; /* Line that is read in */
66:
67: char *username();
68: char *alloc();
69: FENTRY *getfentry();
70: CENTRY *getcentry();
71: int fcomp();
72: int ccomp();
73:
74: main(argc, argv)
75: int argc;
76: register char *argv[];
77: {
78: register char *ap;
79: register int es = 0;
80: register int mflag;
81: register int fd;
82:
83: while (argc>1 && *argv[1]=='-') {
84: for (ap = &argv[1][1]; *ap!='\0'; ap++)
85: switch (*ap) {
86: case 'n':
87: nflag++;
88: if (cflag || fflag)
89: cerr(incopt);
90: break;
91:
92: case 'c':
93: cflag++;
94: if (nflag || fflag)
95: cerr(incopt);
96: break;
97:
98: case 'f':
99: fflag++;
100: if (nflag || cflag)
101: cerr(incopt);
102: break;
103:
104: case 't':
105: tflag++;
106: if (nflag)
107: cerr(incopt);
108: break;
109:
110: default:
111: usage();
112: }
113: argv++;
114: argc--;
115: }
116: argv++;
117: if (argc < 2)
118: usage();
119: mflag = 0;
120: if (argv[0]!=NULL && argv[1]!=NULL)
121: mflag++;
122: for ( ; *argv!=NULL; argv++) {
123: if ((fd = open(*argv, 0)) < 0) {
124: cwarn("cannot open `%s'", *argv);
125: continue;
126: }
127: if (mflag)
128: fprintf(stderr, "%s:\n", *argv);
129: quotinit();
130: es |= quot(fd, *argv);
131: quotterm();
132: close(fd);
133: }
134: exit(es);
135: }
136:
137: /*
138: * Initialise things for `quot' for
139: * each filesystem pass.
140: */
141: quotinit()
142: {
143: if (!cflag && nuids==0) {
144: readnames();
145: nuids *= 2;
146: }
147: if (!nflag && !cflag) {
148: register FENTRY *fep;
149:
150: fentries = (FENTRY *)alloc(nuids*sizeof (FENTRY));
151: efp = &fentries[nuids];
152: for (fep = fentries; fep < efp; fep++)
153: fep->f_uid = BADUID;
154: } else if (cflag) {
155: register CENTRY *cep;
156:
157: centries = (CENTRY *)alloc(NCUM*sizeof(CENTRY));
158: lastcep = centries;
159: ecp = ¢ries[NCUM];
160: for (cep = centries; cep < ecp; cep++)
161: cep->c_nblocks = BADSIZE;
162: } else {
163: line = alloc(1000);
164: readino = 0;
165: }
166: }
167:
168: /*
169: * Routine run between filesystems
170: * to clean up allocated storage.
171: * Also may print out tables.
172: */
173: quotterm()
174: {
175: if (fentries != NULL) {
176: register FENTRY *fep;
177: long totblocks = 0;
178: long totfiles = 0;
179:
180: qsort((char *)fentries, nuids, sizeof(FENTRY), fcomp);
181: for (fep=fentries; fep < efp; fep++) {
182: if (fep->f_uid == BADUID)
183: break;
184: if (tflag) {
185: totblocks += fep->f_nblocks;
186: totfiles += fep->f_nfiles;
187: }
188: if (fflag)
189: printf("%8ld ", fep->f_nfiles);
190: printf("%8ld %s\n", fep->f_nblocks,
191: username(fep->f_uid));
192: }
193: if (tflag) {
194: if (fflag)
195: printf("%8ld ", totfiles);
196: printf("%8ld Total\n", totblocks);
197: }
198: free((char *)fentries);
199: fentries = NULL;
200: } else if (centries != NULL) {
201: register CENTRY *cep;
202: long cumblocks = 0;
203:
204: qsort((char *)centries, lastcep-centries, sizeof(CENTRY), ccomp);
205: for (cep = centries; cep < lastcep; cep++) {
206: if (cep->c_nblocks == BADSIZE)
207: break;
208: cumblocks += cep->c_nblocks*cep->c_nfiles;
209: printf("%8ld %8ld %10ld\n", cep->c_nblocks,
210: cep->c_nfiles, cumblocks);
211: }
212: free((char *)centries);
213: centries = NULL;
214: }
215: }
216:
217: /*
218: * Called for each filesystem that is read.
219: */
220: quot(fsfd, fs)
221: int fsfd;
222: char *fs;
223: {
224: register ino_t maxino;
225: register ino_t inum;
226:
227: {
228: register struct filsys *sbp;
229:
230: lseek(fsfd, (fsize_t)(SUPERI*BSIZE), 0);
231: if (read(fsfd, ibuf, BSIZE) != BSIZE)
232: cerr("%s: bad filesystem format", fs);
233: sbp = ibuf;
234: canshort(sbp->s_isize);
235: candaddr(sbp->s_fsize);
236: if (sbp->s_isize > sbp->s_fsize) {
237: cwarn("%s: ridiculous fsize/isize", fs);
238: return (1);
239: }
240: maxino = (sbp->s_isize-INODEI) * INOPB;
241: }
242: lseek(fsfd, (fsize_t)(INODEI*BSIZE), 0);
243: inum = 0;
244: for (;;) {
245: register struct dinode *ip;
246:
247: if (read(fsfd, ibuf, sizeof ibuf) <= 0) {
248: cwarn("%s: i-node read error", fs);
249: return (1);
250: }
251: for (ip=ibuf; ip < &ibuf[NIREAD]; ip++) {
252: if (inum++ >= maxino)
253: break;
254: canshort(ip->di_uid);
255: canshort(ip->di_mode);
256: cansize(ip->di_size);
257: if (ilook(ip, inum))
258: return (0);
259: }
260: if (inum > maxino)
261: break;
262: }
263: return (0);
264: }
265:
266: /*
267: * Routine that looks at every I-node
268: * that we find.
269: * Return 1 for premature end.
270: */
271: ilook(ip, ino)
272: register struct dinode *ip;
273: register ino_t ino;
274: {
275:
276: if (ip->di_mode == 0)
277: return (0);
278: if (nflag) {
279: register char *lp;
280:
281: while (ino > readino) {
282: if (gets(line) == NULL)
283: return (1);
284: for (lp=line; *lp==' ' || *lp=='\t'; lp++)
285: ;
286: if (!isdigit(*lp))
287: continue;
288: readino = atoi(lp);
289: while (isdigit(*lp))
290: lp++;
291: while (*lp==' ' || *lp=='\t')
292: lp++;
293: readfile = lp;
294: }
295: if (readino == ino)
296: printf("%-16s %s\n", username(ip->di_uid), readfile);
297: } else if (cflag) {
298: register CENTRY *cep;
299: long size;
300:
301: size = (ip->di_size+BSIZE-1)/BSIZE;
302: cep = getcentry(size);
303: cep->c_nfiles++;
304: } else {
305: register FENTRY *fep;
306:
307: fep = getfentry(ip->di_uid);
308: fep->f_nfiles++;
309: switch (ip->di_mode & IFMT) {
310: case IFREG:
311: case IFDIR:
312: fep->f_nblocks += (ip->di_size+BSIZE-1)/BSIZE;
313: break;
314: }
315: }
316: return (0);
317: }
318:
319: /*
320: * Using hashing from a large pool,
321: * return a pointer to where the FENTRY
322: * for that uid was put or created.
323: * The table has to be contiguous for sorting.
324: */
325: FENTRY *
326: getfentry(uid)
327: register short unsigned uid;
328: {
329: register FENTRY *fep;
330:
331: fep = &fentries[(uid<<1)%nuids];
332: for (;;) {
333: if (fep >= efp)
334: fep = fentries;
335: if (fep->f_uid == uid)
336: return (fep);
337: if (fep->f_uid == BADUID) {
338: fep->f_uid = uid;
339: return (fep);
340: }
341: fep++;
342: }
343: }
344:
345: /*
346: * Return a CENTRY for a particular file size.
347: * This is hashed much like the FENTRY code
348: * above.
349: */
350: CENTRY *
351: getcentry(nb)
352: long nb;
353: {
354: register CENTRY *cep;
355:
356: cep = ¢ries[((unsigned)nb<<1)%NCUM];
357: for (;;) {
358: if (cep >= ecp)
359: cep = centries;
360: if (cep->c_nblocks == nb)
361: return (cep);
362: if (cep->c_nblocks == BADSIZE) {
363: cep->c_nblocks = nb;
364: if (cep > lastcep)
365: lastcep = cep+1;
366: return (cep);
367: }
368: cep++;
369: }
370: }
371:
372: /*
373: * Read all of the names from `/etc/passwd'
374: * and build up a hash table.
375: * Duplicates are discarded.
376: */
377: readnames()
378: {
379: register NAME *np;
380: register unsigned short hash;
381: register struct passwd *pwp;
382:
383: while ((pwp = getpwent()) != NULL) {
384: hash = pwp->pw_uid%NHASH;
385: for (np = names[hash]; np != NULL; np = np->n_next)
386: if (np->n_uid == pwp->pw_uid)
387: break;
388: if (np == NULL) {
389: nuids++;
390: np = (NAME*)alloc(sizeof(NAME)+strlen(pwp->pw_name)+1);
391: strcpy(np->n_name, pwp->pw_name);
392: np->n_uid = pwp->pw_uid;
393: np->n_next = names[hash];
394: names[hash] = np;
395: }
396: }
397: endpwent();
398: }
399:
400: /*
401: * Return user name for a particular number.
402: * Names that are not located are given the string
403: * representation of the number.
404: */
405: char *
406: username(uid)
407: unsigned short uid;
408: {
409: register NAME *np;
410: static char buf[12];
411:
412: for (np = names[uid%NHASH]; np != NULL; np = np->n_next)
413: if (np->n_uid == uid)
414: return (np->n_name);
415: return (sprintf(buf, "%u", uid));
416: }
417:
418: /*
419: * Comparison routine for no options
420: * and `-f'. Sort by # blocks, then
421: * # files.
422: */
423: fcomp(fep1, fep2)
424: register FENTRY *fep1, *fep2;
425: {
426: if (fep1->f_nblocks < fep2->f_nblocks)
427: return (1);
428: if (fep1->f_nblocks > fep2->f_nblocks)
429: return (-1);
430: if (fep1->f_nfiles < fep2->f_nfiles)
431: return (1);
432: if (fep1->f_nfiles > fep2->f_nfiles)
433: return (-1);
434: return (0);
435: }
436:
437: /*
438: * Sort comparison routine for the
439: * `-c' (cumulative size table)
440: * option.
441: */
442: ccomp(cep1, cep2)
443: register CENTRY *cep1, *cep2;
444: {
445: if (cep1->c_nblocks < cep2->c_nblocks)
446: return (-1);
447: if (cep1->c_nblocks > cep2->c_nblocks)
448: return (1);
449: return (0);
450: }
451:
452: /*
453: * Call allocator and check for errors.
454: */
455: char *
456: alloc(nb)
457: register unsigned nb;
458: {
459: register char *ap;
460:
461: if ((ap = calloc(nb, 1)) == NULL)
462: cerr("out of memory");
463: return (ap);
464: }
465:
466: /*
467: * Errors and usage message.
468: */
469: /* VARARGS */
470: cerr(x)
471: {
472: fprintf(stderr, "quot: %r\n", &x);
473: exit(1);
474: }
475:
476: cwarn(x)
477: {
478: fprintf(stderr, "quot: %r\n", &x);
479: exit(1);
480: }
481:
482: usage()
483: {
484: fprintf(stderr, "Usage: quot [-n] [-c] [-ft] filesystem [ ... ]\n");
485: exit(1);
486: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.