xref: /openbsd-src/sbin/fsck_ffs/inode.c (revision a7b9eedcb4c1ab985f28fc923822c24bc6417908)
1*a7b9eedcSflorian /*	$OpenBSD: inode.c,v 1.52 2024/05/09 08:35:40 florian Exp $	*/
287304b87Stholo /*	$NetBSD: inode.c,v 1.23 1996/10/11 20:15:47 thorpej Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1986, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
161ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
3378eb0b7eSderaadt #include <sys/param.h>	/* setbit btodb */
34df930be7Sderaadt #include <sys/time.h>
35df930be7Sderaadt #include <ufs/ufs/dinode.h>
36df930be7Sderaadt #include <ufs/ufs/dir.h>
37df930be7Sderaadt #include <ufs/ffs/fs.h>
38df930be7Sderaadt #ifndef SMALL
39df930be7Sderaadt #include <pwd.h>
40df930be7Sderaadt #endif
41df930be7Sderaadt #include <stdio.h>
42df930be7Sderaadt #include <stdlib.h>
43df930be7Sderaadt #include <string.h>
44ffb42f9cSbluhm #include <unistd.h>
45b9fc9a72Sderaadt #include <limits.h>
46df930be7Sderaadt 
47df930be7Sderaadt #include "fsck.h"
4887304b87Stholo #include "fsutil.h"
49df930be7Sderaadt #include "extern.h"
50df930be7Sderaadt 
51b9fc9a72Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
52b9fc9a72Sderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
53b9fc9a72Sderaadt 
54df930be7Sderaadt static ino_t startinum;
55df930be7Sderaadt 
56887c4461Sotto static int iblock(struct inodesc *, long, off_t);
57df930be7Sderaadt 
58df930be7Sderaadt int
ckinode(union dinode * dp,struct inodesc * idesc)592fffe0e0Smillert ckinode(union dinode *dp, struct inodesc *idesc)
60df930be7Sderaadt {
612fffe0e0Smillert 	long ret, ndb, offset;
622fffe0e0Smillert 	union dinode dino;
63887c4461Sotto 	off_t sizepb, remsize;
64df930be7Sderaadt 	mode_t mode;
652fffe0e0Smillert 	int i;
66b9fc9a72Sderaadt 	char pathbuf[PATH_MAX + 1];
67df930be7Sderaadt 
68df930be7Sderaadt 	if (idesc->id_fix != IGNORE)
69df930be7Sderaadt 		idesc->id_fix = DONTKNOW;
70df930be7Sderaadt 	idesc->id_entryno = 0;
712fffe0e0Smillert 	idesc->id_filesize = DIP(dp, di_size);
722fffe0e0Smillert 	mode = DIP(dp, di_mode) & IFMT;
73df930be7Sderaadt 	if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
74da5362d5Sguenther 	    DIP(dp, di_size) < sblock.fs_maxsymlinklen))
75df930be7Sderaadt 		return (KEEPON);
762fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC)
772fffe0e0Smillert 		dino.dp1 = dp->dp1;
782fffe0e0Smillert 	else
792fffe0e0Smillert 		dino.dp2 = dp->dp2;
802fffe0e0Smillert 	ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize);
812fffe0e0Smillert 	for (i = 0; i < NDADDR; i++) {
822fffe0e0Smillert 		if (--ndb == 0 && (offset = blkoff(&sblock,
832fffe0e0Smillert 		    DIP(&dino, di_size))) != 0)
84df930be7Sderaadt 			idesc->id_numfrags =
85df930be7Sderaadt 				numfrags(&sblock, fragroundup(&sblock, offset));
86df930be7Sderaadt 		else
87df930be7Sderaadt 			idesc->id_numfrags = sblock.fs_frag;
882fffe0e0Smillert 		if (DIP(&dino, di_db[i]) == 0) {
897a72ca8cStholo 			if (idesc->id_type == DATA && ndb >= 0) {
907a72ca8cStholo 				/* An empty block in a directory XXX */
9159b30be9Sderaadt 				getpathname(pathbuf, sizeof pathbuf,
9259b30be9Sderaadt 				    idesc->id_number, idesc->id_number);
937a72ca8cStholo 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
947a72ca8cStholo 				    pathbuf);
957a72ca8cStholo 				if (reply("ADJUST LENGTH") == 1) {
967a72ca8cStholo 					dp = ginode(idesc->id_number);
972fffe0e0Smillert 					DIP_SET(dp, di_size,
982fffe0e0Smillert 					    i * sblock.fs_bsize);
997a72ca8cStholo 					printf(
1007a72ca8cStholo 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
1017a72ca8cStholo 					rerun = 1;
1027a72ca8cStholo 					inodirty();
1037a72ca8cStholo 				}
1047a72ca8cStholo 			}
105df930be7Sderaadt 			continue;
1067a72ca8cStholo 		}
1072fffe0e0Smillert 		idesc->id_blkno = DIP(&dino, di_db[i]);
108df930be7Sderaadt 		if (idesc->id_type == ADDR)
109df930be7Sderaadt 			ret = (*idesc->id_func)(idesc);
110df930be7Sderaadt 		else
111df930be7Sderaadt 			ret = dirscan(idesc);
112df930be7Sderaadt 		if (ret & STOP)
113df930be7Sderaadt 			return (ret);
114df930be7Sderaadt 	}
115df930be7Sderaadt 	idesc->id_numfrags = sblock.fs_frag;
1162fffe0e0Smillert 	remsize = DIP(&dino, di_size) - sblock.fs_bsize * NDADDR;
117df930be7Sderaadt 	sizepb = sblock.fs_bsize;
1182fffe0e0Smillert 	for (i = 0; i < NIADDR; i++) {
1192fffe0e0Smillert 		if (DIP(&dino, di_ib[i])) {
1202fffe0e0Smillert 			idesc->id_blkno = DIP(&dino, di_ib[i]);
1212fffe0e0Smillert 			ret = iblock(idesc, i + 1, remsize);
122df930be7Sderaadt 			if (ret & STOP)
123df930be7Sderaadt 				return (ret);
1247a72ca8cStholo 		} else {
1257a72ca8cStholo 			if (idesc->id_type == DATA && remsize > 0) {
1267a72ca8cStholo 				/* An empty block in a directory XXX */
12759b30be9Sderaadt 				getpathname(pathbuf, sizeof pathbuf,
12859b30be9Sderaadt 				    idesc->id_number, idesc->id_number);
1297a72ca8cStholo 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
1307a72ca8cStholo 				    pathbuf);
1317a72ca8cStholo 				if (reply("ADJUST LENGTH") == 1) {
1327a72ca8cStholo 					dp = ginode(idesc->id_number);
1332fffe0e0Smillert 					DIP_SET(dp, di_size,
1342fffe0e0Smillert 					     DIP(dp, di_size) - remsize);
1357a72ca8cStholo 					remsize = 0;
1367a72ca8cStholo 					printf(
1377a72ca8cStholo 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
1387a72ca8cStholo 					rerun = 1;
1397a72ca8cStholo 					inodirty();
1407a72ca8cStholo 					break;
1417a72ca8cStholo 				}
1427a72ca8cStholo 			}
143df930be7Sderaadt 		}
144df930be7Sderaadt 		sizepb *= NINDIR(&sblock);
145df930be7Sderaadt 		remsize -= sizepb;
146df930be7Sderaadt 	}
147df930be7Sderaadt 	return (KEEPON);
148df930be7Sderaadt }
149df930be7Sderaadt 
15087304b87Stholo static int
iblock(struct inodesc * idesc,long ilevel,off_t isize)151887c4461Sotto iblock(struct inodesc *idesc, long ilevel, off_t isize)
152df930be7Sderaadt {
153e073c79dSmpech 	struct bufarea *bp;
154c72b5b24Smillert 	int i, n, (*func)(struct inodesc *), nif;
155887c4461Sotto 	off_t sizepb;
156df930be7Sderaadt 	char buf[BUFSIZ];
157b9fc9a72Sderaadt 	char pathbuf[PATH_MAX + 1];
1582fffe0e0Smillert 	union dinode *dp;
159df930be7Sderaadt 
160df930be7Sderaadt 	if (idesc->id_type == ADDR) {
161df930be7Sderaadt 		func = idesc->id_func;
162df930be7Sderaadt 		if (((n = (*func)(idesc)) & KEEPON) == 0)
163df930be7Sderaadt 			return (n);
164df930be7Sderaadt 	} else
165df930be7Sderaadt 		func = dirscan;
1669b09412eSotto 	if (isize < 0 || chkrange(idesc->id_blkno, idesc->id_numfrags))
167df930be7Sderaadt 		return (SKIP);
168df930be7Sderaadt 	bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
169df930be7Sderaadt 	ilevel--;
170df930be7Sderaadt 	for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
171df930be7Sderaadt 		sizepb *= NINDIR(&sblock);
172887c4461Sotto 	if (howmany(isize, sizepb) > NINDIR(&sblock))
173df930be7Sderaadt 		nif = NINDIR(&sblock);
174fc1d8945Sderaadt 	else
175fc1d8945Sderaadt 		nif = howmany(isize, sizepb);
176df930be7Sderaadt 	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
1772fffe0e0Smillert 		for (i = nif; i < NINDIR(&sblock); i++) {
1782fffe0e0Smillert 			if (IBLK(bp, i) == 0)
179df930be7Sderaadt 				continue;
180dcab0d16Sderaadt 			(void)snprintf(buf, sizeof buf,
1813b92bd08Sderaadt 			    "PARTIALLY TRUNCATED INODE I=%llu",
1823b92bd08Sderaadt 			    (unsigned long long)idesc->id_number);
183887c4461Sotto 			if (preen)
184887c4461Sotto 				pfatal("%s", buf);
185887c4461Sotto 			else if (dofix(idesc, buf)) {
1862fffe0e0Smillert 				IBLK_SET(bp, i, 0);
187df930be7Sderaadt 				dirty(bp);
188df930be7Sderaadt 			}
189df930be7Sderaadt 		}
190df930be7Sderaadt 		flush(fswritefd, bp);
191df930be7Sderaadt 	}
1922fffe0e0Smillert 	for (i = 0; i < nif; i++) {
1932fffe0e0Smillert 		if (IBLK(bp, i)) {
1942fffe0e0Smillert 			idesc->id_blkno = IBLK(bp, i);
195df930be7Sderaadt 			if (ilevel == 0)
196df930be7Sderaadt 				n = (*func)(idesc);
197df930be7Sderaadt 			else
198df930be7Sderaadt 				n = iblock(idesc, ilevel, isize);
199df930be7Sderaadt 			if (n & STOP) {
200df930be7Sderaadt 				bp->b_flags &= ~B_INUSE;
201df930be7Sderaadt 				return (n);
202df930be7Sderaadt 			}
2037a72ca8cStholo 		} else {
2047a72ca8cStholo 			if (idesc->id_type == DATA && isize > 0) {
2057a72ca8cStholo 				/* An empty block in a directory XXX */
20659b30be9Sderaadt 				getpathname(pathbuf, sizeof pathbuf,
20759b30be9Sderaadt 				    idesc->id_number, idesc->id_number);
2087a72ca8cStholo 				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
2097a72ca8cStholo 				    pathbuf);
2107a72ca8cStholo 				if (reply("ADJUST LENGTH") == 1) {
2117a72ca8cStholo 					dp = ginode(idesc->id_number);
2122fffe0e0Smillert 					DIP_SET(dp, di_size,
2132fffe0e0Smillert 					    DIP(dp, di_size) - isize);
2147a72ca8cStholo 					isize = 0;
2157a72ca8cStholo 					printf(
2167a72ca8cStholo 					    "YOU MUST RERUN FSCK AFTERWARDS\n");
2177a72ca8cStholo 					rerun = 1;
2187a72ca8cStholo 					inodirty();
2197a72ca8cStholo 					bp->b_flags &= ~B_INUSE;
2207a72ca8cStholo 					return(STOP);
2217a72ca8cStholo 				}
2227a72ca8cStholo 			}
223df930be7Sderaadt 		}
224df930be7Sderaadt 		isize -= sizepb;
225df930be7Sderaadt 	}
226df930be7Sderaadt 	bp->b_flags &= ~B_INUSE;
227df930be7Sderaadt 	return (KEEPON);
228df930be7Sderaadt }
229df930be7Sderaadt 
230df930be7Sderaadt /*
231df930be7Sderaadt  * Check that a block in a legal block number.
232df930be7Sderaadt  * Return 0 if in range, 1 if out of range.
233df930be7Sderaadt  */
234df930be7Sderaadt int
chkrange(daddr_t blk,int cnt)2351abdbfdeSderaadt chkrange(daddr_t blk, int cnt)
236df930be7Sderaadt {
237e073c79dSmpech 	int c;
238df930be7Sderaadt 
239887c4461Sotto 	if (cnt <= 0 || blk <= 0 || blk > maxfsblock ||
240887c4461Sotto 	    cnt - 1 > maxfsblock - blk)
241df930be7Sderaadt 		return (1);
242887c4461Sotto 	if (cnt > sblock.fs_frag ||
243887c4461Sotto 	    fragnum(&sblock, blk) + cnt > sblock.fs_frag) {
244887c4461Sotto 		if (debug)
245a0a52948Sderaadt 			printf("bad size: blk %lld, offset %lld, size %d\n",
246f138002dSkrw 			    (long long)blk, (long long)fragnum(&sblock, blk),
247bb4f4faeSkrw 			    cnt);
248887c4461Sotto 		return (1);
249887c4461Sotto 	}
250df930be7Sderaadt 	c = dtog(&sblock, blk);
251df930be7Sderaadt 	if (blk < cgdmin(&sblock, c)) {
252df930be7Sderaadt 		if ((blk + cnt) > cgsblock(&sblock, c)) {
253df930be7Sderaadt 			if (debug) {
2544f17ce5eSotto 				printf("blk %lld < cgdmin %lld;",
255f138002dSkrw 				    (long long)blk,
256f138002dSkrw 				    (long long)cgdmin(&sblock, c));
2574f17ce5eSotto 				printf(" blk + cnt %lld > cgsbase %lld\n",
258bb4f4faeSkrw 				    (long long)(blk + cnt),
259f138002dSkrw 				    (long long)cgsblock(&sblock, c));
260df930be7Sderaadt 			}
261df930be7Sderaadt 			return (1);
262df930be7Sderaadt 		}
263df930be7Sderaadt 	} else {
264df930be7Sderaadt 		if ((blk + cnt) > cgbase(&sblock, c+1)) {
265df930be7Sderaadt 			if (debug)  {
2664f17ce5eSotto 				printf("blk %lld >= cgdmin %lld;",
267f138002dSkrw 				    (long long)blk,
268f138002dSkrw 				    (long long)cgdmin(&sblock, c));
2694f17ce5eSotto 				printf(" blk + cnt %lld > sblock.fs_fpg %d\n",
270bb4f4faeSkrw 				    (long long)(blk+cnt), sblock.fs_fpg);
271df930be7Sderaadt 			}
272df930be7Sderaadt 			return (1);
273df930be7Sderaadt 		}
274df930be7Sderaadt 	}
275df930be7Sderaadt 	return (0);
276df930be7Sderaadt }
277df930be7Sderaadt 
278df930be7Sderaadt /*
279df930be7Sderaadt  * General purpose interface for reading inodes.
280df930be7Sderaadt  */
2812fffe0e0Smillert union dinode *
ginode(ino_t inumber)28260d6b16fSgluk ginode(ino_t inumber)
283df930be7Sderaadt {
2841abdbfdeSderaadt 	daddr_t iblk;
285df930be7Sderaadt 
286df930be7Sderaadt 	if (inumber < ROOTINO || inumber > maxino)
2873b92bd08Sderaadt 		errexit("bad inode number %llu to ginode\n",
2883b92bd08Sderaadt 		    (unsigned long long)inumber);
289df930be7Sderaadt 	if (startinum == 0 ||
290df930be7Sderaadt 	    inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
291df930be7Sderaadt 		iblk = ino_to_fsba(&sblock, inumber);
292df930be7Sderaadt 		if (pbp != 0)
293df930be7Sderaadt 			pbp->b_flags &= ~B_INUSE;
294df930be7Sderaadt 		pbp = getdatablk(iblk, sblock.fs_bsize);
295df930be7Sderaadt 		startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
296df930be7Sderaadt 	}
2972fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC)
2982fffe0e0Smillert 		return ((union dinode *)
2992fffe0e0Smillert 		    &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
3002fffe0e0Smillert 	return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]);
301df930be7Sderaadt }
302df930be7Sderaadt 
303df930be7Sderaadt /*
304df930be7Sderaadt  * Special purpose version of ginode used to optimize first pass
305df930be7Sderaadt  * over all the inodes in numerical order.
306df930be7Sderaadt  */
307df930be7Sderaadt ino_t nextino, lastinum;
308df930be7Sderaadt long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
3092fffe0e0Smillert static caddr_t inodebuf;
310df930be7Sderaadt 
3112fffe0e0Smillert union dinode *
getnextinode(ino_t inumber)31260d6b16fSgluk getnextinode(ino_t inumber)
313df930be7Sderaadt {
314df930be7Sderaadt 	long size;
3151abdbfdeSderaadt 	daddr_t dblk;
3162fffe0e0Smillert 	union dinode *dp;
3172fffe0e0Smillert 	static caddr_t nextinop;
318df930be7Sderaadt 
319df930be7Sderaadt 	if (inumber != nextino++ || inumber > maxino)
3203b92bd08Sderaadt 		errexit("bad inode number %llu to nextinode %llu\n",
3213b92bd08Sderaadt 		    (unsigned long long)inumber,
3223b92bd08Sderaadt 		    (unsigned long long)nextino);
323df930be7Sderaadt 	if (inumber >= lastinum) {
324df930be7Sderaadt 		readcnt++;
325df930be7Sderaadt 		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
326df930be7Sderaadt 		if (readcnt % readpercg == 0) {
327df930be7Sderaadt 			size = partialsize;
328df930be7Sderaadt 			lastinum += partialcnt;
329df930be7Sderaadt 		} else {
330df930be7Sderaadt 			size = inobufsize;
331df930be7Sderaadt 			lastinum += fullcnt;
332df930be7Sderaadt 		}
3332fffe0e0Smillert 		(void)bread(fsreadfd, inodebuf, dblk, size);
3342fffe0e0Smillert 		nextinop = inodebuf;
335df930be7Sderaadt 	}
3362fffe0e0Smillert 	dp = (union dinode *)nextinop;
3372fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC)
3382fffe0e0Smillert 		nextinop += sizeof(struct ufs1_dinode);
3392fffe0e0Smillert 	else
3402fffe0e0Smillert 		nextinop += sizeof(struct ufs2_dinode);
3412fffe0e0Smillert 	return (dp);
342df930be7Sderaadt }
343df930be7Sderaadt 
344df930be7Sderaadt void
setinodebuf(ino_t inum)3452fffe0e0Smillert setinodebuf(ino_t inum)
346df930be7Sderaadt {
347df930be7Sderaadt 
348df930be7Sderaadt 	startinum = 0;
3492fffe0e0Smillert 	nextino = inum;
3502fffe0e0Smillert 	lastinum = inum;
351df930be7Sderaadt 	readcnt = 0;
3522fffe0e0Smillert 	if (inodebuf != NULL)
3532fffe0e0Smillert 		return;
354df930be7Sderaadt 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
3552fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC)
356f46297fdStedu 		fullcnt = inobufsize / sizeof(struct ufs1_dinode);
3572fffe0e0Smillert 	else
3582fffe0e0Smillert 		fullcnt = inobufsize / sizeof(struct ufs2_dinode);
359df930be7Sderaadt 	readpercg = sblock.fs_ipg / fullcnt;
360df930be7Sderaadt 	partialcnt = sblock.fs_ipg % fullcnt;
3612fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC)
362f46297fdStedu 		partialsize = partialcnt * sizeof(struct ufs1_dinode);
3632fffe0e0Smillert 	else
3642fffe0e0Smillert 		partialsize = partialcnt * sizeof(struct ufs2_dinode);
365df930be7Sderaadt 	if (partialcnt != 0) {
366df930be7Sderaadt 		readpercg++;
367df930be7Sderaadt 	} else {
368df930be7Sderaadt 		partialcnt = fullcnt;
369df930be7Sderaadt 		partialsize = inobufsize;
370df930be7Sderaadt 	}
371df930be7Sderaadt 	if (inodebuf == NULL &&
3722062fcb1Sotto 	    (inodebuf = Malloc((unsigned)inobufsize)) == NULL)
373df930be7Sderaadt 		errexit("Cannot allocate space for inode buffer\n");
374df930be7Sderaadt }
375df930be7Sderaadt 
376df930be7Sderaadt void
freeinodebuf(void)37760d6b16fSgluk freeinodebuf(void)
378df930be7Sderaadt {
379df930be7Sderaadt 
38060d6b16fSgluk 	free(inodebuf);
381df930be7Sderaadt 	inodebuf = NULL;
382df930be7Sderaadt }
383df930be7Sderaadt 
384df930be7Sderaadt /*
385df930be7Sderaadt  * Routines to maintain information about directory inodes.
386df930be7Sderaadt  * This is built during the first pass and used during the
387df930be7Sderaadt  * second and third passes.
388df930be7Sderaadt  *
389df930be7Sderaadt  * Enter inodes into the cache.
390df930be7Sderaadt  */
391df930be7Sderaadt void
cacheino(union dinode * dp,ino_t inumber)3922fffe0e0Smillert cacheino(union dinode *dp, ino_t inumber)
393df930be7Sderaadt {
394e073c79dSmpech 	struct inoinfo *inp;
395891e039fSderaadt 	struct inoinfo **inpp, **newinpsort;
396df930be7Sderaadt 	unsigned int blks;
397891e039fSderaadt 	long newlistmax;
3982fffe0e0Smillert 	int i;
399df930be7Sderaadt 
4002fffe0e0Smillert 	blks = howmany(DIP(dp, di_size), sblock.fs_bsize);
401df930be7Sderaadt 	if (blks > NDADDR)
402df930be7Sderaadt 		blks = NDADDR + NIADDR;
4032062fcb1Sotto 	inp = Malloc(sizeof(*inp) + (blks ? blks - 1 : 0) * sizeof(daddr_t));
404df930be7Sderaadt 	if (inp == NULL)
405971b3859Stedu 		errexit("cannot allocate memory for inode cache\n");
406df930be7Sderaadt 	inpp = &inphead[inumber % numdirs];
407df930be7Sderaadt 	inp->i_nexthash = *inpp;
408df930be7Sderaadt 	*inpp = inp;
409838c4739Sotto 	inp->i_child = inp->i_sibling = 0;
410df930be7Sderaadt 	if (inumber == ROOTINO)
411df930be7Sderaadt 		inp->i_parent = ROOTINO;
412df930be7Sderaadt 	else
41360d6b16fSgluk 		inp->i_parent = 0;
41460d6b16fSgluk 	inp->i_dotdot = 0;
415df930be7Sderaadt 	inp->i_number = inumber;
4162fffe0e0Smillert 	inp->i_isize = DIP(dp, di_size);
4172fffe0e0Smillert 	inp->i_numblks = blks;
4182fffe0e0Smillert 	for (i = 0; i < (blks < NDADDR ? blks : NDADDR); i++)
4192fffe0e0Smillert 		inp->i_blks[i] = DIP(dp, di_db[i]);
4202fffe0e0Smillert 	if (blks > NDADDR)
4212fffe0e0Smillert 		for (i = 0; i < NIADDR; i++)
4222fffe0e0Smillert 			inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]);
423df930be7Sderaadt 	if (inplast == listmax) {
424891e039fSderaadt 		newlistmax = listmax + 100;
4252062fcb1Sotto 		newinpsort = Reallocarray(inpsort,
426ae8fed2dSderaadt 		    (unsigned)newlistmax, sizeof(struct inoinfo *));
427891e039fSderaadt 		if (newinpsort == NULL)
4282062fcb1Sotto 			errexit("cannot increase directory list\n");
429891e039fSderaadt 		inpsort = newinpsort;
430891e039fSderaadt 		listmax = newlistmax;
431df930be7Sderaadt 	}
432df930be7Sderaadt 	inpsort[inplast++] = inp;
433df930be7Sderaadt }
434df930be7Sderaadt 
435df930be7Sderaadt /*
436df930be7Sderaadt  * Look up an inode cache structure.
437df930be7Sderaadt  */
438df930be7Sderaadt struct inoinfo *
getinoinfo(ino_t inumber)43960d6b16fSgluk getinoinfo(ino_t inumber)
440df930be7Sderaadt {
441e073c79dSmpech 	struct inoinfo *inp;
442df930be7Sderaadt 
443df930be7Sderaadt 	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
444df930be7Sderaadt 		if (inp->i_number != inumber)
445df930be7Sderaadt 			continue;
446df930be7Sderaadt 		return (inp);
447df930be7Sderaadt 	}
4483b92bd08Sderaadt 	errexit("cannot find inode %llu\n", (unsigned long long)inumber);
44989203baaSkstailey 	return (NULL);
450df930be7Sderaadt }
451df930be7Sderaadt 
452df930be7Sderaadt /*
453df930be7Sderaadt  * Clean up all the inode cache structure.
454df930be7Sderaadt  */
455df930be7Sderaadt void
inocleanup(void)45660d6b16fSgluk inocleanup(void)
457df930be7Sderaadt {
458e073c79dSmpech 	struct inoinfo **inpp;
459df930be7Sderaadt 
460df930be7Sderaadt 	if (inphead == NULL)
461df930be7Sderaadt 		return;
462df930be7Sderaadt 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
46360d6b16fSgluk 		free(*inpp);
46460d6b16fSgluk 	free(inphead);
46560d6b16fSgluk 	free(inpsort);
466df930be7Sderaadt 	inphead = inpsort = NULL;
467df930be7Sderaadt }
468df930be7Sderaadt 
469df930be7Sderaadt void
inodirty(void)47060d6b16fSgluk inodirty(void)
471df930be7Sderaadt {
472df930be7Sderaadt 	dirty(pbp);
473df930be7Sderaadt }
474df930be7Sderaadt 
475df930be7Sderaadt void
clri(struct inodesc * idesc,char * type,int flag)47660d6b16fSgluk clri(struct inodesc *idesc, char *type, int flag)
477df930be7Sderaadt {
4782fffe0e0Smillert 	union dinode *dp;
479df930be7Sderaadt 
480df930be7Sderaadt 	dp = ginode(idesc->id_number);
481df930be7Sderaadt 	if (flag == 1) {
482df930be7Sderaadt 		pwarn("%s %s", type,
4832fffe0e0Smillert 		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
484df930be7Sderaadt 		pinode(idesc->id_number);
485df930be7Sderaadt 	}
486df930be7Sderaadt 	if (preen || reply("CLEAR") == 1) {
487df930be7Sderaadt 		if (preen)
488df930be7Sderaadt 			printf(" (CLEARED)\n");
489df930be7Sderaadt 		n_files--;
490df930be7Sderaadt 		(void)ckinode(dp, idesc);
491df930be7Sderaadt 		clearinode(dp);
4924aab0ea5Sotto 		SET_ISTATE(idesc->id_number, USTATE);
493df930be7Sderaadt 		inodirty();
494df930be7Sderaadt 	}
495df930be7Sderaadt }
496df930be7Sderaadt 
497df930be7Sderaadt int
findname(struct inodesc * idesc)49860d6b16fSgluk findname(struct inodesc *idesc)
499df930be7Sderaadt {
500e073c79dSmpech 	struct direct *dirp = idesc->id_dirp;
501df930be7Sderaadt 
502df930be7Sderaadt 	if (dirp->d_ino != idesc->id_parent)
503df930be7Sderaadt 		return (KEEPON);
504df930be7Sderaadt 	memcpy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
505df930be7Sderaadt 	return (STOP|FOUND);
506df930be7Sderaadt }
507df930be7Sderaadt 
508df930be7Sderaadt int
findino(struct inodesc * idesc)50960d6b16fSgluk findino(struct inodesc *idesc)
510df930be7Sderaadt {
511e073c79dSmpech 	struct direct *dirp = idesc->id_dirp;
512df930be7Sderaadt 
513df930be7Sderaadt 	if (dirp->d_ino == 0)
514df930be7Sderaadt 		return (KEEPON);
515df930be7Sderaadt 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
516df930be7Sderaadt 	    dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
517df930be7Sderaadt 		idesc->id_parent = dirp->d_ino;
518df930be7Sderaadt 		return (STOP|FOUND);
519df930be7Sderaadt 	}
520df930be7Sderaadt 	return (KEEPON);
521df930be7Sderaadt }
522df930be7Sderaadt 
523df930be7Sderaadt void
pinode(ino_t ino)52460d6b16fSgluk pinode(ino_t ino)
525df930be7Sderaadt {
5262fffe0e0Smillert 	union dinode *dp;
527dee31871Smillert 	const char *p;
52899dce0a4Sderaadt 	time_t t;
529df930be7Sderaadt 
5303b92bd08Sderaadt 	printf(" I=%llu ", (unsigned long long)ino);
531df930be7Sderaadt 	if (ino < ROOTINO || ino > maxino)
532df930be7Sderaadt 		return;
533df930be7Sderaadt 	dp = ginode(ino);
534df930be7Sderaadt 	printf(" OWNER=");
535df930be7Sderaadt #ifndef SMALL
536dee31871Smillert 	if ((p = user_from_uid(DIP(dp, di_uid), 1)) != NULL)
537dee31871Smillert 		printf("%s ", p);
538df930be7Sderaadt 	else
539df930be7Sderaadt #endif
5402fffe0e0Smillert 		printf("%u ", (unsigned)DIP(dp, di_uid));
5412fffe0e0Smillert 	printf("MODE=%o\n", DIP(dp, di_mode));
542df930be7Sderaadt 	if (preen)
54387304b87Stholo 		printf("%s: ", cdevname());
5442fffe0e0Smillert 	printf("SIZE=%llu ", (unsigned long long)DIP(dp, di_size));
5452fffe0e0Smillert 	t = DIP(dp, di_mtime);
54699dce0a4Sderaadt 	p = ctime(&t);
547*a7b9eedcSflorian 	if (p)
548df930be7Sderaadt 		printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
549*a7b9eedcSflorian 	else
550*a7b9eedcSflorian 		printf("MTIME=%lld ", t);
551df930be7Sderaadt }
552df930be7Sderaadt 
553df930be7Sderaadt void
blkerror(ino_t ino,char * type,daddr_t blk)5541abdbfdeSderaadt blkerror(ino_t ino, char *type, daddr_t blk)
555df930be7Sderaadt {
556df930be7Sderaadt 
5573b92bd08Sderaadt 	pfatal("%lld %s I=%llu", blk, type, (unsigned long long)ino);
558df930be7Sderaadt 	printf("\n");
5594aab0ea5Sotto 	switch (GET_ISTATE(ino)) {
560df930be7Sderaadt 
561df930be7Sderaadt 	case FSTATE:
5624aab0ea5Sotto 		SET_ISTATE(ino, FCLEAR);
563df930be7Sderaadt 		return;
564df930be7Sderaadt 
565df930be7Sderaadt 	case DSTATE:
5664aab0ea5Sotto 		SET_ISTATE(ino, DCLEAR);
567df930be7Sderaadt 		return;
568df930be7Sderaadt 
569df930be7Sderaadt 	case FCLEAR:
570df930be7Sderaadt 	case DCLEAR:
571df930be7Sderaadt 		return;
572df930be7Sderaadt 
573df930be7Sderaadt 	default:
5744aab0ea5Sotto 		errexit("BAD STATE %d TO BLKERR\n", GET_ISTATE(ino));
575df930be7Sderaadt 		/* NOTREACHED */
576df930be7Sderaadt 	}
577df930be7Sderaadt }
578df930be7Sderaadt 
579df930be7Sderaadt /*
580df930be7Sderaadt  * allocate an unused inode
581df930be7Sderaadt  */
582df930be7Sderaadt ino_t
allocino(ino_t request,int type)58360d6b16fSgluk allocino(ino_t request, int type)
584df930be7Sderaadt {
585e073c79dSmpech 	ino_t ino;
5862fffe0e0Smillert 	union dinode *dp;
5872062fcb1Sotto 	struct bufarea *cgbp;
5882062fcb1Sotto 	struct cg *cgp;
589767b3369Sart 	int cg;
5906ae81f76Sderaadt 	time_t t;
5914d91232bSotto 	struct inostat *info;
592df930be7Sderaadt 
593df930be7Sderaadt 	if (request == 0)
594df930be7Sderaadt 		request = ROOTINO;
5954aab0ea5Sotto 	else if (GET_ISTATE(request) != USTATE)
596df930be7Sderaadt 		return (0);
597df930be7Sderaadt 	for (ino = request; ino < maxino; ino++)
5984aab0ea5Sotto 		if (GET_ISTATE(ino) == USTATE)
599df930be7Sderaadt 			break;
600df930be7Sderaadt 	if (ino == maxino)
601df930be7Sderaadt 		return (0);
602767b3369Sart 	cg = ino_to_cg(&sblock, ino);
6034d91232bSotto 	/* If necessary, extend the inoinfo array. grow exponentially */
6044d91232bSotto 	if ((ino % sblock.fs_ipg) >= (uint64_t)inostathead[cg].il_numalloced) {
6054d91232bSotto 		unsigned long newalloced, i;
606b9fc9a72Sderaadt 		newalloced = MINIMUM(sblock.fs_ipg,
607b9fc9a72Sderaadt 			MAXIMUM(2 * inostathead[cg].il_numalloced, 10));
6082062fcb1Sotto 		info = Calloc(newalloced, sizeof(struct inostat));
6094d91232bSotto 		if (info == NULL) {
6106a0422b3Sguenther 			pwarn("cannot alloc %zu bytes to extend inoinfo\n",
6114d91232bSotto 				sizeof(struct inostat) * newalloced);
6124d91232bSotto 			return 0;
6134d91232bSotto 		}
6144d91232bSotto 		memmove(info, inostathead[cg].il_stat,
6154d91232bSotto 			inostathead[cg].il_numalloced * sizeof(*info));
6164d91232bSotto 		for (i = inostathead[cg].il_numalloced; i < newalloced; i++) {
6174d91232bSotto 			info[i].ino_state = USTATE;
6184d91232bSotto 		}
6194d91232bSotto 		if (inostathead[cg].il_numalloced)
6204d91232bSotto 			free(inostathead[cg].il_stat);
6214d91232bSotto 		inostathead[cg].il_stat = info;
6224d91232bSotto 		inostathead[cg].il_numalloced = newalloced;
6234d91232bSotto 		info = inoinfo(ino);
6244d91232bSotto 	}
6252062fcb1Sotto 	cgbp = cglookup(cg);
6262062fcb1Sotto 	cgp = cgbp->b_un.b_cg;
627767b3369Sart 	if (!cg_chkmagic(cgp))
628767b3369Sart 		pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
629767b3369Sart 	setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
630767b3369Sart 	cgp->cg_cs.cs_nifree--;
631767b3369Sart 
632df930be7Sderaadt 	switch (type & IFMT) {
633df930be7Sderaadt 	case IFDIR:
6344aab0ea5Sotto 		SET_ISTATE(ino, DSTATE);
635767b3369Sart 		cgp->cg_cs.cs_ndir++;
636df930be7Sderaadt 		break;
637df930be7Sderaadt 	case IFREG:
638df930be7Sderaadt 	case IFLNK:
6394aab0ea5Sotto 		SET_ISTATE(ino, FSTATE);
640df930be7Sderaadt 		break;
641df930be7Sderaadt 	default:
642df930be7Sderaadt 		return (0);
643df930be7Sderaadt 	}
6442062fcb1Sotto 	dirty(cgbp);
645df930be7Sderaadt 	dp = ginode(ino);
6462fffe0e0Smillert 	DIP_SET(dp, di_db[0],  allocblk(1));
6472fffe0e0Smillert 	if (DIP(dp, di_db[0]) == 0) {
6484aab0ea5Sotto 		SET_ISTATE(ino, USTATE);
649df930be7Sderaadt 		return (0);
650df930be7Sderaadt 	}
6512fffe0e0Smillert 	DIP_SET(dp, di_mode, type);
6522fffe0e0Smillert 	DIP_SET(dp, di_uid, geteuid());
6532fffe0e0Smillert 	DIP_SET(dp, di_gid, getegid());
6542fffe0e0Smillert 	DIP_SET(dp, di_flags, 0);
6556ae81f76Sderaadt 	(void)time(&t);
6562fffe0e0Smillert 	DIP_SET(dp, di_atime, t);
6572fffe0e0Smillert 	DIP_SET(dp, di_atimensec, 0);
6582fffe0e0Smillert 	DIP_SET(dp, di_mtime, t);
6592fffe0e0Smillert 	DIP_SET(dp, di_mtimensec, 0);
6602fffe0e0Smillert 	DIP_SET(dp, di_ctime, t);
6612fffe0e0Smillert 	DIP_SET(dp, di_ctimensec, 0);
6622fffe0e0Smillert 	DIP_SET(dp, di_size, sblock.fs_fsize);
6632fffe0e0Smillert 	DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
664df930be7Sderaadt 	n_files++;
665df930be7Sderaadt 	inodirty();
6664aab0ea5Sotto 	SET_ITYPE(ino, IFTODT(type));
667df930be7Sderaadt 	return (ino);
668df930be7Sderaadt }
669df930be7Sderaadt 
670df930be7Sderaadt /*
671df930be7Sderaadt  * deallocate an inode
672df930be7Sderaadt  */
673df930be7Sderaadt void
freeino(ino_t ino)67460d6b16fSgluk freeino(ino_t ino)
675df930be7Sderaadt {
676df930be7Sderaadt 	struct inodesc idesc;
6772fffe0e0Smillert 	union dinode *dp;
678df930be7Sderaadt 
679df930be7Sderaadt 	memset(&idesc, 0, sizeof(struct inodesc));
680df930be7Sderaadt 	idesc.id_type = ADDR;
681df930be7Sderaadt 	idesc.id_func = pass4check;
682df930be7Sderaadt 	idesc.id_number = ino;
683df930be7Sderaadt 	dp = ginode(ino);
684df930be7Sderaadt 	(void)ckinode(dp, &idesc);
685df930be7Sderaadt 	clearinode(dp);
686df930be7Sderaadt 	inodirty();
6874aab0ea5Sotto 	SET_ISTATE(ino, USTATE);
688df930be7Sderaadt 	n_files--;
689df930be7Sderaadt }
690