xref: /original-bsd/sbin/quotacheck/quotacheck.c (revision a9c19d04)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)quotacheck.c	5.7 (Berkeley) 02/23/87";
15 #endif not lint
16 
17 /*
18  * Fix up / report on disc quotas & usage
19  */
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <sys/param.h>
25 #include <sys/inode.h>
26 #include <sys/fs.h>
27 #include <sys/quota.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <fstab.h>
31 #include <pwd.h>
32 
33 union {
34 	struct	fs	sblk;
35 	char	dummy[MAXBSIZE];
36 } un;
37 #define	sblock	un.sblk
38 
39 #define	ITABSZ	256
40 struct	dinode	itab[ITABSZ];
41 struct	dinode	*dp;
42 
43 #define LOGINNAMESIZE 8
44 struct fileusage {
45 	struct fileusage *fu_next;
46 	struct dqusage fu_usage;
47 	u_short	fu_uid;
48 	char fu_name[LOGINNAMESIZE + 1];
49 };
50 #define FUHASH 997
51 struct fileusage *fuhead[FUHASH];
52 struct fileusage *lookup();
53 struct fileusage *adduid();
54 int highuid;
55 
56 int fi;
57 ino_t ino;
58 long done;
59 struct	passwd	*getpwent();
60 struct	dinode	*ginode();
61 char *malloc(), *makerawname();
62 
63 int	vflag;		/* verbose */
64 int	aflag;		/* all file systems */
65 int	pflag;		/* fsck like parallel check */
66 
67 char *qfname = "quotas";
68 char quotafile[MAXPATHLEN + 1];
69 struct dqblk zerodqbuf;
70 struct fileusage zerofileusage;
71 long dev_bsize = 1;
72 
73 main(argc, argv)
74 	int argc;
75 	char **argv;
76 {
77 	register struct fstab *fs;
78 	register struct fileusage *fup;
79 	register struct passwd *pw;
80 	int i, errs = 0;
81 
82 again:
83 	argc--, argv++;
84 	if (argc > 0 && strcmp(*argv, "-v") == 0) {
85 		vflag++;
86 		goto again;
87 	}
88 	if (argc > 0 && strcmp(*argv, "-a") == 0) {
89 		aflag++;
90 		goto again;
91 	}
92 	if (argc > 0 && strcmp(*argv, "-p") == 0) {
93 		pflag++;
94 		goto again;
95 	}
96 	if (argc <= 0 && !aflag) {
97 		fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
98 			"quotacheck [-v] [-p] -a",
99 			"quotacheck [-v] [-p] filesys ...");
100 		exit(1);
101 	}
102 
103 	setpwent();
104 	while ((pw = getpwent()) != 0) {
105 		fup = lookup(pw->pw_uid);
106 		if (fup == 0) {
107 			fup = adduid(pw->pw_uid);
108 			strncpy(fup->fu_name, pw->pw_name,
109 				sizeof(fup->fu_name));
110 		}
111 	}
112 	endpwent();
113 
114 	if (pflag)
115 		errs = preen(argc, argv);
116 	else {
117 		if (setfsent() == 0) {
118 			fprintf(stderr, "Can't open ");
119 			perror(FSTAB);
120 			exit(8);
121 		}
122 		while ((fs = getfsent()) != NULL) {
123 			if (aflag &&
124 			    (fs->fs_type == 0 ||
125 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
126 				continue;
127 			if (!aflag &&
128 			    !(oneof(fs->fs_file, argv, argc) ||
129 			      oneof(fs->fs_spec, argv, argc)))
130 				continue;
131 			(void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname);
132 			errs += chkquota(fs->fs_spec, fs->fs_file, quotafile);
133 		}
134 		endfsent();
135 	}
136 
137 	for (i = 0; i < argc; i++)
138 		if ((done & (1 << i)) == 0)
139 			fprintf(stderr, "%s not found in %s\n",
140 				argv[i], FSTAB);
141 	exit(errs);
142 }
143 
144 preen(argc, argv)
145 	int argc;
146 	char **argv;
147 {
148 	register struct fstab *fs;
149 	register int passno, anygtr;
150 	register int errs;
151 	union wait status;
152 
153 	passno = 1;
154 	errs = 0;
155 	do {
156 		anygtr = 0;
157 
158 		if (setfsent() == 0) {
159 			fprintf(stderr, "Can't open ");
160 			perror(FSTAB);
161 			exit(8);
162 		}
163 
164 		while ((fs = getfsent()) != NULL) {
165 			if (fs->fs_passno > passno)
166 				anygtr = 1;
167 
168 			if (aflag &&
169 			    (fs->fs_type == 0 ||
170 			     strcmp(fs->fs_type, FSTAB_RQ) != 0))
171 				continue;
172 
173 			if (!aflag &&
174 			    !(oneof(fs->fs_file, argv, argc) ||
175 			      oneof(fs->fs_spec, argv, argc)))
176 				continue;
177 
178 			if (fs->fs_passno != passno)
179 				continue;
180 
181 			switch (fork()) {
182 			case -1:
183 				perror("fork");
184 				exit(8);
185 				break;
186 
187 			case 0:
188 				sprintf(quotafile, "%s/%s",
189 					fs->fs_file, qfname);
190 				exit(chkquota(fs->fs_spec,
191 					fs->fs_file, quotafile));
192 			}
193 		}
194 
195 		while (wait(&status) != -1)
196 			errs += status.w_retcode;
197 
198 		passno++;
199 	} while (anygtr);
200 
201 	return (errs);
202 }
203 
204 chkquota(fsdev, fsfile, qffile)
205 	char *fsdev;
206 	char *fsfile;
207 	char *qffile;
208 {
209 	register struct fileusage *fup;
210 	dev_t quotadev;
211 	register FILE *qfi, *qfo;
212 	u_short uid;
213 	int cg, i, fdo;
214 	char *rawdisk;
215 	struct stat statb;
216 	struct dqblk dqbuf;
217 	static int warned = 0;
218 	extern int errno;
219 
220 	rawdisk = makerawname(fsdev);
221 	if (vflag)
222 		fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile);
223 	fi = open(rawdisk, 0);
224 	if (fi < 0) {
225 		perror(rawdisk);
226 		return (1);
227 	}
228 	qfi = fopen(qffile, "r");
229 	if (qfi == NULL) {
230 		perror(qffile);
231 		close(fi);
232 		return (1);
233 	}
234 	if (fstat(fileno(qfi), &statb) < 0) {
235 		perror(qffile);
236 		fclose(qfi);
237 		close(fi);
238 		return (1);
239 	}
240 	quotadev = statb.st_dev;
241 	if (stat(fsdev, &statb) < 0) {
242 		perror(fsdev);
243 		fclose(qfi);
244 		close(fi);
245 		return (1);
246 	}
247 	if (quotadev != statb.st_rdev) {
248 		fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n",
249 			qffile, quotadev, fsdev, statb.st_rdev);
250 		fclose(qfi);
251 		close(fi);
252 		return (1);
253 	}
254 	/*
255 	 * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w")
256 	 * because fopen(qffile, "w") would truncate the quota file.
257 	 */
258 	fdo = open(qffile, 1);
259 	if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) {
260 		perror(qffile);
261 		if (fdo >= 0)
262 			close(fdo);
263 		fclose(qfi);
264 		close(fi);
265 		return (1);
266 	}
267 	if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 &&
268 	    errno == EINVAL && !warned && vflag) {
269 		warned++;
270 		fprintf(stdout,
271 		    "*** Warning: Quotas are not compiled into this kernel\n");
272 	}
273 	sync();
274 	bread(SBOFF, (char *)&sblock, SBSIZE);
275 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
276 	ino = 0;
277 	for (cg = 0; cg < sblock.fs_ncg; cg++) {
278 		dp = NULL;
279 		for (i = 0; i < sblock.fs_ipg; i++)
280 			acct(ginode());
281 	}
282 	for (uid = 0; uid <= highuid; uid++) {
283 		i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi);
284 		if (i == 0)
285 			dqbuf = zerodqbuf;
286 		fup = lookup(uid);
287 		if (fup == 0)
288 			fup = &zerofileusage;
289 		if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes &&
290 		    dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) {
291 			fup->fu_usage.du_curinodes = 0;
292 			fup->fu_usage.du_curblocks = 0;
293 			fseek(qfo, (long)sizeof(struct dqblk), 1);
294 			continue;
295 		}
296 		if (vflag) {
297 			if (pflag)
298 				printf("%s: ", rawdisk);
299 			if (fup->fu_name[0] != '\0')
300 				printf("%-8s fixed:", fup->fu_name);
301 			else
302 				printf("#%-7d fixed:", uid);
303 			if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes)
304 				fprintf(stdout, "\tinodes %d -> %d",
305 					dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes);
306 			if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks)
307 				fprintf(stdout, "\tblocks %d -> %d",
308 					dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks);
309 			fprintf(stdout, "\n");
310 		}
311 		dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes;
312 		dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks;
313 		fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo);
314 		quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage);
315 		fup->fu_usage.du_curinodes = 0;
316 		fup->fu_usage.du_curblocks = 0;
317 	}
318 	fflush(qfo);
319 	ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk)));
320 	fclose(qfi);
321 	fclose(qfo);
322 	close(fi);
323 	return (0);
324 }
325 
326 acct(ip)
327 	register struct dinode *ip;
328 {
329 	register struct fileusage *fup;
330 
331 	if (ip == NULL)
332 		return;
333 	if (ip->di_mode == 0)
334 		return;
335 	fup = adduid(ip->di_uid);
336 	fup->fu_usage.du_curinodes++;
337 	if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK)
338 		return;
339 	fup->fu_usage.du_curblocks += ip->di_blocks;
340 }
341 
342 oneof(target, list, n)
343 	char *target, *list[];
344 	register int n;
345 {
346 	register int i;
347 
348 	for (i = 0; i < n; i++)
349 		if (strcmp(target, list[i]) == 0) {
350 			done |= 1 << i;
351 			return (1);
352 		}
353 	return (0);
354 }
355 
356 struct dinode *
357 ginode()
358 {
359 	register unsigned long iblk;
360 
361 	if (dp == NULL || ++dp >= &itab[ITABSZ]) {
362 		iblk = itod(&sblock, ino);
363 		bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab);
364 		dp = &itab[ino % INOPB(&sblock)];
365 	}
366 	if (ino++ < ROOTINO)
367 		return(NULL);
368 	return(dp);
369 }
370 
371 bread(bno, buf, cnt)
372 	long unsigned bno;
373 	char *buf;
374 {
375 
376 	if (lseek(fi, bno * dev_bsize, 0) < 0) {
377 		perror("lseek");
378 		exit(1);
379 	}
380 
381 	if (read(fi, buf, cnt) != cnt) {
382 		perror("read");
383 		exit(1);
384 	}
385 }
386 
387 struct fileusage *
388 lookup(uid)
389 	u_short uid;
390 {
391 	register struct fileusage *fup;
392 
393 	for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next)
394 		if (fup->fu_uid == uid)
395 			return (fup);
396 	return ((struct fileusage *)0);
397 }
398 
399 struct fileusage *
400 adduid(uid)
401 	u_short uid;
402 {
403 	struct fileusage *fup, **fhp;
404 	extern char *calloc();
405 
406 	fup = lookup(uid);
407 	if (fup != 0)
408 		return (fup);
409 	fup = (struct fileusage *)calloc(1, sizeof(struct fileusage));
410 	if (fup == 0) {
411 		fprintf(stderr, "out of memory for fileusage structures\n");
412 		exit(1);
413 	}
414 	fhp = &fuhead[uid % FUHASH];
415 	fup->fu_next = *fhp;
416 	*fhp = fup;
417 	fup->fu_uid = uid;
418 	if (uid > highuid)
419 		highuid = uid;
420 	return (fup);
421 }
422 
423 char *
424 makerawname(name)
425 	char *name;
426 {
427 	register char *cp;
428 	char tmp, ch, *rindex();
429 	static char rawname[MAXPATHLEN];
430 
431 	strcpy(rawname, name);
432 	cp = rindex(rawname, '/');
433 	if (cp == NULL)
434 		return (name);
435 	else
436 		cp++;
437 	for (ch = 'r'; *cp != '\0'; ) {
438 		tmp = *cp;
439 		*cp++ = ch;
440 		ch = tmp;
441 	}
442 	*cp++ = ch;
443 	*cp = '\0';
444 	return (rawname);
445 }
446