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