xref: /openbsd-src/sbin/fsck_ext2fs/inode.c (revision a7b9eedcb4c1ab985f28fc923822c24bc6417908)
1*a7b9eedcSflorian /*	$OpenBSD: inode.c,v 1.31 2024/05/09 08:35:40 florian Exp $	*/
20190393fSart /*	$NetBSD: inode.c,v 1.8 2000/01/28 16:01:46 bouyer Exp $	*/
38c424e8eSdownsj 
48c424e8eSdownsj /*
55ff4e0c8Sdownsj  * Copyright (c) 1997 Manuel Bouyer.
68c424e8eSdownsj  * Copyright (c) 1980, 1986, 1993
78c424e8eSdownsj  *	The Regents of the University of California.  All rights reserved.
88c424e8eSdownsj  *
98c424e8eSdownsj  * Redistribution and use in source and binary forms, with or without
108c424e8eSdownsj  * modification, are permitted provided that the following conditions
118c424e8eSdownsj  * are met:
128c424e8eSdownsj  * 1. Redistributions of source code must retain the above copyright
138c424e8eSdownsj  *    notice, this list of conditions and the following disclaimer.
148c424e8eSdownsj  * 2. Redistributions in binary form must reproduce the above copyright
158c424e8eSdownsj  *    notice, this list of conditions and the following disclaimer in the
168c424e8eSdownsj  *    documentation and/or other materials provided with the distribution.
171ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
188c424e8eSdownsj  *    may be used to endorse or promote products derived from this software
198c424e8eSdownsj  *    without specific prior written permission.
208c424e8eSdownsj  *
218c424e8eSdownsj  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
228c424e8eSdownsj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
238c424e8eSdownsj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
248c424e8eSdownsj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
258c424e8eSdownsj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
268c424e8eSdownsj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
278c424e8eSdownsj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
288c424e8eSdownsj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
298c424e8eSdownsj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
308c424e8eSdownsj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
318c424e8eSdownsj  * SUCH DAMAGE.
328c424e8eSdownsj  */
338c424e8eSdownsj 
34b9fc9a72Sderaadt #include <sys/param.h>	/* btodb */
358c424e8eSdownsj #include <sys/time.h>
368c424e8eSdownsj #include <ufs/ext2fs/ext2fs_dinode.h>
378c424e8eSdownsj #include <ufs/ext2fs/ext2fs_dir.h>
388c424e8eSdownsj #include <ufs/ext2fs/ext2fs.h>
398c424e8eSdownsj 
408c424e8eSdownsj #include <ufs/ufs/dinode.h> /* for IFMT & friends */
418c424e8eSdownsj #ifndef SMALL
428c424e8eSdownsj #include <pwd.h>
438c424e8eSdownsj #endif
448c424e8eSdownsj #include <stdio.h>
458c424e8eSdownsj #include <stdlib.h>
468c424e8eSdownsj #include <string.h>
470190393fSart #include <time.h>
48b9fc9a72Sderaadt #include <limits.h>
498c424e8eSdownsj 
508c424e8eSdownsj #include "fsck.h"
518c424e8eSdownsj #include "fsutil.h"
528c424e8eSdownsj #include "extern.h"
538c424e8eSdownsj 
5465348f21Sjasoni /*
5565348f21Sjasoni  * CG is stored in fs byte order in memory, so we can't use ino_to_fsba
5665348f21Sjasoni  * here.
5765348f21Sjasoni  */
5865348f21Sjasoni 
5965348f21Sjasoni #define fsck_ino_to_fsba(fs, x) \
6060a51e06Spelikan 	(letoh32((fs)->e2fs_gd[ino_to_cg(fs, x)].ext2bgd_i_tables) + \
6165348f21Sjasoni 	(((x)-1) % (fs)->e2fs.e2fs_ipg)/(fs)->e2fs_ipb)
6265348f21Sjasoni 
638c424e8eSdownsj static ino_t startinum;
648c424e8eSdownsj 
65c72b5b24Smillert static int iblock(struct inodesc *, long, u_int64_t);
66935730fbSniallo static int setlarge(void);
67935730fbSniallo 
68935730fbSniallo static int
setlarge(void)69935730fbSniallo setlarge(void)
70935730fbSniallo {
71935730fbSniallo 	if (sblock.e2fs.e2fs_rev < E2FS_REV1) {
72935730fbSniallo 		pfatal("LARGE FILES UNSUPPORTED ON REVISION 0 FILESYSTEMS");
73935730fbSniallo 		return 0;
74935730fbSniallo 	}
75bf2afe82Skevlo 	if (!(sblock.e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGE_FILE)) {
76935730fbSniallo 		if (preen)
77935730fbSniallo 			pwarn("SETTING LARGE FILE INDICATOR\n");
78935730fbSniallo 		else if (!reply("SET LARGE FILE INDICATOR"))
79935730fbSniallo 			return 0;
80bf2afe82Skevlo 		sblock.e2fs.e2fs_features_rocompat |= EXT2F_ROCOMPAT_LARGE_FILE;
81935730fbSniallo 		sbdirty();
82935730fbSniallo 	}
83935730fbSniallo 	return 1;
84935730fbSniallo }
85935730fbSniallo 
86935730fbSniallo u_int64_t
inosize(struct ext2fs_dinode * dp)87935730fbSniallo inosize(struct ext2fs_dinode *dp)
88935730fbSniallo {
8960a51e06Spelikan 	u_int64_t size = letoh32(dp->e2di_size);
90935730fbSniallo 
9160a51e06Spelikan 	if ((letoh16(dp->e2di_mode) & IFMT) == IFREG)
9260a51e06Spelikan 		size |= (u_int64_t)letoh32(dp->e2di_size_hi) << 32;
93935730fbSniallo 	if (size >= 0x80000000U)
94935730fbSniallo 		 (void)setlarge();
95935730fbSniallo 	return size;
96935730fbSniallo }
97935730fbSniallo 
98935730fbSniallo void
inossize(struct ext2fs_dinode * dp,u_int64_t size)99935730fbSniallo inossize(struct ext2fs_dinode *dp, u_int64_t size)
100935730fbSniallo {
10160a51e06Spelikan 	if ((letoh16(dp->e2di_mode) & IFMT) == IFREG) {
10260a51e06Spelikan 		dp->e2di_size_hi = htole32(size >> 32);
103935730fbSniallo 		if (size >= 0x80000000U)
104935730fbSniallo 			if (!setlarge())
105935730fbSniallo 				return;
106935730fbSniallo 	} else if (size >= 0x80000000U) {
107935730fbSniallo 		pfatal("TRYING TO SET FILESIZE TO %llu ON MODE %x FILE\n",
10860a51e06Spelikan 		    (unsigned long long)size, letoh16(dp->e2di_mode) & IFMT);
109935730fbSniallo 		return;
110935730fbSniallo 	}
11160a51e06Spelikan 	dp->e2di_size = htole32(size);
112935730fbSniallo }
1138c424e8eSdownsj 
1148c424e8eSdownsj int
ckinode(struct ext2fs_dinode * dp,struct inodesc * idesc)1158809fabbSderaadt ckinode(struct ext2fs_dinode *dp, struct inodesc *idesc)
1168c424e8eSdownsj {
1170190393fSart 	u_int32_t *ap;
11865348f21Sjasoni 	long ret, n, ndb;
1198c424e8eSdownsj 	struct ext2fs_dinode dino;
1208c424e8eSdownsj 	u_int64_t remsize, sizepb;
1218c424e8eSdownsj 	mode_t mode;
122b9fc9a72Sderaadt 	char pathbuf[PATH_MAX + 1];
1238c424e8eSdownsj 
1248c424e8eSdownsj 	if (idesc->id_fix != IGNORE)
1258c424e8eSdownsj 		idesc->id_fix = DONTKNOW;
1268c424e8eSdownsj 	idesc->id_entryno = 0;
127935730fbSniallo 	idesc->id_filesize = inosize(dp);
12860a51e06Spelikan 	mode = letoh16(dp->e2di_mode) & IFMT;
12965348f21Sjasoni 	if (mode == IFBLK || mode == IFCHR || mode == IFIFO ||
130935730fbSniallo 	    (mode == IFLNK && (inosize(dp) < EXT2_MAXSYMLINKLEN)))
1318c424e8eSdownsj 		return (KEEPON);
1328c424e8eSdownsj 	dino = *dp;
133935730fbSniallo 	ndb = howmany(inosize(&dino), sblock.e2fs_bsize);
1348c424e8eSdownsj 	for (ap = &dino.e2di_blocks[0]; ap < &dino.e2di_blocks[NDADDR];
1358c424e8eSdownsj 																ap++,ndb--) {
1368c424e8eSdownsj 		idesc->id_numfrags = 1;
1378c424e8eSdownsj 		if (*ap == 0) {
1388c424e8eSdownsj 			if (idesc->id_type == DATA && ndb > 0) {
1398c424e8eSdownsj 				/* An empty block in a directory XXX */
140487a1948Stedu 				getpathname(pathbuf, sizeof pathbuf,
141487a1948Stedu 				    idesc->id_number, idesc->id_number);
1428c424e8eSdownsj 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
1438c424e8eSdownsj 				    pathbuf);
1448c424e8eSdownsj 				if (reply("ADJUST LENGTH") == 1) {
1458c424e8eSdownsj 					dp = ginode(idesc->id_number);
146935730fbSniallo 					inossize(dp,
147935730fbSniallo 					    (ap - &dino.e2di_blocks[0]) *
14865348f21Sjasoni 					    sblock.e2fs_bsize);
1498c424e8eSdownsj 					printf(
1508c424e8eSdownsj 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
1518c424e8eSdownsj 					rerun = 1;
1528c424e8eSdownsj 					inodirty();
1538c424e8eSdownsj 				}
1548c424e8eSdownsj 			}
1558c424e8eSdownsj 			continue;
1568c424e8eSdownsj 		}
15760a51e06Spelikan 		idesc->id_blkno = letoh32(*ap);
1588c424e8eSdownsj 		if (idesc->id_type == ADDR)
1598c424e8eSdownsj 			ret = (*idesc->id_func)(idesc);
1608c424e8eSdownsj 		else
1618c424e8eSdownsj 			ret = dirscan(idesc);
1628c424e8eSdownsj 		if (ret & STOP)
1638c424e8eSdownsj 			return (ret);
1648c424e8eSdownsj 	}
1658c424e8eSdownsj 	idesc->id_numfrags = 1;
166935730fbSniallo 	remsize = inosize(&dino) - sblock.e2fs_bsize * NDADDR;
1678c424e8eSdownsj 	sizepb = sblock.e2fs_bsize;
1688c424e8eSdownsj 	for (ap = &dino.e2di_blocks[NDADDR], n = 1; n <= NIADDR; ap++, n++) {
1698c424e8eSdownsj 		if (*ap) {
17060a51e06Spelikan 			idesc->id_blkno = letoh32(*ap);
1718c424e8eSdownsj 			ret = iblock(idesc, n, remsize);
1728c424e8eSdownsj 			if (ret & STOP)
1738c424e8eSdownsj 				return (ret);
1748c424e8eSdownsj 		} else {
1758c424e8eSdownsj 			if (idesc->id_type == DATA && remsize > 0) {
1768c424e8eSdownsj 				/* An empty block in a directory XXX */
177487a1948Stedu 				getpathname(pathbuf, sizeof pathbuf,
178487a1948Stedu 				    idesc->id_number, idesc->id_number);
1798c424e8eSdownsj 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
1808c424e8eSdownsj 				    pathbuf);
1818c424e8eSdownsj 				if (reply("ADJUST LENGTH") == 1) {
1828c424e8eSdownsj 					dp = ginode(idesc->id_number);
183935730fbSniallo 					inossize(dp, inosize(dp) - remsize);
1848c424e8eSdownsj 					remsize = 0;
1858c424e8eSdownsj 					printf(
1868c424e8eSdownsj 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
1878c424e8eSdownsj 					rerun = 1;
1888c424e8eSdownsj 					inodirty();
1898c424e8eSdownsj 					break;
1908c424e8eSdownsj 				}
1918c424e8eSdownsj 			}
1928c424e8eSdownsj 		}
1938c424e8eSdownsj 		sizepb *= NINDIR(&sblock);
1948c424e8eSdownsj 		remsize -= sizepb;
1958c424e8eSdownsj 	}
1968c424e8eSdownsj 	return (KEEPON);
1978c424e8eSdownsj }
1988c424e8eSdownsj 
1998c424e8eSdownsj static int
iblock(struct inodesc * idesc,long ilevel,u_int64_t isize)2008809fabbSderaadt iblock(struct inodesc *idesc, long ilevel, u_int64_t isize)
2018c424e8eSdownsj {
202b6d2e2d5Sderaadt 	daddr32_t *ap;
203b6d2e2d5Sderaadt 	daddr32_t *aplim;
2040190393fSart 	struct bufarea *bp;
205c72b5b24Smillert 	int i, n, (*func)(struct inodesc *), nif;
2068c424e8eSdownsj 	u_int64_t sizepb;
2078c424e8eSdownsj 	char buf[BUFSIZ];
208b9fc9a72Sderaadt 	char pathbuf[PATH_MAX + 1];
2098c424e8eSdownsj 	struct ext2fs_dinode *dp;
2108c424e8eSdownsj 
2118c424e8eSdownsj 	if (idesc->id_type == ADDR) {
2128c424e8eSdownsj 		func = idesc->id_func;
2138c424e8eSdownsj 		if (((n = (*func)(idesc)) & KEEPON) == 0)
2148c424e8eSdownsj 			return (n);
2158c424e8eSdownsj 	} else
2168c424e8eSdownsj 		func = dirscan;
2178c424e8eSdownsj 	if (chkrange(idesc->id_blkno, idesc->id_numfrags))
2188c424e8eSdownsj 		return (SKIP);
2198c424e8eSdownsj 	bp = getdatablk(idesc->id_blkno, sblock.e2fs_bsize);
2208c424e8eSdownsj 	ilevel--;
2218c424e8eSdownsj 	for (sizepb = sblock.e2fs_bsize, i = 0; i < ilevel; i++)
2228c424e8eSdownsj 		sizepb *= NINDIR(&sblock);
2238c424e8eSdownsj 	if (isize > sizepb * NINDIR(&sblock))
2248c424e8eSdownsj 		nif = NINDIR(&sblock);
2258c424e8eSdownsj 	else
2268c424e8eSdownsj 		nif = howmany(isize, sizepb);
2278c424e8eSdownsj 	if (idesc->id_func == pass1check &&
2288c424e8eSdownsj 		nif < NINDIR(&sblock)) {
2298c424e8eSdownsj 		aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
2308c424e8eSdownsj 		for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
2318c424e8eSdownsj 			if (*ap == 0)
2328c424e8eSdownsj 				continue;
23365348f21Sjasoni 			(void)snprintf(buf, sizeof(buf),
2343b92bd08Sderaadt 			    "PARTIALLY TRUNCATED INODE I=%llu",
2353b92bd08Sderaadt 			    (unsigned long long)idesc->id_number);
2368c424e8eSdownsj 			if (dofix(idesc, buf)) {
2378c424e8eSdownsj 				*ap = 0;
2388c424e8eSdownsj 				dirty(bp);
2398c424e8eSdownsj 			}
2408c424e8eSdownsj 		}
2418c424e8eSdownsj 		flush(fswritefd, bp);
2428c424e8eSdownsj 	}
2438c424e8eSdownsj 	aplim = &bp->b_un.b_indir[nif];
2448c424e8eSdownsj 	for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
2458c424e8eSdownsj 		if (*ap) {
24660a51e06Spelikan 			idesc->id_blkno = letoh32(*ap);
2478c424e8eSdownsj 			if (ilevel == 0)
2488c424e8eSdownsj 				n = (*func)(idesc);
2498c424e8eSdownsj 			else
2508c424e8eSdownsj 				n = iblock(idesc, ilevel, isize);
2518c424e8eSdownsj 			if (n & STOP) {
2528c424e8eSdownsj 				bp->b_flags &= ~B_INUSE;
2538c424e8eSdownsj 				return (n);
2548c424e8eSdownsj 			}
2558c424e8eSdownsj 		} else {
2568c424e8eSdownsj 			if (idesc->id_type == DATA && isize > 0) {
2578c424e8eSdownsj 				/* An empty block in a directory XXX */
258487a1948Stedu 				getpathname(pathbuf, sizeof pathbuf,
259487a1948Stedu 				    idesc->id_number, idesc->id_number);
2608c424e8eSdownsj 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
2618c424e8eSdownsj 				    pathbuf);
2628c424e8eSdownsj 				if (reply("ADJUST LENGTH") == 1) {
2638c424e8eSdownsj 					dp = ginode(idesc->id_number);
264935730fbSniallo 					inossize(dp, inosize(dp) - isize);
2658c424e8eSdownsj 					isize = 0;
2668c424e8eSdownsj 					printf(
2678c424e8eSdownsj 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
2688c424e8eSdownsj 					rerun = 1;
2698c424e8eSdownsj 					inodirty();
2708c424e8eSdownsj 					bp->b_flags &= ~B_INUSE;
2718c424e8eSdownsj 					return(STOP);
2728c424e8eSdownsj 				}
2738c424e8eSdownsj 			}
2748c424e8eSdownsj 		}
2758c424e8eSdownsj 		isize -= sizepb;
2768c424e8eSdownsj 	}
2778c424e8eSdownsj 	bp->b_flags &= ~B_INUSE;
2788c424e8eSdownsj 	return (KEEPON);
2798c424e8eSdownsj }
2808c424e8eSdownsj 
2818c424e8eSdownsj /*
2828c424e8eSdownsj  * Check that a block in a legal block number.
2838c424e8eSdownsj  * Return 0 if in range, 1 if out of range.
2848c424e8eSdownsj  */
2858c424e8eSdownsj int
chkrange(daddr32_t blk,int cnt)286b6d2e2d5Sderaadt chkrange(daddr32_t blk, int cnt)
2878c424e8eSdownsj {
2880190393fSart 	int c, overh;
2898c424e8eSdownsj 
2908c424e8eSdownsj 	if ((unsigned)(blk + cnt) > maxfsblock)
2918c424e8eSdownsj 		return (1);
2928c424e8eSdownsj 	c = dtog(&sblock, blk);
29365348f21Sjasoni 	overh = cgoverhead(c);
29465348f21Sjasoni 	if (blk < sblock.e2fs.e2fs_bpg * c + overh +
2958c424e8eSdownsj 	    sblock.e2fs.e2fs_first_dblock) {
29665348f21Sjasoni 		if ((blk + cnt) > sblock.e2fs.e2fs_bpg * c + overh +
2978c424e8eSdownsj 		    sblock.e2fs.e2fs_first_dblock) {
2988c424e8eSdownsj 			if (debug) {
2998c424e8eSdownsj 				printf("blk %d < cgdmin %d;",
30065348f21Sjasoni 				    blk, sblock.e2fs.e2fs_bpg * c + overh +
3018c424e8eSdownsj 				    sblock.e2fs.e2fs_first_dblock);
3028c424e8eSdownsj 				printf(" blk + cnt %d > cgsbase %d\n",
30365348f21Sjasoni 				    blk + cnt, sblock.e2fs.e2fs_bpg * c +
30465348f21Sjasoni 				    overh + sblock.e2fs.e2fs_first_dblock);
3058c424e8eSdownsj 			}
3068c424e8eSdownsj 			return (1);
3078c424e8eSdownsj 		}
3088c424e8eSdownsj 	} else {
30965348f21Sjasoni 		if ((blk + cnt) > sblock.e2fs.e2fs_bpg * (c + 1) + overh +
3108c424e8eSdownsj 		    sblock.e2fs.e2fs_first_dblock) {
3118c424e8eSdownsj 			if (debug)  {
3128c424e8eSdownsj 				printf("blk %d >= cgdmin %d;",
31365348f21Sjasoni 				    blk, sblock.e2fs.e2fs_bpg * c + overh +
3148c424e8eSdownsj 				    sblock.e2fs.e2fs_first_dblock);
3158c424e8eSdownsj 				printf(" blk + cnt %d > cgdmax %d\n",
31665348f21Sjasoni 				    blk+cnt, sblock.e2fs.e2fs_bpg * (c + 1) +
31765348f21Sjasoni 				    overh + sblock.e2fs.e2fs_first_dblock);
3188c424e8eSdownsj 			}
3198c424e8eSdownsj 			return (1);
3208c424e8eSdownsj 		}
3218c424e8eSdownsj 	}
3228c424e8eSdownsj 	return (0);
3238c424e8eSdownsj }
3248c424e8eSdownsj 
3258c424e8eSdownsj /*
3268c424e8eSdownsj  * General purpose interface for reading inodes.
3278c424e8eSdownsj  */
3288c424e8eSdownsj struct ext2fs_dinode *
ginode(ino_t inumber)3298809fabbSderaadt ginode(ino_t inumber)
3308c424e8eSdownsj {
331b6d2e2d5Sderaadt 	daddr32_t iblk;
3328c424e8eSdownsj 
3338c424e8eSdownsj 	if ((inumber < EXT2_FIRSTINO && inumber != EXT2_ROOTINO)
3348c424e8eSdownsj 		|| inumber > maxino)
3353b92bd08Sderaadt 		errexit("bad inode number %llu to ginode\n",
3363b92bd08Sderaadt 		    (unsigned long long)inumber);
3378c424e8eSdownsj 	if (startinum == 0 ||
3388c424e8eSdownsj 	    inumber < startinum || inumber >= startinum + sblock.e2fs_ipb) {
3390190393fSart 		iblk = fsck_ino_to_fsba(&sblock, inumber);
3408c424e8eSdownsj 		if (pbp != 0)
3418c424e8eSdownsj 			pbp->b_flags &= ~B_INUSE;
3428c424e8eSdownsj 		pbp = getdatablk(iblk, sblock.e2fs_bsize);
3438c424e8eSdownsj 		startinum = ((inumber -1) / sblock.e2fs_ipb) * sblock.e2fs_ipb + 1;
3448c424e8eSdownsj 	}
3458c424e8eSdownsj 	return (&pbp->b_un.b_dinode[(inumber-1) % sblock.e2fs_ipb]);
3468c424e8eSdownsj }
3478c424e8eSdownsj 
3488c424e8eSdownsj /*
3498c424e8eSdownsj  * Special purpose version of ginode used to optimize first pass
3508c424e8eSdownsj  * over all the inodes in numerical order.
3518c424e8eSdownsj  */
3528c424e8eSdownsj ino_t nextino, lastinum;
3538c424e8eSdownsj long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
3548c424e8eSdownsj struct ext2fs_dinode *inodebuf;
3558c424e8eSdownsj 
3568c424e8eSdownsj struct ext2fs_dinode *
getnextinode(ino_t inumber)3578809fabbSderaadt getnextinode(ino_t inumber)
3588c424e8eSdownsj {
3598c424e8eSdownsj 	long size;
360b6d2e2d5Sderaadt 	daddr32_t dblk;
36150e48d67Skrw 	struct ext2fs_dinode *dp;
36250e48d67Skrw 	static char *bp;
3638c424e8eSdownsj 
3648c424e8eSdownsj 	if (inumber != nextino++ || inumber > maxino)
3653b92bd08Sderaadt 		errexit("bad inode number %llu to nextinode\n",
3663b92bd08Sderaadt 		    (unsigned long long)inumber);
3678c424e8eSdownsj 	if (inumber >= lastinum) {
3688c424e8eSdownsj 		readcnt++;
3690190393fSart 		dblk = fsbtodb(&sblock, fsck_ino_to_fsba(&sblock, lastinum));
3708c424e8eSdownsj 		if (readcnt % readpercg == 0) {
3718c424e8eSdownsj 			size = partialsize;
3728c424e8eSdownsj 			lastinum += partialcnt;
3738c424e8eSdownsj 		} else {
3748c424e8eSdownsj 			size = inobufsize;
3758c424e8eSdownsj 			lastinum += fullcnt;
3768c424e8eSdownsj 		}
3778c424e8eSdownsj 		(void)bread(fsreadfd, (char *)inodebuf, dblk, size);
37850e48d67Skrw 		bp = (char *)inodebuf;
3798c424e8eSdownsj 	}
38050e48d67Skrw 
38150e48d67Skrw 	dp = (struct ext2fs_dinode *)bp;
38250e48d67Skrw 	bp += EXT2_DINODE_SIZE(&sblock);
38350e48d67Skrw 
38450e48d67Skrw 	return (dp);
3858c424e8eSdownsj }
3868c424e8eSdownsj 
3878c424e8eSdownsj void
resetinodebuf(void)3888809fabbSderaadt resetinodebuf(void)
3898c424e8eSdownsj {
3908c424e8eSdownsj 
3918c424e8eSdownsj 	startinum = 0;
3928c424e8eSdownsj 	nextino = 1;
3938c424e8eSdownsj 	lastinum = 1;
3948c424e8eSdownsj 	readcnt = 0;
3958c424e8eSdownsj 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
39650e48d67Skrw 	fullcnt = inobufsize / EXT2_DINODE_SIZE(&sblock);
3978c424e8eSdownsj 	readpercg = sblock.e2fs.e2fs_ipg / fullcnt;
3988c424e8eSdownsj 	partialcnt = sblock.e2fs.e2fs_ipg % fullcnt;
39950e48d67Skrw 	partialsize = partialcnt * EXT2_DINODE_SIZE(&sblock);
4008c424e8eSdownsj 	if (partialcnt != 0) {
4018c424e8eSdownsj 		readpercg++;
4028c424e8eSdownsj 	} else {
4038c424e8eSdownsj 		partialcnt = fullcnt;
4048c424e8eSdownsj 		partialsize = inobufsize;
4058c424e8eSdownsj 	}
4068c424e8eSdownsj 	if (inodebuf == NULL &&
407f87ea3b6Sderaadt 	    (inodebuf = malloc((unsigned)inobufsize)) == NULL)
4088c424e8eSdownsj 		errexit("Cannot allocate space for inode buffer\n");
4098c424e8eSdownsj 	while (nextino < EXT2_ROOTINO)
4108c424e8eSdownsj 		(void)getnextinode(nextino);
4118c424e8eSdownsj }
4128c424e8eSdownsj 
4138c424e8eSdownsj void
freeinodebuf(void)4148809fabbSderaadt freeinodebuf(void)
4158c424e8eSdownsj {
4168c424e8eSdownsj 
4178c424e8eSdownsj 	if (inodebuf != NULL)
4188c424e8eSdownsj 		free((char *)inodebuf);
4198c424e8eSdownsj 	inodebuf = NULL;
4208c424e8eSdownsj }
4218c424e8eSdownsj 
4228c424e8eSdownsj /*
4238c424e8eSdownsj  * Routines to maintain information about directory inodes.
4248c424e8eSdownsj  * This is built during the first pass and used during the
4258c424e8eSdownsj  * second and third passes.
4268c424e8eSdownsj  *
4278c424e8eSdownsj  * Enter inodes into the cache.
4288c424e8eSdownsj  */
4298c424e8eSdownsj void
cacheino(struct ext2fs_dinode * dp,ino_t inumber)4308809fabbSderaadt cacheino(struct ext2fs_dinode *dp, ino_t inumber)
4318c424e8eSdownsj {
4320190393fSart 	struct inoinfo *inp;
4338c424e8eSdownsj 	struct inoinfo **inpp;
4348c424e8eSdownsj 	unsigned int blks;
4358c424e8eSdownsj 
436935730fbSniallo 	blks = howmany(inosize(dp), sblock.e2fs_bsize);
4378c424e8eSdownsj 	if (blks > NDADDR)
4388c424e8eSdownsj 		blks = NDADDR + NIADDR;
439f87ea3b6Sderaadt 	inp = malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr32_t));
4400190393fSart 	if (inp == NULL)
4418c424e8eSdownsj 		return;
4428c424e8eSdownsj 	inpp = &inphead[inumber % numdirs];
4438c424e8eSdownsj 	inp->i_nexthash = *inpp;
4448c424e8eSdownsj 	*inpp = inp;
4458c424e8eSdownsj 	inp->i_child = inp->i_sibling = inp->i_parentp = 0;
4468c424e8eSdownsj 	if (inumber == EXT2_ROOTINO)
4478c424e8eSdownsj 		inp->i_parent = EXT2_ROOTINO;
4488c424e8eSdownsj 	else
4498c424e8eSdownsj 		inp->i_parent = (ino_t)0;
4508c424e8eSdownsj 	inp->i_dotdot = (ino_t)0;
4518c424e8eSdownsj 	inp->i_number = inumber;
452935730fbSniallo 	inp->i_isize = inosize(dp);
453b6d2e2d5Sderaadt 	inp->i_numblks = blks * sizeof(daddr32_t);
4548c424e8eSdownsj 	memcpy(&inp->i_blks[0], &dp->e2di_blocks[0], (size_t)inp->i_numblks);
4558c424e8eSdownsj 	if (inplast == listmax) {
4568c424e8eSdownsj 		listmax += 100;
45724dc2196Sderaadt 		inpsort = reallocarray(inpsort, listmax,
45824dc2196Sderaadt 		    sizeof(struct inoinfo *));
4598c424e8eSdownsj 		if (inpsort == NULL)
4608c424e8eSdownsj 			errexit("cannot increase directory list\n");
4618c424e8eSdownsj 	}
4628c424e8eSdownsj 	inpsort[inplast++] = inp;
4638c424e8eSdownsj }
4648c424e8eSdownsj 
4658c424e8eSdownsj /*
4668c424e8eSdownsj  * Look up an inode cache structure.
4678c424e8eSdownsj  */
4688c424e8eSdownsj struct inoinfo *
getinoinfo(ino_t inumber)4698809fabbSderaadt getinoinfo(ino_t inumber)
4708c424e8eSdownsj {
4710190393fSart 	struct inoinfo *inp;
4728c424e8eSdownsj 
4738c424e8eSdownsj 	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
4748c424e8eSdownsj 		if (inp->i_number != inumber)
4758c424e8eSdownsj 			continue;
4768c424e8eSdownsj 		return (inp);
4778c424e8eSdownsj 	}
4783b92bd08Sderaadt 	errexit("cannot find inode %llu\n", (unsigned long long)inumber);
47992dc8f3cSkstailey 	return (NULL);
4808c424e8eSdownsj }
4818c424e8eSdownsj 
4828c424e8eSdownsj /*
4838c424e8eSdownsj  * Clean up all the inode cache structure.
4848c424e8eSdownsj  */
4858c424e8eSdownsj void
inocleanup(void)4868809fabbSderaadt inocleanup(void)
4878c424e8eSdownsj {
4880190393fSart 	struct inoinfo **inpp;
4898c424e8eSdownsj 
4908c424e8eSdownsj 	if (inphead == NULL)
4918c424e8eSdownsj 		return;
4928c424e8eSdownsj 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
4938c424e8eSdownsj 		free((char *)(*inpp));
4948c424e8eSdownsj 	free((char *)inphead);
4958c424e8eSdownsj 	free((char *)inpsort);
4968c424e8eSdownsj 	inphead = inpsort = NULL;
4978c424e8eSdownsj }
4988c424e8eSdownsj 
4998c424e8eSdownsj void
inodirty(void)5008809fabbSderaadt inodirty(void)
5018c424e8eSdownsj {
5028c424e8eSdownsj 
5038c424e8eSdownsj 	dirty(pbp);
5048c424e8eSdownsj }
5058c424e8eSdownsj 
5068c424e8eSdownsj void
clri(struct inodesc * idesc,char * type,int flag)5078809fabbSderaadt clri(struct inodesc *idesc, char *type, int flag)
5088c424e8eSdownsj {
5090190393fSart 	struct ext2fs_dinode *dp;
5108c424e8eSdownsj 
5118c424e8eSdownsj 	dp = ginode(idesc->id_number);
5128c424e8eSdownsj 	if (flag == 1) {
5138c424e8eSdownsj 		pwarn("%s %s", type,
5148c424e8eSdownsj 		    (dp->e2di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
5158c424e8eSdownsj 		pinode(idesc->id_number);
5168c424e8eSdownsj 	}
5178c424e8eSdownsj 	if (preen || reply("CLEAR") == 1) {
5188c424e8eSdownsj 		if (preen)
5198c424e8eSdownsj 			printf(" (CLEARED)\n");
5208c424e8eSdownsj 		n_files--;
5218c424e8eSdownsj 		(void)ckinode(dp, idesc);
5228c424e8eSdownsj 		clearinode(dp);
5238c424e8eSdownsj 		statemap[idesc->id_number] = USTATE;
5248c424e8eSdownsj 		inodirty();
5258c424e8eSdownsj 	}
5268c424e8eSdownsj }
5278c424e8eSdownsj 
5288c424e8eSdownsj int
findname(struct inodesc * idesc)5298809fabbSderaadt findname(struct inodesc *idesc)
5308c424e8eSdownsj {
5310190393fSart 	struct ext2fs_direct *dirp = idesc->id_dirp;
5320190393fSart 	u_int16_t namlen = dirp->e2d_namlen;
5338c424e8eSdownsj 
53460a51e06Spelikan 	if (letoh32(dirp->e2d_ino) != idesc->id_parent)
5358c424e8eSdownsj 		return (KEEPON);
5360190393fSart 	memcpy(idesc->id_name, dirp->e2d_name, (size_t)namlen);
5370190393fSart 	idesc->id_name[namlen] = '\0';
5388c424e8eSdownsj 	return (STOP|FOUND);
5398c424e8eSdownsj }
5408c424e8eSdownsj 
5418c424e8eSdownsj int
findino(struct inodesc * idesc)5428809fabbSderaadt findino(struct inodesc *idesc)
5438c424e8eSdownsj {
5440190393fSart 	struct ext2fs_direct *dirp = idesc->id_dirp;
54560a51e06Spelikan 	u_int32_t ino = letoh32(dirp->e2d_ino);
5468c424e8eSdownsj 
5470190393fSart 	if (ino == 0)
5488c424e8eSdownsj 		return (KEEPON);
5498c424e8eSdownsj 	if (strcmp(dirp->e2d_name, idesc->id_name) == 0 &&
5500190393fSart 	    (ino == EXT2_ROOTINO || ino >= EXT2_FIRSTINO)
5510190393fSart 		&& ino <= maxino) {
5520190393fSart 		idesc->id_parent = ino;
5538c424e8eSdownsj 		return (STOP|FOUND);
5548c424e8eSdownsj 	}
5558c424e8eSdownsj 	return (KEEPON);
5568c424e8eSdownsj }
5578c424e8eSdownsj 
5588c424e8eSdownsj void
pinode(ino_t ino)5598809fabbSderaadt pinode(ino_t ino)
5608c424e8eSdownsj {
5610190393fSart 	struct ext2fs_dinode *dp;
562dee31871Smillert 	const char *p;
5638c424e8eSdownsj 	time_t t;
564d23196ecSpedro 	u_int32_t uid;
5658c424e8eSdownsj 
5663b92bd08Sderaadt 	printf(" I=%llu ", (unsigned long long)ino);
5678c424e8eSdownsj 	if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino)
5688c424e8eSdownsj 		return;
5698c424e8eSdownsj 	dp = ginode(ino);
5708c424e8eSdownsj 	printf(" OWNER=");
57160a51e06Spelikan 	uid = letoh16(dp->e2di_uid_low) | (letoh16(dp->e2di_uid_high) << 16);
5728c424e8eSdownsj #ifndef SMALL
573dee31871Smillert 	if ((p = user_from_uid(uid, 1)) != NULL)
574dee31871Smillert 		printf("%s ", p);
5758c424e8eSdownsj 	else
5768c424e8eSdownsj #endif
577dee31871Smillert 		printf("%u ", uid);
57860a51e06Spelikan 	printf("MODE=%o\n", letoh16(dp->e2di_mode));
5798c424e8eSdownsj 	if (preen)
5808c424e8eSdownsj 		printf("%s: ", cdevname());
581935730fbSniallo 	printf("SIZE=%llu ", (long long)inosize(dp));
58260a51e06Spelikan 	t = (time_t) letoh32(dp->e2di_mtime);
5838c424e8eSdownsj 	p = ctime(&t);
584*a7b9eedcSflorian 	if (p)
5858c424e8eSdownsj 		printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
586*a7b9eedcSflorian 	else
587*a7b9eedcSflorian 		printf("MTIME=%lld ", t);
5888c424e8eSdownsj }
5898c424e8eSdownsj 
5908c424e8eSdownsj void
blkerror(ino_t ino,char * type,daddr32_t blk)591b6d2e2d5Sderaadt blkerror(ino_t ino, char *type, daddr32_t blk)
5928c424e8eSdownsj {
5938c424e8eSdownsj 
5943b92bd08Sderaadt 	pfatal("%d %s I=%llu", blk, type, (unsigned long long)ino);
5958c424e8eSdownsj 	printf("\n");
5968c424e8eSdownsj 	switch (statemap[ino]) {
5978c424e8eSdownsj 
5988c424e8eSdownsj 	case FSTATE:
5998c424e8eSdownsj 		statemap[ino] = FCLEAR;
6008c424e8eSdownsj 		return;
6018c424e8eSdownsj 
6028c424e8eSdownsj 	case DSTATE:
6038c424e8eSdownsj 		statemap[ino] = DCLEAR;
6048c424e8eSdownsj 		return;
6058c424e8eSdownsj 
6068c424e8eSdownsj 	case FCLEAR:
6078c424e8eSdownsj 	case DCLEAR:
6088c424e8eSdownsj 		return;
6098c424e8eSdownsj 
6108c424e8eSdownsj 	default:
6118c424e8eSdownsj 		errexit("BAD STATE %d TO BLKERR\n", statemap[ino]);
6128c424e8eSdownsj 		/* NOTREACHED */
6138c424e8eSdownsj 	}
6148c424e8eSdownsj }
6158c424e8eSdownsj 
6168c424e8eSdownsj /*
6178c424e8eSdownsj  * allocate an unused inode
6188c424e8eSdownsj  */
6198c424e8eSdownsj ino_t
allocino(ino_t request,int type)6208809fabbSderaadt allocino(ino_t request, int type)
6218c424e8eSdownsj {
6220190393fSart 	ino_t ino;
6230190393fSart 	struct ext2fs_dinode *dp;
6248c424e8eSdownsj 	time_t t;
6258c424e8eSdownsj 
6268c424e8eSdownsj 	if (request == 0)
6278c424e8eSdownsj 		request = EXT2_ROOTINO;
6288c424e8eSdownsj 	else if (statemap[request] != USTATE)
6298c424e8eSdownsj 		return (0);
6308c424e8eSdownsj 	for (ino = request; ino < maxino; ino++) {
6318c424e8eSdownsj 		if ((ino > EXT2_ROOTINO) && (ino < EXT2_FIRSTINO))
6328c424e8eSdownsj 			continue;
6338c424e8eSdownsj 		if (statemap[ino] == USTATE)
6348c424e8eSdownsj 			break;
6358c424e8eSdownsj 	}
6368c424e8eSdownsj 	if (ino == maxino)
6378c424e8eSdownsj 		return (0);
6388c424e8eSdownsj 	switch (type & IFMT) {
6398c424e8eSdownsj 	case IFDIR:
6408c424e8eSdownsj 		statemap[ino] = DSTATE;
6418c424e8eSdownsj 		break;
6428c424e8eSdownsj 	case IFREG:
6438c424e8eSdownsj 	case IFLNK:
6448c424e8eSdownsj 		statemap[ino] = FSTATE;
6458c424e8eSdownsj 		break;
6468c424e8eSdownsj 	default:
6478c424e8eSdownsj 		return (0);
6488c424e8eSdownsj 	}
6498c424e8eSdownsj 	dp = ginode(ino);
65060a51e06Spelikan 	dp->e2di_blocks[0] = htole32(allocblk());
6518c424e8eSdownsj 	if (dp->e2di_blocks[0] == 0) {
6528c424e8eSdownsj 		statemap[ino] = USTATE;
6538c424e8eSdownsj 		return (0);
6548c424e8eSdownsj 	}
65560a51e06Spelikan 	dp->e2di_mode = htole16(type);
6568c424e8eSdownsj 	(void)time(&t);
65760a51e06Spelikan 	dp->e2di_atime = (u_int32_t)htole32(t);
6588c424e8eSdownsj 	dp->e2di_mtime = dp->e2di_ctime = dp->e2di_atime;
6598c424e8eSdownsj 	dp->e2di_dtime = 0;
660935730fbSniallo 	inossize(dp, sblock.e2fs_bsize);
66160a51e06Spelikan 	dp->e2di_nblock = htole32(btodb(sblock.e2fs_bsize));
6628c424e8eSdownsj 	n_files++;
6638c424e8eSdownsj 	inodirty();
6640190393fSart 	typemap[ino] = E2IFTODT(type);
6658c424e8eSdownsj 	return (ino);
6668c424e8eSdownsj }
6678c424e8eSdownsj 
6688c424e8eSdownsj /*
6698c424e8eSdownsj  * deallocate an inode
6708c424e8eSdownsj  */
6718c424e8eSdownsj void
freeino(ino_t ino)6728809fabbSderaadt freeino(ino_t ino)
6738c424e8eSdownsj {
6748c424e8eSdownsj 	struct inodesc idesc;
6758c424e8eSdownsj 	struct ext2fs_dinode *dp;
6768c424e8eSdownsj 
6778c424e8eSdownsj 	memset(&idesc, 0, sizeof(struct inodesc));
6788c424e8eSdownsj 	idesc.id_type = ADDR;
6798c424e8eSdownsj 	idesc.id_func = pass4check;
6808c424e8eSdownsj 	idesc.id_number = ino;
6818c424e8eSdownsj 	dp = ginode(ino);
6828c424e8eSdownsj 	(void)ckinode(dp, &idesc);
6838c424e8eSdownsj 	clearinode(dp);
6848c424e8eSdownsj 	inodirty();
6858c424e8eSdownsj 	statemap[ino] = USTATE;
6868c424e8eSdownsj 	n_files--;
6878c424e8eSdownsj }
688