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