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