Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.sbin / quotacheck / quotacheck.c
blob7e5b250a3c99ec8f3ea6af00b9e022984d7fb1c7
1 /* $NetBSD: quotacheck.c,v 1.39 2008/07/21 13:36:59 lukem Exp $ */
3 /*
4 * Copyright (c) 1980, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
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.
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.
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 */
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.39 2008/07/21 13:36:59 lukem Exp $");
46 #endif
47 #endif /* not lint */
50 * Fix up / report on disk quotas & usage
52 #include <sys/param.h>
53 #include <sys/stat.h>
54 #include <sys/queue.h>
56 #include <ufs/ufs/dinode.h>
57 #include <ufs/ufs/quota.h>
58 #include <ufs/ufs/ufs_bswap.h>
59 #include <ufs/ffs/fs.h>
60 #include <ufs/ffs/ffs_extern.h>
62 #include <err.h>
63 #include <fcntl.h>
64 #include <fstab.h>
65 #include <pwd.h>
66 #include <grp.h>
67 #include <errno.h>
68 #include <unistd.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
73 #include "fsutil.h"
75 #ifndef FS_UFS1_MAGIC
76 # define FS_UFS1_MAGIC FS_MAGIC /* 0x011954 */
77 # define FS_UFS1_MAGIC_SWAPPED 0x54190100 /* bswap32(0x011954) */
78 # define DINODE1_SIZE sizeof(struct dinode)
79 # define DINODE2_SIZE 0
80 #else
81 # define HAVE_UFSv2 1
82 #endif
84 #ifndef SBLOCKSIZE
85 # define SBLOCKSIZE SBSIZE
86 #endif
87 #ifndef SBLOCKSEARCH
88 # define SBLOCKSEARCH { SBSIZE, -1 }
89 #endif
91 static const char *qfname = QUOTAFILENAME;
92 static const char *qfextension[] = INITQFNAMES;
93 static const char *quotagroup = QUOTAGROUP;
95 static union {
96 struct fs sblk;
97 char dummy[MAXBSIZE];
98 } un;
99 #define sblock un.sblk
100 static long dev_bsize;
101 static long maxino;
103 struct quotaname {
104 long flags;
105 char grpqfname[MAXPATHLEN + 1];
106 char usrqfname[MAXPATHLEN + 1];
108 #define HASUSR 1
109 #define HASGRP 2
111 struct fileusage {
112 struct fileusage *fu_next;
113 u_long fu_curinodes;
114 u_long fu_curblocks;
115 u_int32_t fu_id; /* uid_t, gid_t */
116 char fu_name[1];
117 /* actually bigger */
119 #define FUHASH 1024 /* must be power of two */
120 static struct fileusage *fuhead[MAXQUOTAS][FUHASH];
123 union comb_dinode {
124 #ifdef HAVE_UFSv2
125 struct ufs1_dinode dp1;
126 struct ufs2_dinode dp2;
127 #else
128 struct dinode dp1;
129 #endif
131 #ifdef HAVE_UFSv2
132 #define DIP(dp, field) \
133 (is_ufs2 ? (dp)->dp2.di_##field : (dp)->dp1.di_##field)
134 #else
135 #define DIP(dp, field) (dp)->dp1.di_##field
136 #endif
139 static int aflag; /* all file systems */
140 static int gflag; /* check group quotas */
141 static int uflag; /* check user quotas */
142 static int vflag; /* verbose */
143 static int qflag; /* quick but untidy mode */
144 static int fi; /* open disk file descriptor */
145 static u_int32_t highid[MAXQUOTAS];/* highest addid()'ed identifier per type */
146 static int needswap; /* FS is in swapped order */
147 static int got_siginfo = 0; /* got a siginfo signal */
148 static int is_ufs2;
151 int main __P((int, char *[]));
152 static void usage __P((void));
153 static void *needchk __P((struct fstab *));
154 static int chkquota __P((const char *, const char *, const char *, void *,
155 pid_t *));
156 static int update __P((const char *, const char *, int));
157 static u_int32_t skipforward __P((u_int32_t, u_int32_t, FILE *));
158 static int oneof __P((const char *, char *[], int));
159 static int getquotagid __P((void));
160 static int hasquota __P((struct fstab *, int, char **));
161 static struct fileusage *lookup __P((u_int32_t, int));
162 static struct fileusage *addid __P((u_int32_t, int, const char *));
163 static u_int32_t subsequent __P((u_int32_t, int));
164 static union comb_dinode *getnextinode __P((ino_t));
165 static void setinodebuf __P((ino_t));
166 static void freeinodebuf __P((void));
167 static void bread __P((daddr_t, char *, long));
168 static void infohandler __P((int sig));
169 static void swap_dinode1(union comb_dinode *, int);
170 #ifdef HAVE_UFSv2
171 static void swap_dinode2(union comb_dinode *, int);
172 #endif
175 main(argc, argv)
176 int argc;
177 char *argv[];
179 struct fstab *fs;
180 struct passwd *pw;
181 struct group *gr;
182 struct quotaname *auxdata;
183 int i, argnum, maxrun, errs;
184 long done = 0;
185 int flags = CHECK_PREEN;
186 const char *name;
187 int ch;
189 errs = maxrun = 0;
190 while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) {
191 switch(ch) {
192 case 'a':
193 aflag++;
194 break;
195 case 'd':
196 flags |= CHECK_DEBUG;
197 break;
198 case 'g':
199 gflag++;
200 break;
201 case 'u':
202 uflag++;
203 break;
204 case 'q':
205 qflag++;
206 break;
207 case 'v':
208 vflag++;
209 break;
210 case 'l':
211 maxrun = atoi(optarg);
212 break;
213 default:
214 usage();
217 argc -= optind;
218 argv += optind;
219 if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun))
220 usage();
221 if (!gflag && !uflag) {
222 gflag++;
223 uflag++;
226 /* If -a, we do not want to pay the cost of processing every
227 * group and password entry if there are no filesystems with quotas
229 if (aflag) {
230 i = 0;
231 while ((fs = getfsent()) != NULL) {
232 if (needchk(fs))
233 i=1;
235 endfsent();
236 if (!i) /* No filesystems with quotas */
237 exit(0);
240 if (gflag) {
241 setgrent();
242 while ((gr = getgrent()) != 0)
243 (void) addid((u_int32_t)gr->gr_gid, GRPQUOTA, gr->gr_name);
244 endgrent();
246 if (uflag) {
247 setpwent();
248 while ((pw = getpwent()) != 0)
249 (void) addid((u_int32_t)pw->pw_uid, USRQUOTA, pw->pw_name);
250 endpwent();
252 if (aflag)
253 exit(checkfstab(flags, maxrun, needchk, chkquota));
254 if (setfsent() == 0)
255 err(1, "%s: can't open", FSTAB);
256 while ((fs = getfsent()) != NULL) {
257 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
258 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
259 (auxdata = needchk(fs)) &&
260 (name = blockcheck(fs->fs_spec))) {
261 done |= 1 << argnum;
262 errs += chkquota(fs->fs_type, name, fs->fs_file,
263 auxdata, NULL);
266 endfsent();
267 for (i = 0; i < argc; i++)
268 if ((done & (1 << i)) == 0)
269 fprintf(stderr, "%s not found in %s\n",
270 argv[i], FSTAB);
271 exit(errs);
274 static void
275 usage()
278 (void)fprintf(stderr,
279 "usage:\t%s -a [-gquv] [-l maxparallel]\n\t%s [-gquv] filesys ...\n", getprogname(),
280 getprogname());
281 exit(1);
284 static void *
285 needchk(fs)
286 struct fstab *fs;
288 struct quotaname *qnp;
289 char *qfnp;
291 if (strcmp(fs->fs_vfstype, "ffs") ||
292 strcmp(fs->fs_type, FSTAB_RW))
293 return (NULL);
294 if ((qnp = malloc(sizeof(*qnp))) == NULL)
295 err(1, "%s", strerror(errno));
296 qnp->flags = 0;
297 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
298 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
299 qnp->flags |= HASGRP;
301 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
302 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
303 qnp->flags |= HASUSR;
305 if (qnp->flags)
306 return (qnp);
307 free(qnp);
308 return (NULL);
311 off_t sblock_try[] = SBLOCKSEARCH;
314 * Scan the specified filesystem to check quota(s) present on it.
316 static int
317 chkquota(type, fsname, mntpt, v, pid)
318 const char *type, *fsname, *mntpt;
319 void *v;
320 pid_t *pid;
322 struct quotaname *qnp = v;
323 struct fileusage *fup;
324 union comb_dinode *dp;
325 int cg, i, mode, errs = 0, inosused;
326 ino_t ino;
327 struct cg *cgp;
328 char msgbuf[4096];
330 if (pid != NULL) {
331 fflush(stdout);
332 switch ((*pid = fork())) {
333 default:
334 return 0;
335 case 0:
336 break;
337 case -1:
338 err(1, "Cannot fork");
340 setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf);
343 if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
344 warn("Cannot open %s", fsname);
345 if (pid != NULL)
346 exit(1);
347 return 1;
349 if (vflag) {
350 (void)printf("*** Checking ");
351 if (qnp->flags & HASUSR)
352 (void)printf("%s%s", qfextension[USRQUOTA],
353 (qnp->flags & HASGRP) ? " and " : "");
354 if (qnp->flags & HASGRP)
355 (void)printf("%s", qfextension[GRPQUOTA]);
356 (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
357 fflush(stdout);
359 signal(SIGINFO, infohandler);
360 sync();
361 dev_bsize = 1;
363 for (i = 0; ; i++) {
364 if (sblock_try[i] == -1) {
365 warnx("%s: superblock not found", fsname);
366 if (pid != NULL)
367 exit(1);
368 return 1;
370 bread(sblock_try[i], (char *)&sblock, SBLOCKSIZE);
371 switch (sblock.fs_magic) {
372 #ifdef HAVE_UFSv2
373 case FS_UFS2_MAGIC:
374 is_ufs2 = 1;
375 /*FALLTHROUGH*/
376 #endif
377 case FS_UFS1_MAGIC:
378 break;
379 #ifdef HAVE_UFSv2
380 case FS_UFS2_MAGIC_SWAPPED:
381 is_ufs2 = 1;
382 /*FALLTHROUGH*/
383 #endif
384 case FS_UFS1_MAGIC_SWAPPED:
385 needswap = 1;
386 ffs_sb_swap(&sblock, &sblock);
387 break;
388 default:
389 continue;
392 #ifdef HAVE_UFSv2
393 if (is_ufs2 || sblock.fs_old_flags & FS_FLAGS_UPDATED) {
394 if (sblock.fs_sblockloc != sblock_try[i])
395 continue;
396 } else {
397 if (sblock_try[i] == SBLOCK_UFS2)
398 continue;
400 #endif
401 break;
404 cgp = malloc(sblock.fs_cgsize);
405 if (cgp == NULL) {
406 warn("%s: can't allocate %d bytes of cg space", fsname,
407 sblock.fs_cgsize);
408 if (pid != NULL)
409 exit(1);
410 return 1;
413 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
414 maxino = sblock.fs_ncg * sblock.fs_ipg;
415 for (cg = 0; cg < sblock.fs_ncg; cg++) {
416 ino = cg * sblock.fs_ipg;
417 setinodebuf(ino);
418 #ifdef HAVE_UFSv2
419 if (sblock.fs_magic == FS_UFS2_MAGIC) {
420 bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)cgp,
421 sblock.fs_cgsize);
422 if (needswap)
423 ffs_cg_swap(cgp, cgp, &sblock);
424 inosused = cgp->cg_initediblk;
425 } else
426 #endif
427 inosused = sblock.fs_ipg;
428 for (i = 0; i < inosused; i++, ino++) {
429 if (got_siginfo) {
430 fprintf(stderr,
431 "%s: cyl group %d of %d (%d%%)\n",
432 fsname, cg, sblock.fs_ncg,
433 cg * 100 / sblock.fs_ncg);
434 got_siginfo = 0;
436 if (ino < ROOTINO)
437 continue;
438 if ((dp = getnextinode(ino)) == NULL)
439 continue;
440 if ((mode = DIP(dp, mode) & IFMT) == 0)
441 continue;
442 if (qnp->flags & HASGRP) {
443 fup = addid(DIP(dp, gid), GRPQUOTA,
444 (char *)0);
445 fup->fu_curinodes++;
446 if (mode == IFREG || mode == IFDIR ||
447 mode == IFLNK)
448 fup->fu_curblocks += DIP(dp, blocks);
450 if (qnp->flags & HASUSR) {
451 fup = addid(DIP(dp, uid), USRQUOTA,
452 (char *)0);
453 fup->fu_curinodes++;
454 if (mode == IFREG || mode == IFDIR ||
455 mode == IFLNK)
456 fup->fu_curblocks += DIP(dp, blocks);
460 freeinodebuf();
461 free(cgp);
462 if (qnp->flags & HASUSR)
463 errs += update(mntpt, qnp->usrqfname, USRQUOTA);
464 if (qnp->flags & HASGRP)
465 errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
466 close(fi);
467 if (pid != NULL)
468 exit(errs);
469 return errs;
473 * Update a specified quota file.
475 static int
476 update(fsname, quotafile, type)
477 const char *fsname, *quotafile;
478 int type;
480 struct fileusage *fup;
481 FILE *qfi, *qfo;
482 u_int32_t id, lastid, nextid;
483 int need_seek;
484 struct dqblk dqbuf;
485 static int warned = 0;
486 static struct dqblk zerodqbuf;
487 static struct fileusage zerofileusage;
489 if ((qfo = fopen(quotafile, "r+")) == NULL) {
490 if (errno == ENOENT)
491 qfo = fopen(quotafile, "w+");
492 if (qfo) {
493 (void) fprintf(stderr,
494 "quotacheck: creating quota file %s\n", quotafile);
495 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
496 (void) fchown(fileno(qfo), getuid(), getquotagid());
497 (void) fchmod(fileno(qfo), MODE);
498 } else {
499 (void) fprintf(stderr,
500 "quotacheck: %s: %s\n", quotafile, strerror(errno));
501 return (1);
504 if ((qfi = fopen(quotafile, "r")) == NULL) {
505 (void) fprintf(stderr,
506 "quotacheck: %s: %s\n", quotafile, strerror(errno));
507 (void) fclose(qfo);
508 return (1);
510 if (quotactl(fsname, QCMD(Q_SYNC, type), 0, (void *) NULL) < 0 &&
511 errno == EOPNOTSUPP && !warned && vflag) {
512 warned++;
513 (void)printf("*** Warning: %s\n",
514 "Quotas are not compiled into this kernel");
516 need_seek = 1;
517 for (lastid = highid[type], id = 0; id <= lastid; id = nextid) {
518 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
519 dqbuf = zerodqbuf;
520 if ((fup = lookup(id, type)) == 0)
521 fup = &zerofileusage;
523 nextid = subsequent(id, type);
524 if (nextid > 0 && nextid != id + 1) /* watch out for id == UINT32_MAX */
525 nextid = skipforward(id, nextid, qfi);
527 if (got_siginfo) {
528 /* XXX this could try to show percentage through the ID list */
529 fprintf(stderr,
530 "%s: updating %s quotas for id=%" PRIu32 " (%s)\n", fsname,
531 qfextension[type < MAXQUOTAS ? type : MAXQUOTAS],
532 id, fup->fu_name);
533 got_siginfo = 0;
535 if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
536 dqbuf.dqb_curblocks == fup->fu_curblocks) {
537 fup->fu_curinodes = 0; /* reset usage */
538 fup->fu_curblocks = 0; /* for next filesystem */
540 need_seek = 1;
541 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */
542 break;
544 continue;
546 if (vflag) {
547 if (aflag)
548 printf("%s: ", fsname);
549 printf("%-8s fixed:", fup->fu_name);
550 if (dqbuf.dqb_curinodes != fup->fu_curinodes)
551 (void)printf("\tinodes %d -> %ld",
552 dqbuf.dqb_curinodes, fup->fu_curinodes);
553 if (dqbuf.dqb_curblocks != fup->fu_curblocks)
554 (void)printf("\tblocks %d -> %ld",
555 dqbuf.dqb_curblocks, fup->fu_curblocks);
556 (void)printf("\n");
559 * Reset time limit if have a soft limit and were
560 * previously under it, but are now over it.
562 if (dqbuf.dqb_bsoftlimit &&
563 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
564 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
565 dqbuf.dqb_btime = 0;
566 if (dqbuf.dqb_isoftlimit &&
567 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
568 fup->fu_curinodes >= dqbuf.dqb_isoftlimit)
569 dqbuf.dqb_itime = 0;
570 dqbuf.dqb_curinodes = fup->fu_curinodes;
571 dqbuf.dqb_curblocks = fup->fu_curblocks;
573 if (need_seek) {
574 (void) fseeko(qfo, (off_t)id * sizeof(struct dqblk),
575 SEEK_SET);
576 need_seek = nextid != id + 1;
578 (void) fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
580 if (!warned)
581 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
582 (caddr_t)&dqbuf);
584 fup->fu_curinodes = 0;
585 fup->fu_curblocks = 0;
586 if (id == UINT32_MAX || nextid == 0) { /* infinite loop avoidance (OR do as "nextid < id"?) */
587 break;
590 (void) fclose(qfi);
591 (void) fflush(qfo);
592 if (highid[type] != UINT32_MAX)
593 (void) ftruncate(fileno(qfo),
594 (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
595 (void) fclose(qfo);
596 return (0);
599 u_int32_t
600 skipforward(cur, to, qfi)
601 u_int32_t cur, to;
602 FILE *qfi;
604 struct dqblk dqbuf;
606 if (qflag) {
607 (void) fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET);
608 return (to);
611 while (++cur < to) {
613 * if EOF occurs, nothing left to read, we're done
615 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
616 return (to);
619 * If we find an entry that shows usage, before the next
620 * id that has actual usage, we have to stop here, so the
621 * incorrect entry can be corrected in the file
623 if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) {
624 (void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR);
625 return (cur);
628 return (to);
632 * Check to see if target appears in list of size cnt.
634 static int
635 oneof(target, list, cnt)
636 const char *target;
637 char *list[];
638 int cnt;
640 int i;
642 for (i = 0; i < cnt; i++)
643 if (strcmp(target, list[i]) == 0)
644 return (i);
645 return (-1);
649 * Determine the group identifier for quota files.
651 static int
652 getquotagid()
654 struct group *gr;
656 if ((gr = getgrnam(quotagroup)) != NULL)
657 return (gr->gr_gid);
658 return (-1);
662 * Check to see if a particular quota is to be enabled.
664 static int
665 hasquota(fs, type, qfnamep)
666 struct fstab *fs;
667 int type;
668 char **qfnamep;
670 char *opt;
671 char *cp = NULL;
672 static char initname, usrname[100], grpname[100];
673 static char buf[BUFSIZ];
675 if (!initname) {
676 (void)snprintf(usrname, sizeof(usrname),
677 "%s%s", qfextension[USRQUOTA], qfname);
678 (void)snprintf(grpname, sizeof(grpname),
679 "%s%s", qfextension[GRPQUOTA], qfname);
680 initname = 1;
682 (void) strlcpy(buf, fs->fs_mntops, sizeof(buf));
683 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
684 if ((cp = strchr(opt, '=')) != NULL)
685 *cp++ = '\0';
686 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
687 break;
688 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
689 break;
691 if (!opt)
692 return (0);
693 if (cp)
694 *qfnamep = cp;
695 else {
696 (void)snprintf(buf, sizeof(buf),
697 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
698 *qfnamep = buf;
700 return (1);
704 * Routines to manage the file usage table.
706 * Lookup an id of a specific type.
708 static struct fileusage *
709 lookup(id, type)
710 u_int32_t id;
711 int type;
713 struct fileusage *fup;
715 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
716 if (fup->fu_id == id)
717 return (fup);
718 return (NULL);
722 * Add a new file usage id if it does not already exist.
724 static struct fileusage *
725 addid(id, type, name)
726 u_int32_t id;
727 int type;
728 const char *name;
730 struct fileusage *fup, **fhp;
731 int len;
733 if ((fup = lookup(id, type)) != NULL)
734 return (fup);
735 if (name)
736 len = strlen(name);
737 else
738 len = 10;
739 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
740 err(1, "%s", strerror(errno));
741 fhp = &fuhead[type][id & (FUHASH - 1)];
742 fup->fu_next = *fhp;
743 *fhp = fup;
744 fup->fu_id = id;
745 if (id > highid[type])
746 highid[type] = id;
747 if (name)
748 memmove(fup->fu_name, name, len + 1);
749 else
750 (void) sprintf(fup->fu_name, "%" PRIu32, id);
751 return (fup);
754 static u_int32_t
755 subsequent(id, type)
756 u_int32_t id;
757 int type;
759 struct fileusage *fup, **iup, **cup;
760 u_int32_t next, offset;
762 next = highid[type] + 1;
763 offset = 0;
764 cup = iup = &fuhead[type][id & (FUHASH-1)];
765 do {
766 ++offset;
767 if (++cup >= &fuhead[type][FUHASH])
768 cup = &fuhead[type][0];
769 for (fup = *cup; fup != 0; fup = fup->fu_next) {
770 if (fup->fu_id > id && fup->fu_id <= id + offset)
771 return (fup->fu_id);
772 if (fup->fu_id > id && fup->fu_id < next)
773 next = fup->fu_id;
775 } while (cup != iup);
777 return next;
781 * Special purpose version of ginode used to optimize first pass
782 * over all the inodes in numerical order.
784 static ino_t nextino, lastinum, lastvalidinum;
785 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
786 static union comb_dinode *inodebuf;
787 #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
789 union comb_dinode *
790 getnextinode(inumber)
791 ino_t inumber;
793 long size;
794 daddr_t dblk;
795 static union comb_dinode *dp;
796 union comb_dinode *ret;
798 if (inumber != nextino++ || inumber > lastvalidinum) {
799 errx(1, "bad inode number %llu to nextinode",
800 (unsigned long long)inumber);
803 if (inumber >= lastinum) {
804 readcnt++;
805 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
806 if (readcnt % readpercg == 0) {
807 size = partialsize;
808 lastinum += partialcnt;
809 } else {
810 size = inobufsize;
811 lastinum += fullcnt;
813 (void)bread(dblk, (caddr_t)inodebuf, size);
814 if (needswap) {
815 #ifdef HAVE_UFSv2
816 if (is_ufs2)
817 swap_dinode2(inodebuf, lastinum - inumber);
818 else
819 #endif
820 swap_dinode1(inodebuf, lastinum - inumber);
822 dp = (union comb_dinode *)inodebuf;
824 ret = dp;
825 dp = (union comb_dinode *)
826 ((char *)dp + (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE));
827 return ret;
830 void
831 setinodebuf(inum)
832 ino_t inum;
835 if (inum % sblock.fs_ipg != 0)
836 errx(1, "bad inode number %llu to setinodebuf",
837 (unsigned long long)inum);
839 lastvalidinum = inum + sblock.fs_ipg - 1;
840 nextino = inum;
841 lastinum = inum;
842 readcnt = 0;
843 if (inodebuf != NULL)
844 return;
845 inobufsize = blkroundup(&sblock, INOBUFSIZE);
846 fullcnt = inobufsize / (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
847 readpercg = sblock.fs_ipg / fullcnt;
848 partialcnt = sblock.fs_ipg % fullcnt;
849 partialsize = partialcnt * (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE);
850 if (partialcnt != 0) {
851 readpercg++;
852 } else {
853 partialcnt = fullcnt;
854 partialsize = inobufsize;
856 if (inodebuf == NULL &&
857 (inodebuf = malloc((unsigned)inobufsize)) == NULL)
858 errx(1, "Cannot allocate space for inode buffer");
859 while (nextino < ROOTINO)
860 getnextinode(nextino);
863 void
864 freeinodebuf()
867 if (inodebuf != NULL)
868 free((char *)inodebuf);
869 inodebuf = NULL;
873 #ifdef HAVE_UFSv2
874 static void
875 swap_dinode1(union comb_dinode *dp, int n)
877 int i;
878 struct ufs1_dinode *dp1;
880 dp1 = (struct ufs1_dinode *)&dp->dp1;
881 for (i = 0; i < n; i++, dp1++)
882 ffs_dinode1_swap(dp1, dp1);
885 static void
886 swap_dinode2(union comb_dinode *dp, int n)
888 int i;
889 struct ufs2_dinode *dp2;
891 dp2 = (struct ufs2_dinode *)&dp->dp2;
892 for (i = 0; i < n; i++, dp2++)
893 ffs_dinode2_swap(dp2, dp2);
896 #else
898 static void
899 swap_dinode1(union comb_dinode *dp, int n)
901 int i;
902 struct dinode *dp1;
904 dp1 = (struct dinode *) &dp->dp1;
905 for (i = 0; i < n; i++, dp1++)
906 ffs_dinode_swap(dp1, dp1);
909 #endif
912 * Read specified disk blocks.
914 static void
915 bread(bno, buf, cnt)
916 daddr_t bno;
917 char *buf;
918 long cnt;
921 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
922 read(fi, buf, cnt) != cnt)
923 err(1, "block %lld", (long long)bno);
926 void
927 infohandler(int sig)
929 got_siginfo = 1;