11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#if 0
311558Srgrimes#ifndef lint
3223675Speterstatic const char sccsid[] = "@(#)inode.c	8.8 (Berkeley) 4/28/95";
33114589Sobrien#endif /* not lint */
3441477Sjulian#endif
35114589Sobrien#include <sys/cdefs.h>
36114589Sobrien__FBSDID("$FreeBSD$");
371558Srgrimes
381558Srgrimes#include <sys/param.h>
3998542Smckusick#include <sys/stdint.h>
4074556Smckusick#include <sys/sysctl.h>
4123675Speter
421558Srgrimes#include <ufs/ufs/dinode.h>
431558Srgrimes#include <ufs/ufs/dir.h>
441558Srgrimes#include <ufs/ffs/fs.h>
4523675Speter
4623675Speter#include <err.h>
471558Srgrimes#include <pwd.h>
481558Srgrimes#include <string.h>
49217769Smckusick#include <time.h>
5023675Speter
511558Srgrimes#include "fsck.h"
521558Srgrimes
531558Srgrimesstatic ino_t startinum;
541558Srgrimes
55247212Smckusickstatic int iblock(struct inodesc *, long ilevel, off_t isize, int type);
567585Sbde
577585Sbdeint
5898542Smckusickckinode(union dinode *dp, struct inodesc *idesc)
591558Srgrimes{
6098542Smckusick	off_t remsize, sizepb;
6198542Smckusick	int i, offset, ret;
6298542Smckusick	union dinode dino;
6398542Smckusick	ufs2_daddr_t ndb;
641558Srgrimes	mode_t mode;
6518808Sguido	char pathbuf[MAXPATHLEN + 1];
661558Srgrimes
671558Srgrimes	if (idesc->id_fix != IGNORE)
681558Srgrimes		idesc->id_fix = DONTKNOW;
6962668Smckusick	idesc->id_lbn = -1;
701558Srgrimes	idesc->id_entryno = 0;
7198542Smckusick	idesc->id_filesize = DIP(dp, di_size);
7298542Smckusick	mode = DIP(dp, di_mode) & IFMT;
731558Srgrimes	if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
7498542Smckusick	    DIP(dp, di_size) < (unsigned)sblock.fs_maxsymlinklen))
751558Srgrimes		return (KEEPON);
7698542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
7798542Smckusick		dino.dp1 = dp->dp1;
7898542Smckusick	else
7998542Smckusick		dino.dp2 = dp->dp2;
8098542Smckusick	ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize);
8198542Smckusick	for (i = 0; i < NDADDR; i++) {
8262668Smckusick		idesc->id_lbn++;
8398542Smckusick		if (--ndb == 0 &&
8498542Smckusick		    (offset = blkoff(&sblock, DIP(&dino, di_size))) != 0)
851558Srgrimes			idesc->id_numfrags =
861558Srgrimes				numfrags(&sblock, fragroundup(&sblock, offset));
871558Srgrimes		else
881558Srgrimes			idesc->id_numfrags = sblock.fs_frag;
8998542Smckusick		if (DIP(&dino, di_db[i]) == 0) {
9018808Sguido			if (idesc->id_type == DATA && ndb >= 0) {
9118808Sguido				/* An empty block in a directory XXX */
9218808Sguido				getpathname(pathbuf, idesc->id_number,
9318808Sguido						idesc->id_number);
94221110Sdes				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
9518808Sguido					pathbuf);
96221110Sdes				if (reply("ADJUST LENGTH") == 1) {
9718808Sguido					dp = ginode(idesc->id_number);
98221110Sdes					DIP_SET(dp, di_size,
99134589Sscottl					    i * sblock.fs_bsize);
10018808Sguido					printf(
10118808Sguido					    "YOU MUST RERUN FSCK AFTERWARDS\n");
10218808Sguido					rerun = 1;
103221110Sdes					inodirty();
104221110Sdes
105221110Sdes				}
10618808Sguido			}
1071558Srgrimes			continue;
10818808Sguido		}
10998542Smckusick		idesc->id_blkno = DIP(&dino, di_db[i]);
11062668Smckusick		if (idesc->id_type != DATA)
1111558Srgrimes			ret = (*idesc->id_func)(idesc);
1121558Srgrimes		else
1131558Srgrimes			ret = dirscan(idesc);
1141558Srgrimes		if (ret & STOP)
1151558Srgrimes			return (ret);
1161558Srgrimes	}
1171558Srgrimes	idesc->id_numfrags = sblock.fs_frag;
11898542Smckusick	remsize = DIP(&dino, di_size) - sblock.fs_bsize * NDADDR;
1191558Srgrimes	sizepb = sblock.fs_bsize;
12098542Smckusick	for (i = 0; i < NIADDR; i++) {
12162668Smckusick		sizepb *= NINDIR(&sblock);
12298542Smckusick		if (DIP(&dino, di_ib[i])) {
12398542Smckusick			idesc->id_blkno = DIP(&dino, di_ib[i]);
124247212Smckusick			ret = iblock(idesc, i + 1, remsize, BT_LEVEL1 + i);
1251558Srgrimes			if (ret & STOP)
1261558Srgrimes				return (ret);
12718808Sguido		} else {
12862668Smckusick			idesc->id_lbn += sizepb / sblock.fs_bsize;
12918808Sguido			if (idesc->id_type == DATA && remsize > 0) {
13018808Sguido				/* An empty block in a directory XXX */
13118808Sguido				getpathname(pathbuf, idesc->id_number,
13218808Sguido						idesc->id_number);
133221110Sdes				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
13418808Sguido					pathbuf);
135221110Sdes				if (reply("ADJUST LENGTH") == 1) {
13618808Sguido					dp = ginode(idesc->id_number);
137221110Sdes					DIP_SET(dp, di_size,
138134589Sscottl					    DIP(dp, di_size) - remsize);
13918808Sguido					remsize = 0;
14018808Sguido					printf(
14118808Sguido					    "YOU MUST RERUN FSCK AFTERWARDS\n");
14218808Sguido					rerun = 1;
143221110Sdes					inodirty();
14418808Sguido					break;
145221110Sdes				}
14618808Sguido			}
1471558Srgrimes		}
1481558Srgrimes		remsize -= sizepb;
1491558Srgrimes	}
1501558Srgrimes	return (KEEPON);
1511558Srgrimes}
1521558Srgrimes
1537585Sbdestatic int
154247212Smckusickiblock(struct inodesc *idesc, long ilevel, off_t isize, int type)
1551558Srgrimes{
15623675Speter	struct bufarea *bp;
157100935Sphk	int i, n, (*func)(struct inodesc *), nif;
15898542Smckusick	off_t sizepb;
1591558Srgrimes	char buf[BUFSIZ];
16018808Sguido	char pathbuf[MAXPATHLEN + 1];
16198542Smckusick	union dinode *dp;
1621558Srgrimes
16362668Smckusick	if (idesc->id_type != DATA) {
1641558Srgrimes		func = idesc->id_func;
1651558Srgrimes		if (((n = (*func)(idesc)) & KEEPON) == 0)
1661558Srgrimes			return (n);
1671558Srgrimes	} else
1681558Srgrimes		func = dirscan;
1691558Srgrimes	if (chkrange(idesc->id_blkno, idesc->id_numfrags))
1701558Srgrimes		return (SKIP);
171247212Smckusick	bp = getdatablk(idesc->id_blkno, sblock.fs_bsize, type);
1721558Srgrimes	ilevel--;
1731558Srgrimes	for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
1741558Srgrimes		sizepb *= NINDIR(&sblock);
17598542Smckusick	if (howmany(isize, sizepb) > NINDIR(&sblock))
1761558Srgrimes		nif = NINDIR(&sblock);
17798542Smckusick	else
17898542Smckusick		nif = howmany(isize, sizepb);
1791558Srgrimes	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
18098542Smckusick		for (i = nif; i < NINDIR(&sblock); i++) {
18198542Smckusick			if (IBLK(bp, i) == 0)
1821558Srgrimes				continue;
1831558Srgrimes			(void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
18437236Sbde			    (u_long)idesc->id_number);
18576352Smckusick			if (preen) {
18681911Skris				pfatal("%s", buf);
18774556Smckusick			} else if (dofix(idesc, buf)) {
188134589Sscottl				IBLK_SET(bp, i, 0);
1891558Srgrimes				dirty(bp);
1901558Srgrimes			}
1911558Srgrimes		}
1921558Srgrimes		flush(fswritefd, bp);
1931558Srgrimes	}
19498542Smckusick	for (i = 0; i < nif; i++) {
19562668Smckusick		if (ilevel == 0)
19662668Smckusick			idesc->id_lbn++;
19798542Smckusick		if (IBLK(bp, i)) {
19898542Smckusick			idesc->id_blkno = IBLK(bp, i);
1991558Srgrimes			if (ilevel == 0)
2001558Srgrimes				n = (*func)(idesc);
2011558Srgrimes			else
202247212Smckusick				n = iblock(idesc, ilevel, isize, type);
2031558Srgrimes			if (n & STOP) {
2041558Srgrimes				bp->b_flags &= ~B_INUSE;
2051558Srgrimes				return (n);
2061558Srgrimes			}
20718808Sguido		} else {
20818808Sguido			if (idesc->id_type == DATA && isize > 0) {
20918808Sguido				/* An empty block in a directory XXX */
21018808Sguido				getpathname(pathbuf, idesc->id_number,
21118808Sguido						idesc->id_number);
212221110Sdes				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
21318808Sguido					pathbuf);
214221110Sdes				if (reply("ADJUST LENGTH") == 1) {
21518808Sguido					dp = ginode(idesc->id_number);
216221110Sdes					DIP_SET(dp, di_size,
217134589Sscottl					    DIP(dp, di_size) - isize);
21818808Sguido					isize = 0;
21918808Sguido					printf(
22018808Sguido					    "YOU MUST RERUN FSCK AFTERWARDS\n");
22118808Sguido					rerun = 1;
222221110Sdes					inodirty();
22318808Sguido					bp->b_flags &= ~B_INUSE;
22418808Sguido					return(STOP);
225221110Sdes				}
22618808Sguido			}
2271558Srgrimes		}
2281558Srgrimes		isize -= sizepb;
2291558Srgrimes	}
2301558Srgrimes	bp->b_flags &= ~B_INUSE;
2311558Srgrimes	return (KEEPON);
2321558Srgrimes}
2331558Srgrimes
2341558Srgrimes/*
2351558Srgrimes * Check that a block in a legal block number.
2361558Srgrimes * Return 0 if in range, 1 if out of range.
2371558Srgrimes */
2387585Sbdeint
23998542Smckusickchkrange(ufs2_daddr_t blk, int cnt)
2401558Srgrimes{
24192806Sobrien	int c;
2421558Srgrimes
24341474Sjulian	if (cnt <= 0 || blk <= 0 || blk > maxfsblock ||
24441474Sjulian	    cnt - 1 > maxfsblock - blk)
2451558Srgrimes		return (1);
24641474Sjulian	if (cnt > sblock.fs_frag ||
24741474Sjulian	    fragnum(&sblock, blk) + cnt > sblock.fs_frag) {
24841474Sjulian		if (debug)
24986514Siedowse			printf("bad size: blk %ld, offset %i, size %d\n",
25086514Siedowse			    (long)blk, (int)fragnum(&sblock, blk), cnt);
25141474Sjulian		return (1);
25241474Sjulian	}
2531558Srgrimes	c = dtog(&sblock, blk);
2541558Srgrimes	if (blk < cgdmin(&sblock, c)) {
2551558Srgrimes		if ((blk + cnt) > cgsblock(&sblock, c)) {
2561558Srgrimes			if (debug) {
2571558Srgrimes				printf("blk %ld < cgdmin %ld;",
25837236Sbde				    (long)blk, (long)cgdmin(&sblock, c));
2591558Srgrimes				printf(" blk + cnt %ld > cgsbase %ld\n",
26037236Sbde				    (long)(blk + cnt),
26137236Sbde				    (long)cgsblock(&sblock, c));
2621558Srgrimes			}
2631558Srgrimes			return (1);
2641558Srgrimes		}
2651558Srgrimes	} else {
2661558Srgrimes		if ((blk + cnt) > cgbase(&sblock, c+1)) {
2671558Srgrimes			if (debug)  {
2681558Srgrimes				printf("blk %ld >= cgdmin %ld;",
26937236Sbde				    (long)blk, (long)cgdmin(&sblock, c));
2701558Srgrimes				printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
27137236Sbde				    (long)(blk + cnt), (long)sblock.fs_fpg);
2721558Srgrimes			}
2731558Srgrimes			return (1);
2741558Srgrimes		}
2751558Srgrimes	}
2761558Srgrimes	return (0);
2771558Srgrimes}
2781558Srgrimes
2791558Srgrimes/*
2801558Srgrimes * General purpose interface for reading inodes.
2811558Srgrimes */
28298542Smckusickunion dinode *
28392839Simpginode(ino_t inumber)
2841558Srgrimes{
28598542Smckusick	ufs2_daddr_t iblk;
2861558Srgrimes
2871558Srgrimes	if (inumber < ROOTINO || inumber > maxino)
288241012Smdf		errx(EEXIT, "bad inode number %ju to ginode",
289241012Smdf		    (uintmax_t)inumber);
2901558Srgrimes	if (startinum == 0 ||
2911558Srgrimes	    inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
2921558Srgrimes		iblk = ino_to_fsba(&sblock, inumber);
2931558Srgrimes		if (pbp != 0)
2941558Srgrimes			pbp->b_flags &= ~B_INUSE;
295247212Smckusick		pbp = getdatablk(iblk, sblock.fs_bsize, BT_INODES);
2961558Srgrimes		startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
2971558Srgrimes	}
29898542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
29998542Smckusick		return ((union dinode *)
30098542Smckusick		    &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
30198542Smckusick	return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]);
3021558Srgrimes}
3031558Srgrimes
3041558Srgrimes/*
3051558Srgrimes * Special purpose version of ginode used to optimize first pass
3061558Srgrimes * over all the inodes in numerical order.
3071558Srgrimes */
30888413Salfredstatic ino_t nextino, lastinum, lastvalidinum;
309247212Smckusickstatic long readcount, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
310247212Smckusickstatic struct bufarea inobuf;
3111558Srgrimes
31298542Smckusickunion dinode *
313188110Smckusickgetnextinode(ino_t inumber, int rebuildcg)
3141558Srgrimes{
315188110Smckusick	int j;
3161558Srgrimes	long size;
317188110Smckusick	mode_t mode;
318247212Smckusick	ufs2_daddr_t ndb, blk;
31998542Smckusick	union dinode *dp;
32098542Smckusick	static caddr_t nextinop;
3211558Srgrimes
32263231Smckusick	if (inumber != nextino++ || inumber > lastvalidinum)
323241012Smdf		errx(EEXIT, "bad inode number %ju to nextinode",
324241012Smdf		    (uintmax_t)inumber);
3251558Srgrimes	if (inumber >= lastinum) {
326247212Smckusick		readcount++;
327247212Smckusick		blk = ino_to_fsba(&sblock, lastinum);
328247212Smckusick		if (readcount % readpercg == 0) {
3291558Srgrimes			size = partialsize;
3301558Srgrimes			lastinum += partialcnt;
3311558Srgrimes		} else {
3321558Srgrimes			size = inobufsize;
3331558Srgrimes			lastinum += fullcnt;
3341558Srgrimes		}
33541474Sjulian		/*
336247212Smckusick		 * If getblk encounters an error, it will already have zeroed
33741474Sjulian		 * out the buffer, so we do not need to do so here.
33841474Sjulian		 */
339247212Smckusick		getblk(&inobuf, blk, size);
340247212Smckusick		nextinop = inobuf.b_un.b_buf;
3411558Srgrimes	}
34298542Smckusick	dp = (union dinode *)nextinop;
343247212Smckusick	if (rebuildcg && nextinop == inobuf.b_un.b_buf) {
344188110Smckusick		/*
345188110Smckusick		 * Try to determine if we have reached the end of the
346188110Smckusick		 * allocated inodes.
347188110Smckusick		 */
348188110Smckusick		mode = DIP(dp, di_mode) & IFMT;
349188110Smckusick		if (mode == 0) {
350188110Smckusick			if (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
351188110Smckusick				NDADDR * sizeof(ufs2_daddr_t)) ||
352188110Smckusick			      memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
353188110Smckusick				NIADDR * sizeof(ufs2_daddr_t)) ||
354188110Smckusick			      dp->dp2.di_mode || dp->dp2.di_size)
355188110Smckusick				return (NULL);
356188110Smckusick			goto inodegood;
357188110Smckusick		}
358188110Smckusick		if (!ftypeok(dp))
359188110Smckusick			return (NULL);
360188110Smckusick		ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
361188110Smckusick		if (ndb < 0)
362188110Smckusick			return (NULL);
363188110Smckusick		if (mode == IFBLK || mode == IFCHR)
364188110Smckusick			ndb++;
365188110Smckusick		if (mode == IFLNK) {
366188110Smckusick			/*
367188110Smckusick			 * Fake ndb value so direct/indirect block checks below
368188110Smckusick			 * will detect any garbage after symlink string.
369188110Smckusick			 */
370188110Smckusick			if (DIP(dp, di_size) < (off_t)sblock.fs_maxsymlinklen) {
371188110Smckusick				ndb = howmany(DIP(dp, di_size),
372188110Smckusick				    sizeof(ufs2_daddr_t));
373188110Smckusick				if (ndb > NDADDR) {
374188110Smckusick					j = ndb - NDADDR;
375188110Smckusick					for (ndb = 1; j > 1; j--)
376188110Smckusick						ndb *= NINDIR(&sblock);
377188110Smckusick					ndb += NDADDR;
378188110Smckusick				}
379188110Smckusick			}
380188110Smckusick		}
381188110Smckusick		for (j = ndb; ndb < NDADDR && j < NDADDR; j++)
382188110Smckusick			if (DIP(dp, di_db[j]) != 0)
383188110Smckusick				return (NULL);
384188110Smckusick		for (j = 0, ndb -= NDADDR; ndb > 0; j++)
385188110Smckusick			ndb /= NINDIR(&sblock);
386188110Smckusick		for (; j < NIADDR; j++)
387188110Smckusick			if (DIP(dp, di_ib[j]) != 0)
388188110Smckusick				return (NULL);
389188110Smckusick	}
390188110Smckusickinodegood:
39198542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
39298542Smckusick		nextinop += sizeof(struct ufs1_dinode);
39398542Smckusick	else
39498542Smckusick		nextinop += sizeof(struct ufs2_dinode);
39598542Smckusick	return (dp);
3961558Srgrimes}
3971558Srgrimes
3987585Sbdevoid
39992839Simpsetinodebuf(ino_t inum)
4001558Srgrimes{
4011558Srgrimes
40241474Sjulian	if (inum % sblock.fs_ipg != 0)
403241012Smdf		errx(EEXIT, "bad inode number %ju to setinodebuf",
404241012Smdf		    (uintmax_t)inum);
40563231Smckusick	lastvalidinum = inum + sblock.fs_ipg - 1;
4061558Srgrimes	startinum = 0;
40741474Sjulian	nextino = inum;
40841474Sjulian	lastinum = inum;
409247212Smckusick	readcount = 0;
410247212Smckusick	if (inobuf.b_un.b_buf != NULL)
41141474Sjulian		return;
4121558Srgrimes	inobufsize = blkroundup(&sblock, INOBUFSIZE);
41398542Smckusick	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
41498542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
4151558Srgrimes	readpercg = sblock.fs_ipg / fullcnt;
4161558Srgrimes	partialcnt = sblock.fs_ipg % fullcnt;
41798542Smckusick	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
41898542Smckusick	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
4191558Srgrimes	if (partialcnt != 0) {
4201558Srgrimes		readpercg++;
4211558Srgrimes	} else {
4221558Srgrimes		partialcnt = fullcnt;
4231558Srgrimes		partialsize = inobufsize;
4241558Srgrimes	}
425247212Smckusick	initbarea(&inobuf, BT_INODES);
426248658Smckusick	if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL)
42737000Scharnier		errx(EEXIT, "cannot allocate space for inode buffer");
4281558Srgrimes}
4291558Srgrimes
4307585Sbdevoid
43192839Simpfreeinodebuf(void)
4321558Srgrimes{
4331558Srgrimes
434247212Smckusick	if (inobuf.b_un.b_buf != NULL)
435247212Smckusick		free((char *)inobuf.b_un.b_buf);
436247212Smckusick	inobuf.b_un.b_buf = NULL;
4371558Srgrimes}
4381558Srgrimes
4391558Srgrimes/*
4401558Srgrimes * Routines to maintain information about directory inodes.
4411558Srgrimes * This is built during the first pass and used during the
4421558Srgrimes * second and third passes.
4431558Srgrimes *
4441558Srgrimes * Enter inodes into the cache.
4451558Srgrimes */
4467585Sbdevoid
44798542Smckusickcacheino(union dinode *dp, ino_t inumber)
4481558Srgrimes{
44998542Smckusick	struct inoinfo *inp, **inpp;
45098542Smckusick	int i, blks;
4511558Srgrimes
45298542Smckusick	if (howmany(DIP(dp, di_size), sblock.fs_bsize) > NDADDR)
4531558Srgrimes		blks = NDADDR + NIADDR;
45498542Smckusick	else
45598542Smckusick		blks = howmany(DIP(dp, di_size), sblock.fs_bsize);
4561558Srgrimes	inp = (struct inoinfo *)
457248658Smckusick		Malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t));
4581558Srgrimes	if (inp == NULL)
45941474Sjulian		errx(EEXIT, "cannot increase directory list");
46057573Smckusick	inpp = &inphead[inumber % dirhash];
4611558Srgrimes	inp->i_nexthash = *inpp;
4621558Srgrimes	*inpp = inp;
46341474Sjulian	inp->i_parent = inumber == ROOTINO ? ROOTINO : (ino_t)0;
4641558Srgrimes	inp->i_dotdot = (ino_t)0;
4651558Srgrimes	inp->i_number = inumber;
46698542Smckusick	inp->i_isize = DIP(dp, di_size);
46798542Smckusick	inp->i_numblks = blks;
46898542Smckusick	for (i = 0; i < (blks < NDADDR ? blks : NDADDR); i++)
46998542Smckusick		inp->i_blks[i] = DIP(dp, di_db[i]);
47098542Smckusick	if (blks > NDADDR)
47198542Smckusick		for (i = 0; i < NIADDR; i++)
47298542Smckusick			inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]);
4731558Srgrimes	if (inplast == listmax) {
4741558Srgrimes		listmax += 100;
4751558Srgrimes		inpsort = (struct inoinfo **)realloc((char *)inpsort,
4761558Srgrimes		    (unsigned)listmax * sizeof(struct inoinfo *));
4771558Srgrimes		if (inpsort == NULL)
47823675Speter			errx(EEXIT, "cannot increase directory list");
4791558Srgrimes	}
4801558Srgrimes	inpsort[inplast++] = inp;
4811558Srgrimes}
4821558Srgrimes
4831558Srgrimes/*
4841558Srgrimes * Look up an inode cache structure.
4851558Srgrimes */
4861558Srgrimesstruct inoinfo *
48792839Simpgetinoinfo(ino_t inumber)
4881558Srgrimes{
48992806Sobrien	struct inoinfo *inp;
4901558Srgrimes
49157573Smckusick	for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) {
4921558Srgrimes		if (inp->i_number != inumber)
4931558Srgrimes			continue;
4941558Srgrimes		return (inp);
4951558Srgrimes	}
496241012Smdf	errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
4971558Srgrimes	return ((struct inoinfo *)0);
4981558Srgrimes}
4991558Srgrimes
5001558Srgrimes/*
5011558Srgrimes * Clean up all the inode cache structure.
5021558Srgrimes */
5037585Sbdevoid
50492839Simpinocleanup(void)
5051558Srgrimes{
50692806Sobrien	struct inoinfo **inpp;
5071558Srgrimes
5081558Srgrimes	if (inphead == NULL)
5091558Srgrimes		return;
5101558Srgrimes	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
5111558Srgrimes		free((char *)(*inpp));
5121558Srgrimes	free((char *)inphead);
5131558Srgrimes	free((char *)inpsort);
5141558Srgrimes	inphead = inpsort = NULL;
5151558Srgrimes}
5168871Srgrimes
5177585Sbdevoid
51892839Simpinodirty(void)
5191558Srgrimes{
52023797Sbde
5211558Srgrimes	dirty(pbp);
5221558Srgrimes}
5231558Srgrimes
5247585Sbdevoid
525100935Sphkclri(struct inodesc *idesc, const char *type, int flag)
5261558Srgrimes{
52798542Smckusick	union dinode *dp;
5281558Srgrimes
5291558Srgrimes	dp = ginode(idesc->id_number);
5301558Srgrimes	if (flag == 1) {
5311558Srgrimes		pwarn("%s %s", type,
53298542Smckusick		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
5331558Srgrimes		pinode(idesc->id_number);
5341558Srgrimes	}
5351558Srgrimes	if (preen || reply("CLEAR") == 1) {
5361558Srgrimes		if (preen)
5371558Srgrimes			printf(" (CLEARED)\n");
5381558Srgrimes		n_files--;
53974556Smckusick		if (bkgrdflag == 0) {
54074556Smckusick			(void)ckinode(dp, idesc);
54174556Smckusick			inoinfo(idesc->id_number)->ino_state = USTATE;
54274556Smckusick			clearinode(dp);
54374556Smckusick			inodirty();
54474556Smckusick		} else {
54574556Smckusick			cmd.value = idesc->id_number;
54698542Smckusick			cmd.size = -DIP(dp, di_nlink);
54774556Smckusick			if (debug)
548100935Sphk				printf("adjrefcnt ino %ld amt %lld\n",
549100935Sphk				    (long)cmd.value, (long long)cmd.size);
55074556Smckusick			if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
55174556Smckusick			    &cmd, sizeof cmd) == -1)
55274556Smckusick				rwerror("ADJUST INODE", cmd.value);
55374556Smckusick		}
5541558Srgrimes	}
5551558Srgrimes}
5561558Srgrimes
5577585Sbdeint
55892839Simpfindname(struct inodesc *idesc)
5591558Srgrimes{
56092806Sobrien	struct direct *dirp = idesc->id_dirp;
5611558Srgrimes
56241474Sjulian	if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
56341474Sjulian		idesc->id_entryno++;
5641558Srgrimes		return (KEEPON);
56541474Sjulian	}
56623675Speter	memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
5671558Srgrimes	return (STOP|FOUND);
5681558Srgrimes}
5691558Srgrimes
5707585Sbdeint
57192839Simpfindino(struct inodesc *idesc)
5721558Srgrimes{
57392806Sobrien	struct direct *dirp = idesc->id_dirp;
5741558Srgrimes
5751558Srgrimes	if (dirp->d_ino == 0)
5761558Srgrimes		return (KEEPON);
5771558Srgrimes	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
5781558Srgrimes	    dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
5791558Srgrimes		idesc->id_parent = dirp->d_ino;
5801558Srgrimes		return (STOP|FOUND);
5811558Srgrimes	}
5821558Srgrimes	return (KEEPON);
5831558Srgrimes}
5841558Srgrimes
58541474Sjulianint
58692839Simpclearentry(struct inodesc *idesc)
58741474Sjulian{
58892806Sobrien	struct direct *dirp = idesc->id_dirp;
58941474Sjulian
59041474Sjulian	if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
59141474Sjulian		idesc->id_entryno++;
59241474Sjulian		return (KEEPON);
59341474Sjulian	}
59441474Sjulian	dirp->d_ino = 0;
59541474Sjulian	return (STOP|FOUND|ALTERED);
59641474Sjulian}
59741474Sjulian
5987585Sbdevoid
59992839Simppinode(ino_t ino)
6001558Srgrimes{
60198542Smckusick	union dinode *dp;
60292806Sobrien	char *p;
6031558Srgrimes	struct passwd *pw;
60424002Speter	time_t t;
6051558Srgrimes
60637236Sbde	printf(" I=%lu ", (u_long)ino);
6071558Srgrimes	if (ino < ROOTINO || ino > maxino)
6081558Srgrimes		return;
6091558Srgrimes	dp = ginode(ino);
6101558Srgrimes	printf(" OWNER=");
61198542Smckusick	if ((pw = getpwuid((int)DIP(dp, di_uid))) != 0)
6121558Srgrimes		printf("%s ", pw->pw_name);
6131558Srgrimes	else
61498542Smckusick		printf("%u ", (unsigned)DIP(dp, di_uid));
61598542Smckusick	printf("MODE=%o\n", DIP(dp, di_mode));
6161558Srgrimes	if (preen)
6171558Srgrimes		printf("%s: ", cdevname);
618101037Smux	printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size));
61998542Smckusick	t = DIP(dp, di_mtime);
62024002Speter	p = ctime(&t);
6211558Srgrimes	printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
6221558Srgrimes}
6231558Srgrimes
6247585Sbdevoid
625100935Sphkblkerror(ino_t ino, const char *type, ufs2_daddr_t blk)
6261558Srgrimes{
6271558Srgrimes
628101037Smux	pfatal("%jd %s I=%ju", (intmax_t)blk, type, (uintmax_t)ino);
6291558Srgrimes	printf("\n");
63041474Sjulian	switch (inoinfo(ino)->ino_state) {
6311558Srgrimes
6321558Srgrimes	case FSTATE:
633136281Struckman	case FZLINK:
63441474Sjulian		inoinfo(ino)->ino_state = FCLEAR;
6351558Srgrimes		return;
6361558Srgrimes
6371558Srgrimes	case DSTATE:
638136281Struckman	case DZLINK:
63941474Sjulian		inoinfo(ino)->ino_state = DCLEAR;
6401558Srgrimes		return;
6411558Srgrimes
6421558Srgrimes	case FCLEAR:
6431558Srgrimes	case DCLEAR:
6441558Srgrimes		return;
6451558Srgrimes
6461558Srgrimes	default:
64741474Sjulian		errx(EEXIT, "BAD STATE %d TO BLKERR", inoinfo(ino)->ino_state);
6481558Srgrimes		/* NOTREACHED */
6491558Srgrimes	}
6501558Srgrimes}
6511558Srgrimes
6521558Srgrimes/*
6531558Srgrimes * allocate an unused inode
6541558Srgrimes */
6551558Srgrimesino_t
65692839Simpallocino(ino_t request, int type)
6571558Srgrimes{
65892806Sobrien	ino_t ino;
65998542Smckusick	union dinode *dp;
660248658Smckusick	struct bufarea *cgbp;
661248658Smckusick	struct cg *cgp;
66234266Sjulian	int cg;
6631558Srgrimes
6641558Srgrimes	if (request == 0)
6651558Srgrimes		request = ROOTINO;
66641474Sjulian	else if (inoinfo(request)->ino_state != USTATE)
6671558Srgrimes		return (0);
6681558Srgrimes	for (ino = request; ino < maxino; ino++)
66941474Sjulian		if (inoinfo(ino)->ino_state == USTATE)
6701558Srgrimes			break;
6711558Srgrimes	if (ino == maxino)
6721558Srgrimes		return (0);
67334266Sjulian	cg = ino_to_cg(&sblock, ino);
674248658Smckusick	cgbp = cgget(cg);
675248658Smckusick	cgp = cgbp->b_un.b_cg;
676248658Smckusick	if (!check_cgmagic(cg, cgbp))
677188110Smckusick		return (0);
67834266Sjulian	setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
67934266Sjulian	cgp->cg_cs.cs_nifree--;
6801558Srgrimes	switch (type & IFMT) {
6811558Srgrimes	case IFDIR:
68241474Sjulian		inoinfo(ino)->ino_state = DSTATE;
68334266Sjulian		cgp->cg_cs.cs_ndir++;
6841558Srgrimes		break;
6851558Srgrimes	case IFREG:
6861558Srgrimes	case IFLNK:
68741474Sjulian		inoinfo(ino)->ino_state = FSTATE;
6881558Srgrimes		break;
6891558Srgrimes	default:
6901558Srgrimes		return (0);
6911558Srgrimes	}
692248658Smckusick	dirty(cgbp);
6931558Srgrimes	dp = ginode(ino);
694134589Sscottl	DIP_SET(dp, di_db[0], allocblk((long)1));
69598542Smckusick	if (DIP(dp, di_db[0]) == 0) {
69641474Sjulian		inoinfo(ino)->ino_state = USTATE;
6971558Srgrimes		return (0);
6981558Srgrimes	}
699134589Sscottl	DIP_SET(dp, di_mode, type);
700134589Sscottl	DIP_SET(dp, di_flags, 0);
701134589Sscottl	DIP_SET(dp, di_atime, time(NULL));
702134589Sscottl	DIP_SET(dp, di_ctime, DIP(dp, di_atime));
703134589Sscottl	DIP_SET(dp, di_mtime, DIP(dp, di_ctime));
704134589Sscottl	DIP_SET(dp, di_mtimensec, 0);
705134589Sscottl	DIP_SET(dp, di_ctimensec, 0);
706134589Sscottl	DIP_SET(dp, di_atimensec, 0);
707134589Sscottl	DIP_SET(dp, di_size, sblock.fs_fsize);
708134589Sscottl	DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
7091558Srgrimes	n_files++;
7101558Srgrimes	inodirty();
71196483Sphk	inoinfo(ino)->ino_type = IFTODT(type);
7121558Srgrimes	return (ino);
7131558Srgrimes}
7141558Srgrimes
7151558Srgrimes/*
7161558Srgrimes * deallocate an inode
7171558Srgrimes */
7187585Sbdevoid
71992839Simpfreeino(ino_t ino)
7201558Srgrimes{
7211558Srgrimes	struct inodesc idesc;
72298542Smckusick	union dinode *dp;
7231558Srgrimes
72423675Speter	memset(&idesc, 0, sizeof(struct inodesc));
7251558Srgrimes	idesc.id_type = ADDR;
7261558Srgrimes	idesc.id_func = pass4check;
7271558Srgrimes	idesc.id_number = ino;
7281558Srgrimes	dp = ginode(ino);
7291558Srgrimes	(void)ckinode(dp, &idesc);
7301558Srgrimes	clearinode(dp);
7311558Srgrimes	inodirty();
73241474Sjulian	inoinfo(ino)->ino_state = USTATE;
7331558Srgrimes	n_files--;
7341558Srgrimes}
735