1/*	$NetBSD: inode.c,v 1.30 2009/10/19 18:41:08 bouyer Exp $	*/
2
3/*
4 * Copyright (c) 1980, 1986, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1997 Manuel Bouyer.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
45 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
47 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
48 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
49 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
53 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54 */
55
56#include <sys/cdefs.h>
57#ifndef lint
58#if 0
59static char sccsid[] = "@(#)inode.c	8.5 (Berkeley) 2/8/95";
60#else
61__RCSID("$NetBSD: inode.c,v 1.30 2009/10/19 18:41:08 bouyer Exp $");
62#endif
63#endif /* not lint */
64
65#include <sys/param.h>
66#include <sys/time.h>
67#include <ufs/ext2fs/ext2fs_dinode.h>
68#include <ufs/ext2fs/ext2fs_dir.h>
69#include <ufs/ext2fs/ext2fs.h>
70
71#include <ufs/ufs/dinode.h> /* for IFMT & friends */
72#ifndef SMALL
73#include <pwd.h>
74#endif
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
78#include <time.h>
79
80#include "fsck.h"
81#include "fsutil.h"
82#include "extern.h"
83
84/*
85 * CG is stored in fs byte order in memory, so we can't use ino_to_fsba
86 * here.
87 */
88
89#define fsck_ino_to_fsba(fs, x)                      \
90	(fs2h32((fs)->e2fs_gd[ino_to_cg(fs, x)].ext2bgd_i_tables) + \
91	(((x)-1) % (fs)->e2fs.e2fs_ipg)/(fs)->e2fs_ipb)
92
93
94static ino_t startinum;
95
96static int iblock(struct inodesc *, long, u_int64_t);
97
98static int setlarge(void);
99
100static int
101setlarge(void)
102{
103	if (sblock.e2fs.e2fs_rev < E2FS_REV1) {
104		pfatal("LARGE FILES UNSUPPORTED ON REVISION 0 FILESYSTEMS");
105		return 0;
106	}
107	if (!(sblock.e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGEFILE)) {
108		if (preen)
109			pwarn("SETTING LARGE FILE INDICATOR\n");
110		else if (!reply("SET LARGE FILE INDICATOR"))
111			return 0;
112		sblock.e2fs.e2fs_features_rocompat |= EXT2F_ROCOMPAT_LARGEFILE;
113		sbdirty();
114	}
115	return 1;
116}
117
118u_int64_t
119inosize(struct ext2fs_dinode *dp)
120{
121	u_int64_t size = fs2h32(dp->e2di_size);
122
123	if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG)
124		size |= (u_int64_t)fs2h32(dp->e2di_dacl) << 32;
125	if (size > INT32_MAX)
126		(void)setlarge();
127	return size;
128}
129
130void
131inossize(struct ext2fs_dinode *dp, u_int64_t size)
132{
133	if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG) {
134		dp->e2di_dacl = h2fs32(size >> 32);
135		if (size > INT32_MAX)
136			if (!setlarge())
137				return;
138	} else if (size > INT32_MAX) {
139		pfatal("TRYING TO SET FILESIZE TO %llu ON MODE %x FILE\n",
140		    (unsigned long long)size, fs2h16(dp->e2di_mode) & IFMT);
141		return;
142	}
143	dp->e2di_size = h2fs32(size);
144}
145
146int
147ckinode(struct ext2fs_dinode *dp, struct inodesc *idesc)
148{
149	u_int32_t *ap;
150	long ret, n, ndb;
151	struct ext2fs_dinode dino;
152	u_int64_t remsize, sizepb;
153	mode_t mode;
154	char pathbuf[MAXPATHLEN + 1];
155
156	if (idesc->id_fix != IGNORE)
157		idesc->id_fix = DONTKNOW;
158	idesc->id_entryno = 0;
159	idesc->id_filesize = inosize(dp);
160	mode = fs2h16(dp->e2di_mode) & IFMT;
161	if (mode == IFBLK || mode == IFCHR || mode == IFIFO ||
162	    (mode == IFLNK && (inosize(dp) < EXT2_MAXSYMLINKLEN)))
163		return (KEEPON);
164	dino = *dp;
165	ndb = howmany(inosize(&dino), sblock.e2fs_bsize);
166	for (ap = &dino.e2di_blocks[0]; ap < &dino.e2di_blocks[NDADDR];
167	    ap++,ndb--) {
168		idesc->id_numfrags = 1;
169		if (*ap == 0) {
170			if (idesc->id_type == DATA && ndb > 0) {
171				/* An empty block in a directory XXX */
172				getpathname(pathbuf, sizeof(pathbuf),
173				    idesc->id_number, idesc->id_number);
174				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
175				    pathbuf);
176				if (reply("ADJUST LENGTH") == 1) {
177					dp = ginode(idesc->id_number);
178					inossize(dp,
179					    (ap - &dino.e2di_blocks[0]) *
180					    sblock.e2fs_bsize);
181					printf(
182					    "YOU MUST RERUN FSCK AFTERWARDS\n");
183					rerun = 1;
184					inodirty();
185				}
186			}
187			continue;
188		}
189		idesc->id_blkno = fs2h32(*ap);
190		if (idesc->id_type == ADDR)
191			ret = (*idesc->id_func)(idesc);
192		else
193			ret = dirscan(idesc);
194		if (ret & STOP)
195			return (ret);
196	}
197	idesc->id_numfrags = 1;
198	remsize = inosize(&dino) - sblock.e2fs_bsize * NDADDR;
199	sizepb = sblock.e2fs_bsize;
200	for (ap = &dino.e2di_blocks[NDADDR], n = 1; n <= NIADDR; ap++, n++) {
201		if (*ap) {
202			idesc->id_blkno = fs2h32(*ap);
203			ret = iblock(idesc, n, remsize);
204			if (ret & STOP)
205				return (ret);
206		} else {
207			if (idesc->id_type == DATA && remsize > 0) {
208				/* An empty block in a directory XXX */
209				getpathname(pathbuf, sizeof(pathbuf),
210				    idesc->id_number, idesc->id_number);
211				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
212				    pathbuf);
213				if (reply("ADJUST LENGTH") == 1) {
214					dp = ginode(idesc->id_number);
215					inossize(dp, inosize(dp) - remsize);
216					remsize = 0;
217					printf(
218					    "YOU MUST RERUN FSCK AFTERWARDS\n");
219					rerun = 1;
220					inodirty();
221					break;
222				}
223			}
224		}
225		sizepb *= NINDIR(&sblock);
226		remsize -= sizepb;
227	}
228	return (KEEPON);
229}
230
231static int
232iblock(struct inodesc *idesc, long ilevel, u_int64_t isize)
233{
234	/* XXX ondisk32 */
235	int32_t *ap;
236	int32_t *aplim;
237	struct bufarea *bp;
238	int i, n, (*func)(struct inodesc *);
239	size_t nif;
240	u_int64_t sizepb;
241	char buf[BUFSIZ];
242	char pathbuf[MAXPATHLEN + 1];
243	struct ext2fs_dinode *dp;
244
245	if (idesc->id_type == ADDR) {
246		func = idesc->id_func;
247		if (((n = (*func)(idesc)) & KEEPON) == 0)
248			return (n);
249	} else
250		func = dirscan;
251	if (chkrange(idesc->id_blkno, idesc->id_numfrags))
252		return (SKIP);
253	bp = getdatablk(idesc->id_blkno, sblock.e2fs_bsize);
254	ilevel--;
255	for (sizepb = sblock.e2fs_bsize, i = 0; i < ilevel; i++)
256		sizepb *= NINDIR(&sblock);
257	if (isize > sizepb * NINDIR(&sblock))
258		nif = NINDIR(&sblock);
259	else
260		nif = howmany(isize, sizepb);
261	if (idesc->id_func == pass1check &&
262		nif < NINDIR(&sblock)) {
263		aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
264		for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
265			if (*ap == 0)
266				continue;
267			(void)snprintf(buf, sizeof(buf),
268			    "PARTIALLY TRUNCATED INODE I=%llu",
269			    (unsigned long long)idesc->id_number);
270			if (dofix(idesc, buf)) {
271				*ap = 0;
272				dirty(bp);
273			}
274		}
275		flush(fswritefd, bp);
276	}
277	aplim = &bp->b_un.b_indir[nif];
278	for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
279		if (*ap) {
280			idesc->id_blkno = fs2h32(*ap);
281			if (ilevel == 0)
282				n = (*func)(idesc);
283			else
284				n = iblock(idesc, ilevel, isize);
285			if (n & STOP) {
286				bp->b_flags &= ~B_INUSE;
287				return (n);
288			}
289		} else {
290			if (idesc->id_type == DATA && isize > 0) {
291				/* An empty block in a directory XXX */
292				getpathname(pathbuf, sizeof(pathbuf),
293				    idesc->id_number, idesc->id_number);
294				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
295				    pathbuf);
296				if (reply("ADJUST LENGTH") == 1) {
297					dp = ginode(idesc->id_number);
298					inossize(dp, inosize(dp) - isize);
299					isize = 0;
300					printf(
301					    "YOU MUST RERUN FSCK AFTERWARDS\n");
302					rerun = 1;
303					inodirty();
304					bp->b_flags &= ~B_INUSE;
305					return(STOP);
306				}
307			}
308		}
309		isize -= sizepb;
310	}
311	bp->b_flags &= ~B_INUSE;
312	return (KEEPON);
313}
314
315/*
316 * Check that a block in a legal block number.
317 * Return 0 if in range, 1 if out of range.
318 */
319int
320chkrange(daddr_t blk, int cnt)
321{
322	int c, overh;
323
324	if ((unsigned int)(blk + cnt) > maxfsblock)
325		return (1);
326	c = dtog(&sblock, blk);
327	overh = cgoverhead(c);
328	if (blk < sblock.e2fs.e2fs_bpg * c + overh +
329	    sblock.e2fs.e2fs_first_dblock) {
330		if ((blk + cnt) > sblock.e2fs.e2fs_bpg * c + overh +
331		    sblock.e2fs.e2fs_first_dblock) {
332			if (debug) {
333				printf("blk %lld < cgdmin %d;",
334				    (long long)blk,
335				    sblock.e2fs.e2fs_bpg * c + overh +
336				    sblock.e2fs.e2fs_first_dblock);
337				printf(" blk + cnt %lld > cgsbase %d\n",
338				    (long long)(blk + cnt),
339				    sblock.e2fs.e2fs_bpg * c +
340				    overh + sblock.e2fs.e2fs_first_dblock);
341			}
342			return (1);
343		}
344	} else {
345		if ((blk + cnt) > sblock.e2fs.e2fs_bpg * (c + 1) + overh +
346		    sblock.e2fs.e2fs_first_dblock) {
347			if (debug)  {
348				printf("blk %lld >= cgdmin %d;",
349				    (long long)blk,
350				    sblock.e2fs.e2fs_bpg * c + overh +
351				    sblock.e2fs.e2fs_first_dblock);
352				printf(" blk + cnt %lld > cgdmax %d\n",
353				    (long long)(blk+cnt),
354				    sblock.e2fs.e2fs_bpg * (c + 1) +
355				    overh + sblock.e2fs.e2fs_first_dblock);
356			}
357			return (1);
358		}
359	}
360	return (0);
361}
362
363/*
364 * General purpose interface for reading inodes.
365 */
366struct ext2fs_dinode *
367ginode(ino_t inumber)
368{
369	daddr_t iblk;
370	struct ext2fs_dinode *dp;
371
372	if ((inumber < EXT2_FIRSTINO &&
373	     inumber != EXT2_ROOTINO &&
374	     !(inumber == EXT2_RESIZEINO &&
375	       (sblock.e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) != 0))
376		|| inumber > maxino)
377		errexit("bad inode number %llu to ginode",
378		    (unsigned long long)inumber);
379	if (startinum == 0 ||
380	    inumber < startinum || inumber >= startinum + sblock.e2fs_ipb) {
381		iblk = fsck_ino_to_fsba(&sblock, inumber);
382		if (pbp != 0)
383			pbp->b_flags &= ~B_INUSE;
384		pbp = getdatablk(iblk, sblock.e2fs_bsize);
385		startinum =
386		    ((inumber - 1) / sblock.e2fs_ipb) * sblock.e2fs_ipb + 1;
387	}
388	dp = (struct ext2fs_dinode *)(pbp->b_un.b_buf +
389	    EXT2_DINODE_SIZE(&sblock) * ino_to_fsbo(&sblock, inumber));
390
391	return dp;
392}
393
394/*
395 * Special purpose version of ginode used to optimize first pass
396 * over all the inodes in numerical order.
397 */
398ino_t nextino, lastinum;
399long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
400char *inodebuf;
401
402struct ext2fs_dinode *
403getnextinode(ino_t inumber)
404{
405	long size;
406	daddr_t dblk;
407	struct ext2fs_dinode *dp;
408	static char *bp;
409
410	if (inumber != nextino++ || inumber > maxino)
411		errexit("bad inode number %llu to nextinode",
412		    (unsigned long long)inumber);
413	if (inumber >= lastinum) {
414		readcnt++;
415		dblk = fsbtodb(&sblock, fsck_ino_to_fsba(&sblock, lastinum));
416		if (readcnt % readpercg == 0) {
417			size = partialsize;
418			lastinum += partialcnt;
419		} else {
420			size = inobufsize;
421			lastinum += fullcnt;
422		}
423		(void)bread(fsreadfd, inodebuf, dblk, size);
424		bp = inodebuf;
425	}
426	dp = (struct ext2fs_dinode *)bp;
427	bp += EXT2_DINODE_SIZE(&sblock);
428
429	return dp;
430}
431
432void
433resetinodebuf(void)
434{
435
436	startinum = 0;
437	nextino = 1;
438	lastinum = 1;
439	readcnt = 0;
440	inobufsize = blkroundup(&sblock, INOBUFSIZE);
441	fullcnt = inobufsize / EXT2_DINODE_SIZE(&sblock);
442	readpercg = sblock.e2fs.e2fs_ipg / fullcnt;
443	partialcnt = sblock.e2fs.e2fs_ipg % fullcnt;
444	partialsize = partialcnt * EXT2_DINODE_SIZE(&sblock);
445	if (partialcnt != 0) {
446		readpercg++;
447	} else {
448		partialcnt = fullcnt;
449		partialsize = inobufsize;
450	}
451	if (inodebuf == NULL &&
452	    (inodebuf = malloc((unsigned int)inobufsize)) == NULL)
453		errexit("Cannot allocate space for inode buffer");
454	while (nextino < EXT2_ROOTINO)
455		(void)getnextinode(nextino);
456}
457
458void
459freeinodebuf(void)
460{
461
462	if (inodebuf != NULL)
463		free(inodebuf);
464	inodebuf = NULL;
465}
466
467/*
468 * Routines to maintain information about directory inodes.
469 * This is built during the first pass and used during the
470 * second and third passes.
471 *
472 * Enter inodes into the cache.
473 */
474void
475cacheino(struct ext2fs_dinode *dp, ino_t inumber)
476{
477	struct inoinfo *inp;
478	struct inoinfo **inpp;
479	unsigned int blks;
480
481	blks = howmany(inosize(dp), sblock.e2fs_bsize);
482	if (blks > NDADDR)
483		blks = NDADDR + NIADDR;
484	/* XXX ondisk32 */
485	inp = malloc(sizeof(*inp) + (blks - 1) * sizeof(int32_t));
486	if (inp == NULL)
487		return;
488	inpp = &inphead[inumber % numdirs];
489	inp->i_nexthash = *inpp;
490	*inpp = inp;
491	inp->i_child = inp->i_sibling = inp->i_parentp = 0;
492	if (inumber == EXT2_ROOTINO)
493		inp->i_parent = EXT2_ROOTINO;
494	else
495		inp->i_parent = (ino_t)0;
496	inp->i_dotdot = (ino_t)0;
497	inp->i_number = inumber;
498	inp->i_isize = inosize(dp);
499	/* XXX ondisk32 */
500	inp->i_numblks = blks * sizeof(int32_t);
501	memcpy(&inp->i_blks[0], &dp->e2di_blocks[0], (size_t)inp->i_numblks);
502	if (inplast == listmax) {
503		listmax += 100;
504		inpsort = (struct inoinfo **)realloc((char *)inpsort,
505		    (unsigned int)listmax * sizeof(struct inoinfo *));
506		if (inpsort == NULL)
507			errexit("cannot increase directory list");
508	}
509	inpsort[inplast++] = inp;
510}
511
512/*
513 * Look up an inode cache structure.
514 */
515struct inoinfo *
516getinoinfo(ino_t inumber)
517{
518	struct inoinfo *inp;
519
520	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
521		if (inp->i_number != inumber)
522			continue;
523		return (inp);
524	}
525	errexit("cannot find inode %llu", (unsigned long long)inumber);
526	return ((struct inoinfo *)0);
527}
528
529/*
530 * Clean up all the inode cache structure.
531 */
532void
533inocleanup(void)
534{
535	struct inoinfo **inpp;
536
537	if (inphead == NULL)
538		return;
539	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
540		free(*inpp);
541	free(inphead);
542	free(inpsort);
543	inphead = inpsort = NULL;
544}
545
546void
547inodirty(void)
548{
549
550	dirty(pbp);
551}
552
553void
554clri(struct inodesc *idesc, const char *type, int flag)
555{
556	struct ext2fs_dinode *dp;
557
558	dp = ginode(idesc->id_number);
559	if (flag == 1) {
560		pwarn("%s %s", type,
561		    (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
562		pinode(idesc->id_number);
563	}
564	if (preen || reply("CLEAR") == 1) {
565		if (preen)
566			printf(" (CLEARED)\n");
567		n_files--;
568		(void)ckinode(dp, idesc);
569		clearinode(dp);
570		statemap[idesc->id_number] = USTATE;
571		inodirty();
572	}
573}
574
575int
576findname(struct inodesc *idesc)
577{
578	struct ext2fs_direct *dirp = idesc->id_dirp;
579	u_int16_t namlen = dirp->e2d_namlen;
580	/* from utilities.c namebuf[] variable */
581	char *buf = __UNCONST(idesc->id_name);
582	if (namlen > MAXPATHLEN) {
583		/* XXX: Prevent overflow but don't fix */
584		namlen = MAXPATHLEN;
585	}
586
587	if (fs2h32(dirp->e2d_ino) != idesc->id_parent)
588		return (KEEPON);
589	(void)memcpy(buf, dirp->e2d_name, (size_t)namlen);
590	buf[namlen] = '\0';
591	return (STOP|FOUND);
592}
593
594int
595findino(struct inodesc *idesc)
596{
597	struct ext2fs_direct *dirp = idesc->id_dirp;
598	u_int32_t ino = fs2h32(dirp->e2d_ino);
599
600	if (ino == 0)
601		return (KEEPON);
602	if (strcmp(dirp->e2d_name, idesc->id_name) == 0 &&
603	    (ino == EXT2_ROOTINO || ino >= EXT2_FIRSTINO)
604		&& ino <= maxino) {
605		idesc->id_parent = ino;
606		return (STOP|FOUND);
607	}
608	return (KEEPON);
609}
610
611void
612pinode(ino_t ino)
613{
614	struct ext2fs_dinode *dp;
615	struct passwd *pw;
616	uid_t uid;
617
618	printf(" I=%llu ", (unsigned long long)ino);
619	if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino)
620		return;
621	dp = ginode(ino);
622	uid = fs2h16(dp->e2di_uid);
623	if (sblock.e2fs.e2fs_rev > E2FS_REV0)
624		uid |= fs2h16(dp->e2di_uid_high) << 16;
625	printf(" OWNER=");
626#ifndef SMALL
627	if (Uflag && (pw = getpwuid(uid)) != 0)
628		printf("%s ", pw->pw_name);
629	else
630#endif
631		printf("%u ", (unsigned int)uid);
632	printf("MODE=%o\n", fs2h16(dp->e2di_mode));
633	if (preen)
634		printf("%s: ", cdevname());
635	printf("SIZE=%llu ", (long long)inosize(dp));
636	printf("MTIME=%s ", print_mtime(fs2h32(dp->e2di_mtime)));
637}
638
639void
640blkerror(ino_t ino, const char *type, daddr_t blk)
641{
642
643	pfatal("%lld %s I=%llu", (long long)blk, type, (unsigned long long)ino);
644	printf("\n");
645	switch (statemap[ino]) {
646
647	case FSTATE:
648		statemap[ino] = FCLEAR;
649		return;
650
651	case DSTATE:
652		statemap[ino] = DCLEAR;
653		return;
654
655	case FCLEAR:
656	case DCLEAR:
657		return;
658
659	default:
660		errexit("BAD STATE %d TO BLKERR", statemap[ino]);
661		/* NOTREACHED */
662	}
663}
664
665/*
666 * allocate an unused inode
667 */
668ino_t
669allocino(ino_t request, int type)
670{
671	ino_t ino;
672	struct ext2fs_dinode *dp;
673	time_t t;
674
675	if (request == 0)
676		request = EXT2_ROOTINO;
677	else if (statemap[request] != USTATE)
678		return (0);
679	for (ino = request; ino < maxino; ino++) {
680		if ((ino > EXT2_ROOTINO) && (ino < EXT2_FIRSTINO))
681			continue;
682		if (statemap[ino] == USTATE)
683			break;
684	}
685	if (ino == maxino)
686		return (0);
687	switch (type & IFMT) {
688	case IFDIR:
689		statemap[ino] = DSTATE;
690		break;
691	case IFREG:
692	case IFLNK:
693		statemap[ino] = FSTATE;
694		break;
695	default:
696		return (0);
697	}
698	dp = ginode(ino);
699	dp->e2di_blocks[0] = h2fs32(allocblk());
700	if (dp->e2di_blocks[0] == 0) {
701		statemap[ino] = USTATE;
702		return (0);
703	}
704	dp->e2di_mode = h2fs16(type);
705	(void)time(&t);
706	dp->e2di_atime = h2fs32(t);
707	dp->e2di_mtime = dp->e2di_ctime = dp->e2di_atime;
708	dp->e2di_dtime = 0;
709	inossize(dp, sblock.e2fs_bsize);
710	dp->e2di_nblock = h2fs32(btodb(sblock.e2fs_bsize));
711	n_files++;
712	inodirty();
713	typemap[ino] = E2IFTODT(type);
714	return (ino);
715}
716
717/*
718 * deallocate an inode
719 */
720void
721freeino(ino_t ino)
722{
723	struct inodesc idesc;
724	struct ext2fs_dinode *dp;
725
726	memset(&idesc, 0, sizeof(struct inodesc));
727	idesc.id_type = ADDR;
728	idesc.id_func = pass4check;
729	idesc.id_number = ino;
730	dp = ginode(ino);
731	(void)ckinode(dp, &idesc);
732	clearinode(dp);
733	inodirty();
734	statemap[ino] = USTATE;
735	n_files--;
736}
737