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