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