quotacheck.c revision 1.41 1 /* $NetBSD: quotacheck.c,v 1.41 2011/03/06 17:08:43 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)quotacheck.c 8.6 (Berkeley) 4/28/95";
44 #else
45 __RCSID("$NetBSD: quotacheck.c,v 1.41 2011/03/06 17:08:43 bouyer Exp $");
46 #endif
47 #endif /* not lint */
48
49 /*
50 * Fix up / report on disk quotas & usage
51 */
52 #include <sys/param.h>
53 #include <sys/stat.h>
54 #include <sys/queue.h>
55 #include <sys/statvfs.h>
56
57 #include <ufs/ufs/dinode.h>
58 #include <ufs/ufs/quota1.h>
59 #include <ufs/ufs/ufs_bswap.h>
60 #include <ufs/ffs/fs.h>
61 #include <ufs/ffs/ffs_extern.h>
62
63 #include <err.h>
64 #include <fcntl.h>
65 #include <fstab.h>
66 #include <pwd.h>
67 #include <grp.h>
68 #include <errno.h>
69 #include <unistd.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73
74 #include "fsutil.h"
75
76 #ifndef FS_UFS1_MAGIC
77 # define FS_UFS1_MAGIC FS_MAGIC /* 0x011954 */
78 # define FS_UFS1_MAGIC_SWAPPED 0x54190100 /* bswap32(0x011954) */
79 # define DINODE1_SIZE sizeof(struct dinode)
80 # define DINODE2_SIZE 0
81 #else
82 # define HAVE_UFSv2 1
83 #endif
84
85 #ifndef SBLOCKSIZE
86 # define SBLOCKSIZE SBSIZE
87 #endif
88 #ifndef SBLOCKSEARCH
89 # define SBLOCKSEARCH { SBSIZE, -1 }
90 #endif
91
92 static const char *qfname = QUOTAFILENAME;
93 static const char *qfextension[] = INITQFNAMES;
94 static const char *quotagroup = QUOTAGROUP;
95
96 static union {
97 struct fs sblk;
98 char dummy[MAXBSIZE];
99 } un;
100 #define sblock un.sblk
101 static long dev_bsize;
102 static long maxino;
103
104 struct quotaname {
105 long flags;
106 char grpqfname[MAXPATHLEN + 1];
107 char usrqfname[MAXPATHLEN + 1];
108 };
109 #define HASUSR 1
110 #define HASGRP 2
111
112 struct fileusage {
113 struct fileusage *fu_next;
114 u_long fu_curinodes;
115 u_long fu_curblocks;
116 u_int32_t fu_id; /* uid_t, gid_t */
117 char fu_name[1];
118 /* actually bigger */
119 };
120 #define FUHASH 1024 /* must be power of two */
121 static struct fileusage *fuhead[MAXQUOTAS][FUHASH];
122
123
124 union comb_dinode {
125 #ifdef HAVE_UFSv2
126 struct ufs1_dinode dp1;
127 struct ufs2_dinode dp2;
128 #else
129 struct dinode dp1;
130 #endif
131 };
132 #ifdef HAVE_UFSv2
133 #define DIP(dp, field) \
134 (is_ufs2 ? (dp)->dp2.di_##field : (dp)->dp1.di_##field)
135 #else
136 #define DIP(dp, field) (dp)->dp1.di_##field
137 #endif
138
139
140 static int aflag; /* all file systems */
141 static int gflag; /* check group quotas */
142 static int uflag; /* check user quotas */
143 static int vflag; /* verbose */
144 static int qflag; /* quick but untidy mode */
145 static int fi; /* open disk file descriptor */
146 static u_int32_t highid[MAXQUOTAS];/* highest addid()'ed identifier per type */
147 static int needswap; /* FS is in swapped order */
148 static int got_siginfo = 0; /* got a siginfo signal */
149 static int is_ufs2;
150
151
152 int main __P((int, char *[]));
153 static void usage __P((void));
154 static void *needchk __P((struct fstab *));
155 static int chkquota __P((const char *, const char *, const char *, void *,
156 pid_t *));
157 static int update __P((const char *, const char *, int));
158 static u_int32_t skipforward __P((u_int32_t, u_int32_t, FILE *));
159 static int oneof __P((const char *, char *[], int));
160 static int getquotagid __P((void));
161 static int hasquota __P((struct fstab *, int, char **));
162 static struct fileusage *lookup __P((u_int32_t, int));
163 static struct fileusage *addid __P((u_int32_t, int, const char *));
164 static u_int32_t subsequent __P((u_int32_t, int));
165 static union comb_dinode *getnextinode __P((ino_t));
166 static void setinodebuf __P((ino_t));
167 static void freeinodebuf __P((void));
168 static void bread __P((daddr_t, char *, long));
169 static void infohandler __P((int sig));
170 static void swap_dinode1(union comb_dinode *, int);
171 #ifdef HAVE_UFSv2
172 static void swap_dinode2(union comb_dinode *, int);
173 #endif
174
175 int
176 main(argc, argv)
177 int argc;
178 char *argv[];
179 {
180 struct fstab *fs;
181 struct passwd *pw;
182 struct group *gr;
183 struct quotaname *auxdata;
184 int i, argnum, maxrun, errs;
185 long done = 0;
186 int flags = CHECK_PREEN;
187 const char *name;
188 int ch;
189
190 errs = maxrun = 0;
191 while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) {
192 switch(ch) {
193 case 'a':
194 aflag++;
195 break;
196 case 'd':
197 flags |= CHECK_DEBUG;
198 break;
199 case 'g':
200 gflag++;
201 break;
202 case 'u':
203 uflag++;
204 break;
205 case 'q':
206 qflag++;
207 break;
208 case 'v':
209 vflag++;
210 break;
211 case 'l':
212 maxrun = atoi(optarg);
213 break;
214 default:
215 usage();
216 }
217 }
218 argc -= optind;
219 argv += optind;
220 if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun))
221 usage();
222 if (!gflag && !uflag) {
223 gflag++;
224 uflag++;
225 }
226
227 /* If -a, we do not want to pay the cost of processing every
228 * group and password entry if there are no filesystems with quotas
229 */
230 if (aflag) {
231 i = 0;
232 while ((fs = getfsent()) != NULL) {
233 if (needchk(fs))
234 i=1;
235 }
236 endfsent();
237 if (!i) /* No filesystems with quotas */
238 exit(0);
239 }
240
241 if (gflag) {
242 setgrent();
243 while ((gr = getgrent()) != 0)
244 (void) addid((u_int32_t)gr->gr_gid, GRPQUOTA, gr->gr_name);
245 endgrent();
246 }
247 if (uflag) {
248 setpwent();
249 while ((pw = getpwent()) != 0)
250 (void) addid((u_int32_t)pw->pw_uid, USRQUOTA, pw->pw_name);
251 endpwent();
252 }
253 if (aflag)
254 exit(checkfstab(flags, maxrun, needchk, chkquota));
255 if (setfsent() == 0)
256 err(1, "%s: can't open", FSTAB);
257 while ((fs = getfsent()) != NULL) {
258 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
259 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
260 (auxdata = needchk(fs)) &&
261 (name = blockcheck(fs->fs_spec))) {
262 done |= 1 << argnum;
263 errs += chkquota(fs->fs_type, name, fs->fs_file,
264 auxdata, NULL);
265 }
266 }
267 endfsent();
268 for (i = 0; i < argc; i++)
269 if ((done & (1 << i)) == 0)
270 fprintf(stderr, "%s not found in %s\n",
271 argv[i], FSTAB);
272 exit(errs);
273 }
274
275 static void
276 usage()
277 {
278
279 (void)fprintf(stderr,
280 "usage:\t%s -a [-gquv] [-l maxparallel]\n\t%s [-gquv] filesys ...\n", getprogname(),
281 getprogname());
282 exit(1);
283 }
284
285 static void *
286 needchk(fs)
287 struct fstab *fs;
288 {
289 struct quotaname *qnp;
290 char *qfnp;
291
292 if (strcmp(fs->fs_vfstype, "ffs") ||
293 strcmp(fs->fs_type, FSTAB_RW))
294 return (NULL);
295 if ((qnp = malloc(sizeof(*qnp))) == NULL)
296 err(1, "%s", strerror(errno));
297 qnp->flags = 0;
298 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
299 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
300 qnp->flags |= HASGRP;
301 }
302 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
303 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
304 qnp->flags |= HASUSR;
305 }
306 if (qnp->flags)
307 return (qnp);
308 free(qnp);
309 return (NULL);
310 }
311
312 off_t sblock_try[] = SBLOCKSEARCH;
313
314 /*
315 * Scan the specified filesystem to check quota(s) present on it.
316 */
317 static int
318 chkquota(type, fsname, mntpt, v, pid)
319 const char *type, *fsname, *mntpt;
320 void *v;
321 pid_t *pid;
322 {
323 struct quotaname *qnp = v;
324 struct fileusage *fup;
325 union comb_dinode *dp;
326 int cg, i, mode, errs = 0, inosused;
327 ino_t ino;
328 struct cg *cgp;
329 char msgbuf[4096];
330
331 if (pid != NULL) {
332 fflush(stdout);
333 switch ((*pid = fork())) {
334 default:
335 return 0;
336 case 0:
337 break;
338 case -1:
339 err(1, "Cannot fork");
340 }
341 setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf);
342 }
343
344 if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
345 warn("Cannot open %s", fsname);
346 if (pid != NULL)
347 exit(1);
348 return 1;
349 }
350 if (vflag) {
351 (void)printf("*** Checking ");
352 if (qnp->flags & HASUSR)
353 (void)printf("%s%s", qfextension[USRQUOTA],
354 (qnp->flags & HASGRP) ? " and " : "");
355 if (qnp->flags & HASGRP)
356 (void)printf("%s", qfextension[GRPQUOTA]);
357 (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
358 fflush(stdout);
359 }
360 signal(SIGINFO, infohandler);
361 sync();
362 dev_bsize = 1;
363
364 for (i = 0; ; i++) {
365 if (sblock_try[i] == -1) {
366 warnx("%s: superblock not found", fsname);
367 if (pid != NULL)
368 exit(1);
369 return 1;
370 }
371 bread(sblock_try[i], (char *)&sblock, SBLOCKSIZE);
372 switch (sblock.fs_magic) {
373 #ifdef HAVE_UFSv2
374 case FS_UFS2_MAGIC:
375 is_ufs2 = 1;
376 /*FALLTHROUGH*/
377 #endif
378 case FS_UFS1_MAGIC:
379 break;
380 #ifdef HAVE_UFSv2
381 case FS_UFS2_MAGIC_SWAPPED:
382 is_ufs2 = 1;
383 /*FALLTHROUGH*/
384 #endif
385 case FS_UFS1_MAGIC_SWAPPED:
386 needswap = 1;
387 ffs_sb_swap(&sblock, &sblock);
388 break;
389 default:
390 continue;
391 }
392
393 #ifdef HAVE_UFSv2
394 if (is_ufs2 || sblock.fs_old_flags & FS_FLAGS_UPDATED) {
395 if (sblock.fs_sblockloc != sblock_try[i])
396 continue;
397 } else {
398 if (sblock_try[i] == SBLOCK_UFS2)
399 continue;
400 }
401 #endif
402 break;
403 }
404
405 cgp = malloc(sblock.fs_cgsize);
406 if (cgp == NULL) {
407 warn("%s: can't allocate %d bytes of cg space", fsname,
408 sblock.fs_cgsize);
409 if (pid != NULL)
410 exit(1);
411 return 1;
412 }
413
414 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
415 maxino = sblock.fs_ncg * sblock.fs_ipg;
416 for (cg = 0; cg < sblock.fs_ncg; cg++) {
417 ino = cg * sblock.fs_ipg;
418 setinodebuf(ino);
419 #ifdef HAVE_UFSv2
420 if (sblock.fs_magic == FS_UFS2_MAGIC) {
421 bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)cgp,
422 sblock.fs_cgsize);
423 if (needswap)
424 ffs_cg_swap(cgp, cgp, &sblock);
425 inosused = cgp->cg_initediblk;
426 } else
427 #endif
428 inosused = sblock.fs_ipg;
429 for (i = 0; i < inosused; i++, ino++) {
430 if (got_siginfo) {
431 fprintf(stderr,
432 "%s: cyl group %d of %d (%d%%)\n",
433 fsname, cg, sblock.fs_ncg,
434 cg * 100 / sblock.fs_ncg);
435 got_siginfo = 0;
436 }
437 if (ino < ROOTINO)
438 continue;
439 if ((dp = getnextinode(ino)) == NULL)
440 continue;
441 if ((mode = DIP(dp, mode) & IFMT) == 0)
442 continue;
443 if (qnp->flags & HASGRP) {
444 fup = addid(DIP(dp, gid), GRPQUOTA,
445 (char *)0);
446 fup->fu_curinodes++;
447 if (mode == IFREG || mode == IFDIR ||
448 mode == IFLNK)
449 fup->fu_curblocks += DIP(dp, blocks);
450 }
451 if (qnp->flags & HASUSR) {
452 fup = addid(DIP(dp, uid), USRQUOTA,
453 (char *)0);
454 fup->fu_curinodes++;
455 if (mode == IFREG || mode == IFDIR ||
456 mode == IFLNK)
457 fup->fu_curblocks += DIP(dp, blocks);
458 }
459 }
460 }
461 freeinodebuf();
462 free(cgp);
463 if (qnp->flags & HASUSR)
464 errs += update(mntpt, qnp->usrqfname, USRQUOTA);
465 if (qnp->flags & HASGRP)
466 errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
467 close(fi);
468 if (pid != NULL)
469 exit(errs);
470 return errs;
471 }
472
473 /*
474 * Update a specified quota file.
475 */
476 static int
477 update(fsname, quotafile, type)
478 const char *fsname, *quotafile;
479 int type;
480 {
481 struct fileusage *fup;
482 FILE *qfi, *qfo;
483 u_int32_t id, lastid, nextid;
484 int need_seek;
485 struct dqblk dqbuf;
486 static struct dqblk zerodqbuf;
487 static struct fileusage zerofileusage;
488 struct statvfs *fst;
489 int nfst, i;
490
491 nfst = getmntinfo(&fst, MNT_WAIT);
492 if (nfst == 0)
493 errx(1, "no filesystems mounted!");
494
495 for (i = 0; i < nfst; i++) {
496 if (strncmp(fst[i].f_fstypename, "ffs",
497 sizeof(fst[i].f_fstypename)) == 0 &&
498 strncmp(fst[i].f_mntonname, fsname,
499 sizeof(fst[i].f_mntonname)) == 0 &&
500 (fst[i].f_flag & ST_QUOTA) != 0) {
501 warnx("filesystem %s has quotas already turned on",
502 fsname);
503 }
504 }
505
506 if ((qfo = fopen(quotafile, "r+")) == NULL) {
507 if (errno == ENOENT)
508 qfo = fopen(quotafile, "w+");
509 if (qfo) {
510 (void) fprintf(stderr,
511 "quotacheck: creating quota file %s\n", quotafile);
512 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
513 (void) fchown(fileno(qfo), getuid(), getquotagid());
514 (void) fchmod(fileno(qfo), MODE);
515 } else {
516 (void) fprintf(stderr,
517 "quotacheck: %s: %s\n", quotafile, strerror(errno));
518 return (1);
519 }
520 }
521 if ((qfi = fopen(quotafile, "r")) == NULL) {
522 (void) fprintf(stderr,
523 "quotacheck: %s: %s\n", quotafile, strerror(errno));
524 (void) fclose(qfo);
525 return (1);
526 }
527 need_seek = 1;
528 for (lastid = highid[type], id = 0; id <= lastid; id = nextid) {
529 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
530 dqbuf = zerodqbuf;
531 if ((fup = lookup(id, type)) == 0)
532 fup = &zerofileusage;
533
534 nextid = subsequent(id, type);
535 if (nextid > 0 && nextid != id + 1) /* watch out for id == UINT32_MAX */
536 nextid = skipforward(id, nextid, qfi);
537
538 if (got_siginfo) {
539 /* XXX this could try to show percentage through the ID list */
540 fprintf(stderr,
541 "%s: updating %s quotas for id=%" PRIu32 " (%s)\n", fsname,
542 qfextension[type < MAXQUOTAS ? type : MAXQUOTAS],
543 id, fup->fu_name);
544 got_siginfo = 0;
545 }
546 if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
547 dqbuf.dqb_curblocks == fup->fu_curblocks) {
548 fup->fu_curinodes = 0; /* reset usage */
549 fup->fu_curblocks = 0; /* for next filesystem */
550
551 need_seek = 1;
552 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */
553 break;
554 }
555 continue;
556 }
557 if (vflag) {
558 if (aflag)
559 printf("%s: ", fsname);
560 printf("%-8s fixed:", fup->fu_name);
561 if (dqbuf.dqb_curinodes != fup->fu_curinodes)
562 (void)printf("\tinodes %d -> %ld",
563 dqbuf.dqb_curinodes, fup->fu_curinodes);
564 if (dqbuf.dqb_curblocks != fup->fu_curblocks)
565 (void)printf("\tblocks %d -> %ld",
566 dqbuf.dqb_curblocks, fup->fu_curblocks);
567 (void)printf("\n");
568 }
569 /*
570 * Reset time limit if have a soft limit and were
571 * previously under it, but are now over it.
572 */
573 if (dqbuf.dqb_bsoftlimit &&
574 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
575 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
576 dqbuf.dqb_btime = 0;
577 if (dqbuf.dqb_isoftlimit &&
578 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
579 fup->fu_curinodes >= dqbuf.dqb_isoftlimit)
580 dqbuf.dqb_itime = 0;
581 dqbuf.dqb_curinodes = fup->fu_curinodes;
582 dqbuf.dqb_curblocks = fup->fu_curblocks;
583
584 if (need_seek) {
585 (void) fseeko(qfo, (off_t)id * sizeof(struct dqblk),
586 SEEK_SET);
587 need_seek = nextid != id + 1;
588 }
589 (void) fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
590
591 fup->fu_curinodes = 0;
592 fup->fu_curblocks = 0;
593 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */
594 break;
595 }
596 }
597 (void) fclose(qfi);
598 (void) fflush(qfo);
599 if (highid[type] != UINT32_MAX)
600 (void) ftruncate(fileno(qfo),
601 (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
602 (void) fclose(qfo);
603 return (0);
604 }
605
606 u_int32_t
607 skipforward(cur, to, qfi)
608 u_int32_t cur, to;
609 FILE *qfi;
610 {
611 struct dqblk dqbuf;
612
613 if (qflag) {
614 (void) fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET);
615 return (to);
616 }
617
618 while (++cur < to) {
619 /*
620 * if EOF occurs, nothing left to read, we're done
621 */
622 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
623 return (to);
624
625 /*
626 * If we find an entry that shows usage, before the next
627 * id that has actual usage, we have to stop here, so the
628 * incorrect entry can be corrected in the file
629 */
630 if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) {
631 (void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR);
632 return (cur);
633 }
634 }
635 return (to);
636 }
637
638 /*
639 * Check to see if target appears in list of size cnt.
640 */
641 static int
642 oneof(target, list, cnt)
643 const char *target;
644 char *list[];
645 int cnt;
646 {
647 int i;
648
649 for (i = 0; i < cnt; i++)
650 if (strcmp(target, list[i]) == 0)
651 return (i);
652 return (-1);
653 }
654
655 /*
656 * Determine the group identifier for quota files.
657 */
658 static int
659 getquotagid()
660 {
661 struct group *gr;
662
663 if ((gr = getgrnam(quotagroup)) != NULL)
664 return (gr->gr_gid);
665 return (-1);
666 }
667
668 /*
669 * Check to see if a particular quota is to be enabled.
670 */
671 static int
672 hasquota(fs, type, qfnamep)
673 struct fstab *fs;
674 int type;
675 char **qfnamep;
676 {
677 char *opt;
678 char *cp = NULL;
679 static char initname, usrname[100], grpname[100];
680 static char buf[BUFSIZ];
681
682 if (!initname) {
683 (void)snprintf(usrname, sizeof(usrname),
684 "%s%s", qfextension[USRQUOTA], qfname);
685 (void)snprintf(grpname, sizeof(grpname),
686 "%s%s", qfextension[GRPQUOTA], qfname);
687 initname = 1;
688 }
689 (void) strlcpy(buf, fs->fs_mntops, sizeof(buf));
690 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
691 if ((cp = strchr(opt, '=')) != NULL)
692 *cp++ = '\0';
693 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
694 break;
695 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
696 break;
697 }
698 if (!opt)
699 return (0);
700 if (cp)
701 *qfnamep = cp;
702 else {
703 (void)snprintf(buf, sizeof(buf),
704 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
705 *qfnamep = buf;
706 }
707 return (1);
708 }
709
710 /*
711 * Routines to manage the file usage table.
712 *
713 * Lookup an id of a specific type.
714 */
715 static struct fileusage *
716 lookup(id, type)
717 u_int32_t id;
718 int type;
719 {
720 struct fileusage *fup;
721
722 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
723 if (fup->fu_id == id)
724 return (fup);
725 return (NULL);
726 }
727
728 /*
729 * Add a new file usage id if it does not already exist.
730 */
731 static struct fileusage *
732 addid(id, type, name)
733 u_int32_t id;
734 int type;
735 const char *name;
736 {
737 struct fileusage *fup, **fhp;
738 int len;
739
740 if ((fup = lookup(id, type)) != NULL)
741 return (fup);
742 if (name)
743 len = strlen(name);
744 else
745 len = 10;
746 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
747 err(1, "%s", strerror(errno));
748 fhp = &fuhead[type][id & (FUHASH - 1)];
749 fup->fu_next = *fhp;
750 *fhp = fup;
751 fup->fu_id = id;
752 if (id > highid[type])
753 highid[type] = id;
754 if (name)
755 memmove(fup->fu_name, name, len + 1);
756 else
757 (void) sprintf(fup->fu_name, "%" PRIu32, id);
758 return (fup);
759 }
760
761 static u_int32_t
762 subsequent(id, type)
763 u_int32_t id;
764 int type;
765 {
766 struct fileusage *fup, **iup, **cup;
767 u_int32_t next, offset;
768
769 next = highid[type] + 1;
770 offset = 0;
771 cup = iup = &fuhead[type][id & (FUHASH-1)];
772 do {
773 ++offset;
774 if (++cup >= &fuhead[type][FUHASH])
775 cup = &fuhead[type][0];
776 for (fup = *cup; fup != 0; fup = fup->fu_next) {
777 if (fup->fu_id > id && fup->fu_id <= id + offset)
778 return (fup->fu_id);
779 if (fup->fu_id > id && fup->fu_id < next)
780 next = fup->fu_id;
781 }
782 } while (cup != iup);
783
784 return next;
785 }
786
787 /*
788 * Special purpose version of ginode used to optimize first pass
789 * over all the inodes in numerical order.
790 */
791 static ino_t nextino, lastinum, lastvalidinum;
792 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
793 static union comb_dinode *inodebuf;
794 #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
795
796 union comb_dinode *
797 getnextinode(inumber)
798 ino_t inumber;
799 {
800 long size;
801 daddr_t dblk;
802 static union comb_dinode *dp;
803 union comb_dinode *ret;
804
805 if (inumber != nextino++ || inumber > lastvalidinum) {
806 errx(1, "bad inode number %llu to nextinode",
807 (unsigned long long)inumber);
808 }
809
810 if (inumber >= lastinum) {
811 readcnt++;
812 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
813 if (readcnt % readpercg == 0) {
814 size = partialsize;
815 lastinum += partialcnt;
816 } else {
817 size = inobufsize;
818 lastinum += fullcnt;
819 }
820 (void)bread(dblk, (caddr_t)inodebuf, size);
821 if (needswap) {
822 #ifdef HAVE_UFSv2
823 if (is_ufs2)
824 swap_dinode2(inodebuf, lastinum - inumber);
825 else
826 #endif
827 swap_dinode1(inodebuf, lastinum - inumber);
828 }
829 dp = (union comb_dinode *)inodebuf;
830 }
831 ret = dp;
832 dp = (union comb_dinode *)
833 ((char *)dp + (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE));
834 return ret;
835 }
836
837 void
838 setinodebuf(inum)
839 ino_t inum;
840 {
841
842 if (inum % sblock.fs_ipg != 0)
843 errx(1, "bad inode number %llu to setinodebuf",
844 (unsigned long long)inum);
845
846 lastvalidinum = inum + sblock.fs_ipg - 1;
847 nextino = inum;
848 lastinum = inum;
849 readcnt = 0;
850 if (inodebuf != NULL)
851 return;
852 inobufsize = blkroundup(&sblock, INOBUFSIZE);
853 fullcnt = inobufsize / (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
854 readpercg = sblock.fs_ipg / fullcnt;
855 partialcnt = sblock.fs_ipg % fullcnt;
856 partialsize = partialcnt * (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
857 if (partialcnt != 0) {
858 readpercg++;
859 } else {
860 partialcnt = fullcnt;
861 partialsize = inobufsize;
862 }
863 if (inodebuf == NULL &&
864 (inodebuf = malloc((unsigned)inobufsize)) == NULL)
865 errx(1, "Cannot allocate space for inode buffer");
866 while (nextino < ROOTINO)
867 getnextinode(nextino);
868 }
869
870 void
871 freeinodebuf()
872 {
873
874 if (inodebuf != NULL)
875 free((char *)inodebuf);
876 inodebuf = NULL;
877 }
878
879
880 #ifdef HAVE_UFSv2
881 static void
882 swap_dinode1(union comb_dinode *dp, int n)
883 {
884 int i;
885 struct ufs1_dinode *dp1;
886
887 dp1 = (struct ufs1_dinode *)&dp->dp1;
888 for (i = 0; i < n; i++, dp1++)
889 ffs_dinode1_swap(dp1, dp1);
890 }
891
892 static void
893 swap_dinode2(union comb_dinode *dp, int n)
894 {
895 int i;
896 struct ufs2_dinode *dp2;
897
898 dp2 = (struct ufs2_dinode *)&dp->dp2;
899 for (i = 0; i < n; i++, dp2++)
900 ffs_dinode2_swap(dp2, dp2);
901 }
902
903 #else
904
905 static void
906 swap_dinode1(union comb_dinode *dp, int n)
907 {
908 int i;
909 struct dinode *dp1;
910
911 dp1 = (struct dinode *) &dp->dp1;
912 for (i = 0; i < n; i++, dp1++)
913 ffs_dinode_swap(dp1, dp1);
914 }
915
916 #endif
917
918 /*
919 * Read specified disk blocks.
920 */
921 static void
922 bread(bno, buf, cnt)
923 daddr_t bno;
924 char *buf;
925 long cnt;
926 {
927
928 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
929 read(fi, buf, cnt) != cnt)
930 err(1, "block %lld", (long long)bno);
931 }
932
933 void
934 infohandler(int sig)
935 {
936 got_siginfo = 1;
937 }
938