GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/quotacheck/quotacheck.c Lines: 0 330 0.0 %
Date: 2017-11-07 Branches: 0 278 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: quotacheck.c,v 1.39 2015/12/10 17:27:00 mmcc Exp $	*/
2
/*	$NetBSD: quotacheck.c,v 1.12 1996/03/30 22:34:25 mark Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1990, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * This code is derived from software contributed to Berkeley by
9
 * Robert Elz at The University of Melbourne.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 * 3. Neither the name of the University nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
/*
37
 * Fix up / report on disk quotas & usage
38
 */
39
#include <sys/param.h>	/* DEV_BSIZE MAXBSIZE */
40
#include <sys/stat.h>
41
#include <sys/wait.h>
42
43
#include <ufs/ufs/dinode.h>
44
#include <ufs/ufs/quota.h>
45
#include <ufs/ffs/fs.h>
46
47
#include <fcntl.h>
48
#include <fstab.h>
49
#include <pwd.h>
50
#include <grp.h>
51
#include <errno.h>
52
#include <unistd.h>
53
#include <limits.h>
54
#include <util.h>
55
#include <stdio.h>
56
#include <stdlib.h>
57
#include <string.h>
58
#include <err.h>
59
#include "fsutil.h"
60
61
char *qfname = QUOTAFILENAME;
62
char *qfextension[] = INITQFNAMES;
63
char *quotagroup = QUOTAGROUP;
64
65
union {
66
	struct	fs	sblk;
67
	char	dummy[MAXBSIZE];
68
} sb_un;
69
#define	sblock	sb_un.sblk
70
union {
71
	struct	cg	cgblk;
72
	char	dummy[MAXBSIZE];
73
} cg_un;
74
#define	cgblk	cg_un.cgblk
75
76
long maxino;
77
78
union dinode {
79
	struct ufs1_dinode dp1;
80
	struct ufs2_dinode dp2;
81
};
82
#define	DIP(dp, field) \
83
	((sblock.fs_magic == FS_UFS1_MAGIC) ? \
84
	(dp)->dp1.field : (dp)->dp2.field)
85
86
struct quotaname {
87
	long	flags;
88
	char	grpqfname[PATH_MAX + 1];
89
	char	usrqfname[PATH_MAX + 1];
90
};
91
#define	HASUSR	1
92
#define	HASGRP	2
93
94
struct fileusage {
95
	struct fileusage *fu_next;
96
	u_int32_t	fu_curinodes;
97
	u_int32_t	fu_curblocks;
98
	u_int32_t	fu_id;	/* uid_t or gid_t */
99
	char		fu_name[1];
100
	/* actually bigger */
101
};
102
#define FUHASH 1024	/* must be power of two */
103
struct fileusage *fuhead[MAXQUOTAS][FUHASH];
104
105
int	gflag;			/* check group quotas */
106
int	uflag;			/* check user quotas */
107
int	flags;			/* check flags (avd) */
108
int	fi;			/* open disk file descriptor */
109
u_int32_t highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
110
111
struct fileusage *
112
	 addid(u_int32_t, int, char *);
113
char	*blockcheck(char *);
114
void	 bread(daddr_t, char *, long);
115
int	 chkquota(const char *, const char *, const char *, void *, pid_t *);
116
void	 freeinodebuf(void);
117
union dinode *
118
	 getnextinode(ino_t);
119
int	 getquotagid(void);
120
int	 hasquota(struct fstab *, int, char **);
121
struct fileusage *
122
	 lookup(u_int32_t, int);
123
void	*needchk(struct fstab *);
124
int	 oneof_realpath(char *, char*[], int);
125
int	 oneof_specname(char *, char*[], int);
126
void	 setinodebuf(ino_t);
127
int	 update(const char *, const char *, int);
128
void	 usage(void);
129
130
int
131
main(int argc, char *argv[])
132
{
133
	struct fstab *fs;
134
	struct passwd *pw;
135
	struct group *gr;
136
	struct quotaname *auxdata;
137
	int i, argnum, maxrun, errs, ch;
138
	u_int64_t done = 0;	/* XXX supports maximum 64 filesystems */
139
	const char *errstr;
140
	char *name;
141
142
	errs = maxrun = 0;
143
	while ((ch = getopt(argc, argv, "adguvl:")) != -1) {
144
		switch(ch) {
145
		case 'a':
146
			flags |= CHECK_PREEN;
147
			break;
148
		case 'd':
149
			flags |= CHECK_DEBUG;
150
			break;
151
		case 'g':
152
			gflag = 1;
153
			break;
154
		case 'l':
155
			maxrun = strtonum(optarg, 0, INT_MAX, &errstr);
156
			if (errstr)
157
				errx(1, "-l %s: %s", optarg, errstr);
158
			break;
159
		case 'u':
160
			uflag = 1;
161
			break;
162
		case 'v':
163
			flags |= CHECK_VERBOSE;
164
			break;
165
		default:
166
			usage();
167
		}
168
	}
169
	argc -= optind;
170
	argv += optind;
171
	if ((argc == 0 && !(flags&CHECK_PREEN)) ||
172
	    (argc > 0 && (flags&CHECK_PREEN)))
173
		usage();
174
	if (!gflag && !uflag) {
175
		gflag++;
176
		uflag++;
177
	}
178
	if (gflag) {
179
		setgrent();
180
		while ((gr = getgrent()) != 0)
181
			(void) addid(gr->gr_gid, GRPQUOTA, gr->gr_name);
182
		endgrent();
183
	}
184
	if (uflag) {
185
		setpwent();
186
		while ((pw = getpwent()) != 0)
187
			(void) addid(pw->pw_uid, USRQUOTA, pw->pw_name);
188
		endpwent();
189
	}
190
	if (flags&CHECK_PREEN)
191
		exit(checkfstab(flags, maxrun, needchk, chkquota));
192
	if (setfsent() == 0)
193
		err(1, "%s: can't open", _PATH_FSTAB);
194
	while ((fs = getfsent()) != NULL) {
195
		if (((argnum = oneof_realpath(fs->fs_file, argv, argc)) >= 0 ||
196
		    (argnum = oneof_specname(fs->fs_spec, argv, argc)) >= 0) &&
197
		    (auxdata = needchk(fs)) &&
198
		    (name = blockcheck(fs->fs_spec))) {
199
			done |= 1 << argnum;
200
			errs += chkquota(fs->fs_vfstype, name,
201
			    fs->fs_file, auxdata, NULL);
202
		}
203
	}
204
	endfsent();
205
	for (i = 0; i < argc; i++)
206
		if ((done & (1 << i)) == 0)
207
			fprintf(stderr, "%s not found in %s\n",
208
			    argv[i], _PATH_FSTAB);
209
	exit(errs);
210
}
211
212
void
213
usage(void)
214
{
215
	extern char *__progname;
216
	(void)fprintf(stderr, "usage: %s [-adguv] [-l maxparallel] "
217
	    "filesystem ...\n", __progname);
218
	exit(1);
219
}
220
221
void *
222
needchk(struct fstab *fs)
223
{
224
	struct quotaname *qnp;
225
	char *qfnp;
226
227
	if (fs->fs_passno == 0)
228
		return NULL;
229
	if (strcmp(fs->fs_type, FSTAB_RW))
230
		return (NULL);
231
	if (strcmp(fs->fs_vfstype, "ffs") &&
232
	    strcmp(fs->fs_vfstype, "ufs") &&
233
	    strcmp(fs->fs_vfstype, "mfs"))
234
		return (NULL);
235
	if ((qnp = malloc(sizeof(*qnp))) == NULL)
236
		err(1, "%s", strerror(errno));
237
	qnp->flags = 0;
238
	if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
239
		strlcpy(qnp->grpqfname, qfnp, sizeof qnp->grpqfname);
240
		qnp->flags |= HASGRP;
241
	}
242
	if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
243
		strlcpy(qnp->usrqfname, qfnp, sizeof qnp->usrqfname);
244
		qnp->flags |= HASUSR;
245
	}
246
	if (qnp->flags)
247
		return (qnp);
248
	free(qnp);
249
	return (NULL);
250
}
251
252
/*
253
 * Possible superblock locations ordered from most to least likely.
254
 */
255
static int sblock_try[] = SBLOCKSEARCH;
256
257
/*
258
 * Scan the specified file system to check quota(s) present on it.
259
 */
260
int
261
chkquota(const char *vfstype, const char *fsname, const char *mntpt,
262
    void *auxarg, pid_t *pidp)
263
{
264
	struct quotaname *qnp = auxarg;
265
	struct fileusage *fup;
266
	union dinode *dp;
267
	int cg, i, mode, errs = 0, status;
268
	ino_t ino, inosused;
269
	pid_t pid;
270
	char *cp;
271
272
	switch (pid = fork()) {
273
	case -1:	/* error */
274
		warn("fork");
275
		return 1;
276
	case 0:		/* child */
277
		if ((fi = opendev(fsname, O_RDONLY, 0, NULL)) < 0)
278
			err(1, "%s", fsname);
279
		sync();
280
		for (i = 0; sblock_try[i] != -1; i++) {
281
			bread(sblock_try[i] / DEV_BSIZE, (char *)&sblock,
282
			    (long)SBLOCKSIZE);
283
			if ((sblock.fs_magic == FS_UFS1_MAGIC ||
284
			     (sblock.fs_magic == FS_UFS2_MAGIC &&
285
			      sblock.fs_sblockloc == sblock_try[i])) &&
286
			    sblock.fs_bsize <= MAXBSIZE &&
287
			    sblock.fs_bsize >= sizeof(struct fs))
288
				break;
289
		}
290
		if (sblock_try[i] == -1) {
291
			warn("Cannot find file system superblock");
292
			return (1);
293
		}
294
		maxino = sblock.fs_ncg * sblock.fs_ipg;
295
		for (cg = 0; cg < sblock.fs_ncg; cg++) {
296
			ino = cg * sblock.fs_ipg;
297
			setinodebuf(ino);
298
			bread(fsbtodb(&sblock, cgtod(&sblock, cg)),
299
			    (char *)(&cgblk), sblock.fs_cgsize);
300
			if (sblock.fs_magic == FS_UFS2_MAGIC)
301
				inosused = cgblk.cg_initediblk;
302
			else
303
				inosused = sblock.fs_ipg;
304
			/*
305
			 * If we are using soft updates, then we can trust the
306
			 * cylinder group inode allocation maps to tell us which
307
			 * inodes are allocated. We will scan the used inode map
308
			 * to find the inodes that are really in use, and then
309
			 * read only those inodes in from disk.
310
			 */
311
			if (sblock.fs_flags & FS_DOSOFTDEP) {
312
				if (!cg_chkmagic(&cgblk))
313
					errx(1, "CG %d: BAD MAGIC NUMBER\n", cg);
314
				cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT];
315
				for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
316
					if (*cp == 0)
317
						continue;
318
					for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
319
						if (*cp & i)
320
							break;
321
						inosused--;
322
					}
323
					break;
324
				}
325
				if (inosused <= 0)
326
					continue;
327
			}
328
			for (i = 0; i < inosused; i++, ino++) {
329
				if ((dp = getnextinode(ino)) == NULL ||
330
				    ino < ROOTINO ||
331
				    (mode = DIP(dp, di_mode) & IFMT) == 0)
332
					continue;
333
				if (qnp->flags & HASGRP) {
334
					fup = addid(DIP(dp, di_gid),
335
					    GRPQUOTA, NULL);
336
					fup->fu_curinodes++;
337
					if (mode == IFREG || mode == IFDIR ||
338
					    mode == IFLNK)
339
						fup->fu_curblocks +=
340
						    DIP(dp, di_blocks);
341
				}
342
				if (qnp->flags & HASUSR) {
343
					fup = addid(DIP(dp, di_uid),
344
					    USRQUOTA, NULL);
345
					fup->fu_curinodes++;
346
					if (mode == IFREG || mode == IFDIR ||
347
					    mode == IFLNK)
348
						fup->fu_curblocks +=
349
						    DIP(dp, di_blocks);
350
				}
351
			}
352
		}
353
		freeinodebuf();
354
		if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) {
355
			(void)printf("*** Checking ");
356
			if (qnp->flags & HASUSR) {
357
				(void)printf("%s", qfextension[USRQUOTA]);
358
				if (qnp->flags & HASGRP)
359
					(void)printf(" and ");
360
			}
361
			if (qnp->flags & HASGRP)
362
				(void)printf("%s", qfextension[GRPQUOTA]);
363
			(void)printf(" quotas for %s (%s), %swait\n",
364
			    fsname, mntpt, pidp? "no" : "");
365
		}
366
		if (qnp->flags & HASUSR)
367
			errs += update(mntpt, qnp->usrqfname, USRQUOTA);
368
		if (qnp->flags & HASGRP)
369
			errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
370
		close(fi);
371
		exit (errs);
372
		break;
373
	default:	/* parent */
374
		if (pidp != NULL) {
375
			*pidp = pid;
376
			return 0;
377
		}
378
		if (waitpid(pid, &status, 0) < 0) {
379
			warn("waitpid");
380
			return 1;
381
		}
382
		if (WIFEXITED(status)) {
383
			if (WEXITSTATUS(status) != 0)
384
				return WEXITSTATUS(status);
385
		} else if (WIFSIGNALED(status)) {
386
			warnx("%s: %s", fsname, strsignal(WTERMSIG(status)));
387
			return 1;
388
		}
389
		break;
390
	}
391
	return (0);
392
}
393
394
/*
395
 * Update a specified quota file.
396
 */
397
int
398
update(const char *fsname, const char *quotafile, int type)
399
{
400
	struct fileusage *fup;
401
	FILE *qfi, *qfo;
402
	u_int32_t id, lastid;
403
	struct dqblk dqbuf;
404
	static int warned = 0;
405
	static struct dqblk zerodqbuf;
406
	static struct fileusage zerofileusage;
407
408
	if (flags&CHECK_DEBUG)
409
		printf("updating: %s\n", quotafile);
410
411
	if ((qfo = fopen(quotafile, (flags&CHECK_DEBUG)? "r" : "r+")) == NULL) {
412
		if (errno == ENOENT)
413
			qfo = fopen(quotafile, "w+");
414
		if (qfo) {
415
			warnx("creating quota file: %s", quotafile);
416
#define	MODE	(S_IRUSR|S_IWUSR|S_IRGRP)
417
			(void) fchown(fileno(qfo), getuid(), getquotagid());
418
			(void) fchmod(fileno(qfo), MODE);
419
		} else {
420
			warn("%s", quotafile);
421
			return (1);
422
		}
423
	}
424
	if ((qfi = fopen(quotafile, "r")) == NULL) {
425
		warn("%s", quotafile);
426
		(void) fclose(qfo);
427
		return (1);
428
	}
429
	if (quotactl(fsname, QCMD(Q_SYNC, type), 0, (caddr_t)0) < 0 &&
430
	    errno == EOPNOTSUPP && !warned &&
431
	    (flags&(CHECK_DEBUG|CHECK_VERBOSE))) {
432
		warned++;
433
		(void)printf("*** Warning: %s\n",
434
		    "Quotas are not compiled into this kernel");
435
	}
436
	for (lastid = highid[type], id = 0; id <= lastid; id++) {
437
		if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
438
			dqbuf = zerodqbuf;
439
		if ((fup = lookup(id, type)) == 0)
440
			fup = &zerofileusage;
441
		if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
442
		    dqbuf.dqb_curblocks == fup->fu_curblocks) {
443
			fup->fu_curinodes = 0;
444
			fup->fu_curblocks = 0;
445
			fseek(qfo, (long)sizeof(struct dqblk), SEEK_CUR);
446
			continue;
447
		}
448
		if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) {
449
			if (flags&CHECK_PREEN)
450
				printf("%s: ", fsname);
451
			printf("%-8s fixed:", fup->fu_name);
452
			if (dqbuf.dqb_curinodes != fup->fu_curinodes)
453
				(void)printf("\tinodes %d -> %u",
454
				    dqbuf.dqb_curinodes, fup->fu_curinodes);
455
			if (dqbuf.dqb_curblocks != fup->fu_curblocks)
456
				(void)printf("\tblocks %u -> %u",
457
				    dqbuf.dqb_curblocks, fup->fu_curblocks);
458
			(void)printf("\n");
459
		}
460
		/*
461
		 * Reset time limit if have a soft limit and were
462
		 * previously under it, but are now over it.
463
		 */
464
		if (dqbuf.dqb_bsoftlimit &&
465
		    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
466
		    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
467
			dqbuf.dqb_btime = 0;
468
		if (dqbuf.dqb_isoftlimit &&
469
		    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
470
		    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
471
			dqbuf.dqb_itime = 0;
472
		dqbuf.dqb_curinodes = fup->fu_curinodes;
473
		dqbuf.dqb_curblocks = fup->fu_curblocks;
474
		if (!(flags & CHECK_DEBUG)) {
475
			fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
476
			(void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
477
			    (caddr_t)&dqbuf);
478
		}
479
		fup->fu_curinodes = 0;
480
		fup->fu_curblocks = 0;
481
	}
482
	fclose(qfi);
483
	fflush(qfo);
484
	if (!(flags & CHECK_DEBUG))
485
		ftruncate(fileno(qfo),
486
		    (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
487
	fclose(qfo);
488
	return (0);
489
}
490
491
/*
492
 * Check to see if realpath(target) matches a realpath() in list of size cnt.
493
 */
494
int
495
oneof_realpath(char *target, char *list[], int cnt)
496
{
497
	int i;
498
	char realtarget[PATH_MAX], realargv[PATH_MAX];
499
	char *rv;
500
501
	rv = realpath(target, realtarget);
502
	if (rv == NULL)
503
		return (-1);
504
505
	for (i = 0; i < cnt; i++) {
506
		rv = realpath(list[i], realargv);
507
		if (rv && strcmp(realtarget, realargv) == 0)
508
			break;
509
	}
510
511
	if (i < cnt)
512
		return (i);
513
	else
514
		return (-1);
515
}
516
517
/*
518
 * Check to see if opendev(target) matches a opendev() in list of size cnt.
519
 */
520
int
521
oneof_specname(char *target, char *list[], int cnt)
522
{
523
	int i, fd;
524
	char *tmp, *targetdev, *argvdev;
525
526
	fd = opendev(target, O_RDONLY, 0, &tmp);
527
	if (fd == -1)
528
		return (-1);
529
	close(fd);
530
	targetdev = strdup(tmp);
531
532
	for (i = 0; i < cnt; i++) {
533
		fd = opendev(list[i], O_RDONLY, 0, &argvdev);
534
		if (fd == -1)
535
			continue;
536
		close(fd);
537
		if (strcmp(targetdev, argvdev) == 0)
538
			break;
539
	}
540
541
	free(targetdev);
542
543
	if (i < cnt)
544
		return (i);
545
	else
546
		return (-1);
547
}
548
549
/*
550
 * Determine the group identifier for quota files.
551
 */
552
int
553
getquotagid(void)
554
{
555
	struct group *gr;
556
557
	if ((gr = getgrnam(quotagroup)) != NULL)
558
		return (gr->gr_gid);
559
	return (-1);
560
}
561
562
/*
563
 * Check to see if a particular quota is to be enabled.
564
 */
565
int
566
hasquota(struct fstab *fs, int type, char **qfnamep)
567
{
568
	char *opt, *cp;
569
	static char initname, usrname[100], grpname[100];
570
	static char buf[BUFSIZ];
571
572
	if (!initname) {
573
		(void)snprintf(usrname, sizeof(usrname),
574
		    "%s%s", qfextension[USRQUOTA], qfname);
575
		(void)snprintf(grpname, sizeof(grpname),
576
		    "%s%s", qfextension[GRPQUOTA], qfname);
577
		initname = 1;
578
	}
579
	(void)strlcpy(buf, fs->fs_mntops, sizeof(buf));
580
	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
581
		if ((cp = strchr(opt, '=')) != NULL)
582
			*cp++ = '\0';
583
		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
584
			break;
585
		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
586
			break;
587
	}
588
	if (!opt)
589
		return (0);
590
	if (cp)
591
		*qfnamep = cp;
592
	else {
593
		(void)snprintf(buf, sizeof(buf),
594
		    "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
595
		*qfnamep = buf;
596
	}
597
	return (1);
598
}
599
600
/*
601
 * Routines to manage the file usage table.
602
 *
603
 * Lookup an id of a specific type.
604
 */
605
struct fileusage *
606
lookup(u_int32_t id, int type)
607
{
608
	struct fileusage *fup;
609
610
	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
611
		if (fup->fu_id == id)
612
			return (fup);
613
	return (NULL);
614
}
615
616
/*
617
 * Add a new file usage id if it does not already exist.
618
 */
619
struct fileusage *
620
addid(u_int32_t id, int type, char *name)
621
{
622
	struct fileusage *fup, **fhp;
623
	int len;
624
625
	if ((fup = lookup(id, type)) != NULL)
626
		return (fup);
627
	if (name)
628
		len = strlen(name);
629
	else
630
		len = 10;
631
	if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
632
		err(1, "%s", strerror(errno));
633
	fhp = &fuhead[type][id & (FUHASH - 1)];
634
	fup->fu_next = *fhp;
635
	*fhp = fup;
636
	fup->fu_id = id;
637
	if (id > highid[type])
638
		highid[type] = id;
639
	if (name)
640
		memcpy(fup->fu_name, name, len + 1);
641
	else
642
		(void)snprintf(fup->fu_name, len, "%u", id);
643
	return (fup);
644
}
645
646
/*
647
 * Special purpose version of ginode used to optimize pass
648
 * over all the inodes in numerical order.
649
 */
650
static ino_t nextino, lastinum, lastvalidinum;
651
static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
652
static caddr_t inodebuf;
653
#define	INOBUFSIZE	56*1024		/* size of buffer to read inodes */
654
655
union dinode *
656
getnextinode(ino_t inumber)
657
{
658
	long size;
659
	daddr_t dblk;
660
	union dinode *dp;
661
	static caddr_t nextinop;
662
663
	if (inumber != nextino++ || inumber > lastvalidinum)
664
		err(1, "bad inode number %llu to nextinode",
665
		    (unsigned long long)inumber);
666
	if (inumber >= lastinum) {
667
		readcnt++;
668
		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
669
		if (readcnt % readpercg == 0) {
670
			size = partialsize;
671
			lastinum += partialcnt;
672
		} else {
673
			size = inobufsize;
674
			lastinum += fullcnt;
675
		}
676
		/*
677
		 * If bread returns an error, it will already have zeroed
678
		 * out the buffer, so we do not need to do so here.
679
		 */
680
		bread(dblk, inodebuf, size);
681
		nextinop = inodebuf;
682
	}
683
	dp = (union dinode *)nextinop;
684
	if (sblock.fs_magic == FS_UFS1_MAGIC)
685
		nextinop += sizeof(struct ufs1_dinode);
686
	else
687
		nextinop += sizeof(struct ufs2_dinode);
688
	return (dp);
689
}
690
691
/*
692
 * Prepare to scan a set of inodes.
693
 */
694
void
695
setinodebuf(ino_t inum)
696
{
697
698
	if (inum % sblock.fs_ipg != 0)
699
		errx(1, "bad inode number %llu to setinodebuf",
700
		    (unsigned long long)inum);
701
	lastvalidinum = inum + sblock.fs_ipg - 1;
702
	nextino = inum;
703
	lastinum = inum;
704
	readcnt = 0;
705
	if (inodebuf != NULL)
706
		return;
707
	inobufsize = blkroundup(&sblock, INOBUFSIZE);
708
	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
709
	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
710
	readpercg = sblock.fs_ipg / fullcnt;
711
	partialcnt = sblock.fs_ipg % fullcnt;
712
	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
713
	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
714
	if (partialcnt != 0) {
715
		readpercg++;
716
	} else {
717
		partialcnt = fullcnt;
718
		partialsize = inobufsize;
719
	}
720
	if ((inodebuf = malloc((size_t)inobufsize)) == NULL)
721
		errx(1, "cannot allocate space for inode buffer");
722
}
723
724
/*
725
 * Free up data structures used to scan inodes.
726
 */
727
void
728
freeinodebuf(void)
729
{
730
731
	free(inodebuf);
732
	inodebuf = NULL;
733
}
734
735
/*
736
 * Read specified disk blocks.
737
 */
738
void
739
bread(daddr_t bno, char *buf, long cnt)
740
{
741
	if (pread(fi, buf, cnt, bno * DEV_BSIZE) != cnt)
742
		err(1, "read failed on block %lld", (long long)bno);
743
}