xref: /netbsd/sbin/fsck_ext2fs/inode.c (revision 6550d01e)
1 /*	$NetBSD: inode.c,v 1.31 2010/02/04 23:55:42 christos 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
59 static char sccsid[] = "@(#)inode.c	8.5 (Berkeley) 2/8/95";
60 #else
61 __RCSID("$NetBSD: inode.c,v 1.31 2010/02/04 23:55:42 christos 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 
94 static ino_t startinum;
95 
96 static int iblock(struct inodesc *, long, u_int64_t);
97 
98 static int setlarge(void);
99 
100 static int
101 setlarge(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 
118 u_int64_t
119 inosize(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 
130 void
131 inossize(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 
146 int
147 ckinode(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 
231 static int
232 iblock(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  */
319 int
320 chkrange(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  */
366 struct ext2fs_dinode *
367 ginode(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  */
398 ino_t nextino, lastinum;
399 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
400 char *inodebuf;
401 
402 struct ext2fs_dinode *
403 getnextinode(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 
432 void
433 resetinodebuf(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 
458 void
459 freeinodebuf(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  */
474 void
475 cacheino(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  */
515 struct inoinfo *
516 getinoinfo(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  */
532 void
533 inocleanup(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 
546 void
547 inodirty(void)
548 {
549 
550 	dirty(pbp);
551 }
552 
553 void
554 clri(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 
575 int
576 findname(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 
594 int
595 findino(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 
611 void
612 pinode(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 
639 void
640 blkerror(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  */
668 ino_t
669 allocino(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  */
720 void
721 freeino(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