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