xref: /original-bsd/sbin/quotacheck/quotacheck.c (revision e9b82df0)
1 /*
2  * Copyright (c) 1980, 1990 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Robert Elz at The University of Melbourne.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)quotacheck.c	5.14 (Berkeley) 06/01/90";
19 #endif /* not lint */
20 
21 /*
22  * Fix up / report on disk quotas & usage
23  */
24 #include <sys/param.h>
25 #include <ufs/dinode.h>
26 #include <ufs/fs.h>
27 #include <ufs/quota.h>
28 #include <fstab.h>
29 #include <pwd.h>
30 #include <grp.h>
31 #include <stdio.h>
32 #include <errno.h>
33 
34 union {
35 	struct	fs	sblk;
36 	char	dummy[MAXBSIZE];
37 } un;
38 #define	sblock	un.sblk
39 long dev_bsize = 1;
40 long maxino;
41 
42 struct quotaname {
43 	long	flags;
44 	char	grpqfname[MAXPATHLEN + 1];
45 	char	usrqfname[MAXPATHLEN + 1];
46 };
47 #define	HASUSR	1
48 #define	HASGRP	2
49 
50 struct fileusage {
51 	struct	fileusage *fu_next;
52 	u_long	fu_curinodes;
53 	u_long	fu_curblocks;
54 	u_long	fu_id;
55 	char	fu_name[1];
56 	/* actually bigger */
57 };
58 #define FUHASH 1024	/* must be power of two */
59 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
60 struct fileusage *lookup();
61 struct fileusage *addid();
62 struct dinode *getnextinode();
63 
64 int	aflag;			/* all file systems */
65 int	gflag;			/* check group quotas */
66 int	uflag;			/* check user quotas */
67 int	vflag;			/* verbose */
68 int	fi;			/* open disk file descriptor */
69 u_long	highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
70 
71 main(argc, argv)
72 	int argc;
73 	char **argv;
74 {
75 	register struct fstab *fs;
76 	register struct passwd *pw;
77 	register struct group *gr;
78 	int i, argnum, maxrun, errs = 0;
79 	long auxdata, done = 0;
80 	char ch, *name, *blockcheck();
81 	int needchk(), chkquota();
82 	extern char *optarg;
83 	extern int optind;
84 
85 	while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
86 		switch(ch) {
87 		case 'a':
88 			aflag++;
89 			break;
90 		case 'g':
91 			gflag++;
92 			break;
93 		case 'u':
94 			uflag++;
95 			break;
96 		case 'v':
97 			vflag++;
98 			break;
99 		case 'l':
100 			maxrun = atoi(optarg);
101 			break;
102 		default:
103 			usage();
104 		}
105 	}
106 	argc -= optind;
107 	argv += optind;
108 	if ((argc == 0 && !aflag) || (argc > 0 && aflag))
109 		usage();
110 	if (!gflag && !uflag) {
111 		gflag++;
112 		uflag++;
113 	}
114 	if (gflag) {
115 		setgrent();
116 		while ((gr = getgrent()) != 0)
117 			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
118 		endgrent();
119 	}
120 	if (uflag) {
121 		setpwent();
122 		while ((pw = getpwent()) != 0)
123 			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
124 		endpwent();
125 	}
126 	if (aflag)
127 		exit(checkfstab(1, maxrun, needchk, chkquota));
128 	if (setfsent() == 0) {
129 		fprintf(stderr, "Can't open ");
130 		perror(FSTAB);
131 		exit(8);
132 	}
133 	while ((fs = getfsent()) != NULL) {
134 		if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
135 		    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
136 		    (auxdata = needchk(fs)) &&
137 		    (name = blockcheck(fs->fs_spec))) {
138 			done |= 1 << argnum;
139 			errs += chkquota(name, fs->fs_file, auxdata);
140 		}
141 	}
142 	endfsent();
143 	for (i = 0; i < argc; i++)
144 		if ((done & (1 << i)) == 0)
145 			fprintf(stderr, "%s not found in %s\n",
146 				argv[i], FSTAB);
147 	exit(errs);
148 }
149 
150 usage()
151 {
152 
153 	fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
154 		"quotacheck [-g] [-u] [-v] -a",
155 		"quotacheck [-g] [-u] [-v] filesys ...");
156 	exit(1);
157 }
158 
159 needchk(fs)
160 	register struct fstab *fs;
161 {
162 	register struct quotaname *qnp;
163 	char *qfnp;
164 
165 	if (strcmp(fs->fs_vfstype, "ufs") ||
166 	    strcmp(fs->fs_type, FSTAB_RW))
167 		return (0);
168 	if ((qnp = (struct quotaname *)malloc(sizeof *qnp)) == 0) {
169 		fprintf(stderr, "out of memory for quota structures\n");
170 		exit(1);
171 	}
172 	qnp->flags = 0;
173 	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
174 		strcpy(qnp->grpqfname, qfnp);
175 		qnp->flags |= HASGRP;
176 	}
177 	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
178 		strcpy(qnp->usrqfname, qfnp);
179 		qnp->flags |= HASUSR;
180 	}
181 	if (qnp->flags)
182 		return ((int)qnp);
183 	free((char *)qnp);
184 	return (0);
185 }
186 
187 /*
188  * Scan the specified filesystem to check quota(s) present on it.
189  */
190 chkquota(fsname, mntpt, qnp)
191 	char *fsname, *mntpt;
192 	register struct quotaname *qnp;
193 {
194 	register struct fileusage *fup;
195 	register struct dinode *dp;
196 	int cg, i, mode, errs = 0;
197 	ino_t ino;
198 
199 	if ((fi = open(fsname, 0)) < 0) {
200 		perror(fsname);
201 		return (1);
202 	}
203 	if (vflag) {
204 		fprintf(stdout, "*** Checking ");
205 		if (qnp->flags & HASUSR)
206 			fprintf(stdout, "%s%s", qfextension[USRQUOTA],
207 			    (qnp->flags & HASGRP) ? " and " : "");
208 		if (qnp->flags & HASGRP)
209 			fprintf(stdout, "%s", qfextension[GRPQUOTA]);
210 		fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt);
211 	}
212 	sync();
213 	bread(SBOFF, (char *)&sblock, (long)SBSIZE);
214 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
215 	maxino = sblock.fs_ncg * sblock.fs_ipg;
216 	resetinodebuf();
217 	for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
218 		for (i = 0; i < sblock.fs_ipg; i++, ino++) {
219 			if (ino < ROOTINO)
220 				continue;
221 			if ((dp = getnextinode(ino)) == NULL)
222 				continue;
223 			if ((mode = dp->di_mode & IFMT) == 0)
224 				continue;
225 			if (qnp->flags & HASGRP) {
226 				fup = addid((u_long)dp->di_gid, GRPQUOTA,
227 				    (char *)0);
228 				fup->fu_curinodes++;
229 				if (mode == IFREG || mode == IFDIR ||
230 				    mode == IFLNK)
231 					fup->fu_curblocks += dp->di_blocks;
232 			}
233 			if (qnp->flags & HASUSR) {
234 				fup = addid((u_long)dp->di_uid, USRQUOTA,
235 				    (char *)0);
236 				fup->fu_curinodes++;
237 				if (mode == IFREG || mode == IFDIR ||
238 				    mode == IFLNK)
239 					fup->fu_curblocks += dp->di_blocks;
240 			}
241 		}
242 	}
243 	freeinodebuf();
244 	if (qnp->flags & HASUSR)
245 		errs += update(mntpt, qnp->usrqfname, USRQUOTA);
246 	if (qnp->flags & HASGRP)
247 		errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
248 	close(fi);
249 	return (errs);
250 }
251 
252 /*
253  * Update a specified quota file.
254  */
255 update(fsname, quotafile, type)
256 	char *fsname, *quotafile;
257 	register int type;
258 {
259 	register struct fileusage *fup;
260 	register FILE *qfi, *qfo;
261 	register u_long id, lastid;
262 	struct dqblk dqbuf;
263 	extern int errno;
264 	static int warned = 0;
265 	static struct dqblk zerodqbuf;
266 	static struct fileusage zerofileusage;
267 
268 	if ((qfo = fopen(quotafile, "r+")) == NULL) {
269 		if (errno != ENOENT) {
270 			perror(quotafile);
271 			return (1);
272 		}
273 		if ((qfo = fopen(quotafile, "w+")) == NULL) {
274 			perror(quotafile);
275 			return (1);
276 		}
277 		fprintf(stderr, "Creating quota file %s\n", quotafile);
278 		(void) fchown(fileno(qfo), getuid(), getquotagid());
279 		(void) fchmod(fileno(qfo), 0640);
280 	}
281 	if ((qfi = fopen(quotafile, "r")) == NULL) {
282 		perror(quotafile);
283 		fclose(qfo);
284 		return (1);
285 	}
286 	if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
287 	    errno == EOPNOTSUPP && !warned && vflag) {
288 		warned++;
289 		fprintf(stdout, "*** Warning: %s\n",
290 		    "Quotas are not compiled into this kernel");
291 	}
292 	for (lastid = highid[type], id = 0; id <= lastid; id++) {
293 		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
294 			dqbuf = zerodqbuf;
295 		if ((fup = lookup(id, type)) == 0)
296 			fup = &zerofileusage;
297 		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
298 		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
299 			fup->fu_curinodes = 0;
300 			fup->fu_curblocks = 0;
301 			fseek(qfo, (long)sizeof(struct dqblk), 1);
302 			continue;
303 		}
304 		if (vflag) {
305 			if (aflag)
306 				printf("%s: ", fsname);
307 			printf("%-8s fixed:", fup->fu_name);
308 			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
309 				fprintf(stdout, "\tinodes %d -> %d",
310 					dqbuf.dqb_curinodes, fup->fu_curinodes);
311 			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
312 				fprintf(stdout, "\tblocks %d -> %d",
313 					dqbuf.dqb_curblocks, fup->fu_curblocks);
314 			fprintf(stdout, "\n");
315 		}
316 		/*
317 		 * Reset time limit if have a soft limit and were
318 		 * previously under it, but are now over it.
319 		 */
320 		if (dqbuf.dqb_bsoftlimit &&
321 		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
322 		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
323 			dqbuf.dqb_btime = 0;
324 		if (dqbuf.dqb_isoftlimit &&
325 		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
326 		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
327 			dqbuf.dqb_itime = 0;
328 		dqbuf.dqb_curinodes = fup->fu_curinodes;
329 		dqbuf.dqb_curblocks = fup->fu_curblocks;
330 		fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
331 		(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
332 		    (caddr_t)&dqbuf);
333 		fup->fu_curinodes = 0;
334 		fup->fu_curblocks = 0;
335 	}
336 	fclose(qfi);
337 	fflush(qfo);
338 	ftruncate(fileno(qfo),
339 	    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
340 	fclose(qfo);
341 	return (0);
342 }
343 
344 /*
345  * Check to see if target appears in list of size cnt.
346  */
347 oneof(target, list, cnt)
348 	register char *target, *list[];
349 	int cnt;
350 {
351 	register int i;
352 
353 	for (i = 0; i < cnt; i++)
354 		if (strcmp(target, list[i]) == 0)
355 			return (i);
356 	return (-1);
357 }
358 
359 /*
360  * Determine the group identifier for quota files.
361  */
362 getquotagid()
363 {
364 	struct group *gr;
365 
366 	if (gr = getgrnam(quotagroup))
367 		return (gr->gr_gid);
368 	return (-1);
369 }
370 
371 /*
372  * Check to see if a particular quota is to be enabled.
373  */
374 hasquota(fs, type, qfnamep)
375 	register struct fstab *fs;
376 	int type;
377 	char **qfnamep;
378 {
379 	register char *opt;
380 	char *cp, *index(), *strtok();
381 	static char initname, usrname[100], grpname[100];
382 	static char buf[BUFSIZ];
383 
384 	if (!initname) {
385 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
386 		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
387 		initname = 1;
388 	}
389 	strcpy(buf, fs->fs_mntops);
390 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
391 		if (cp = index(opt, '='))
392 			*cp++ = '\0';
393 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
394 			break;
395 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
396 			break;
397 	}
398 	if (!opt)
399 		return (0);
400 	if (cp) {
401 		*qfnamep = cp;
402 		return (1);
403 	}
404 	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
405 	*qfnamep = buf;
406 	return (1);
407 }
408 
409 /*
410  * Routines to manage the file usage table.
411  *
412  * Lookup an id of a specific type.
413  */
414 struct fileusage *
415 lookup(id, type)
416 	u_long id;
417 	int type;
418 {
419 	register struct fileusage *fup;
420 
421 	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
422 		if (fup->fu_id == id)
423 			return (fup);
424 	return ((struct fileusage *)0);
425 }
426 
427 /*
428  * Add a new file usage id if it does not already exist.
429  */
430 struct fileusage *
431 addid(id, type, name)
432 	u_long id;
433 	int type;
434 	char *name;
435 {
436 	struct fileusage *fup, **fhp;
437 	int len;
438 	extern char *calloc();
439 
440 	if (fup = lookup(id, type))
441 		return (fup);
442 	if (name)
443 		len = strlen(name);
444 	else
445 		len = 10;
446 	if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) {
447 		fprintf(stderr, "out of memory for fileusage structures\n");
448 		exit(1);
449 	}
450 	fhp = &fuhead[type][id & (FUHASH - 1)];
451 	fup->fu_next = *fhp;
452 	*fhp = fup;
453 	fup->fu_id = id;
454 	if (id > highid[type])
455 		highid[type] = id;
456 	if (name) {
457 		bcopy(name, fup->fu_name, len + 1);
458 	} else {
459 		sprintf(fup->fu_name, "%u", id);
460 	}
461 	return (fup);
462 }
463 
464 /*
465  * Special purpose version of ginode used to optimize pass
466  * over all the inodes in numerical order.
467  */
468 ino_t nextino, lastinum;
469 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
470 struct dinode *inodebuf;
471 #define	INOBUFSIZE	56*1024	/* size of buffer to read inodes */
472 
473 struct dinode *
474 getnextinode(inumber)
475 	ino_t inumber;
476 {
477 	long size;
478 	daddr_t dblk;
479 	static struct dinode *dp;
480 
481 	if (inumber != nextino++ || inumber > maxino) {
482 		fprintf(stderr, "bad inode number %d to nextinode\n", inumber);
483 		exit(1);
484 	}
485 	if (inumber >= lastinum) {
486 		readcnt++;
487 		dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
488 		if (readcnt % readpercg == 0) {
489 			size = partialsize;
490 			lastinum += partialcnt;
491 		} else {
492 			size = inobufsize;
493 			lastinum += fullcnt;
494 		}
495 		bread(dblk, (char *)inodebuf, size);
496 		dp = inodebuf;
497 	}
498 	return (dp++);
499 }
500 
501 /*
502  * Prepare to scan a set of inodes.
503  */
504 resetinodebuf()
505 {
506 
507 	nextino = 0;
508 	lastinum = 0;
509 	readcnt = 0;
510 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
511 	fullcnt = inobufsize / sizeof(struct dinode);
512 	readpercg = sblock.fs_ipg / fullcnt;
513 	partialcnt = sblock.fs_ipg % fullcnt;
514 	partialsize = partialcnt * sizeof(struct dinode);
515 	if (partialcnt != 0) {
516 		readpercg++;
517 	} else {
518 		partialcnt = fullcnt;
519 		partialsize = inobufsize;
520 	}
521 	if (inodebuf == NULL &&
522 	   (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) {
523 		fprintf(stderr, "Cannot allocate space for inode buffer\n");
524 		exit(1);
525 	}
526 	while (nextino < ROOTINO)
527 		getnextinode(nextino);
528 }
529 
530 /*
531  * Free up data structures used to scan inodes.
532  */
533 freeinodebuf()
534 {
535 
536 	if (inodebuf != NULL)
537 		free((char *)inodebuf);
538 	inodebuf = NULL;
539 }
540 
541 /*
542  * Read specified disk blocks.
543  */
544 bread(bno, buf, cnt)
545 	daddr_t bno;
546 	char *buf;
547 	long cnt;
548 {
549 
550 	if (lseek(fi, bno * dev_bsize, 0) < 0) {
551 		perror("lseek");
552 		exit(1);
553 	}
554 
555 	if (read(fi, buf, cnt) != cnt) {
556 		perror("read");
557 		exit(1);
558 	}
559 }
560