Properly handle the replacement of a partially allocated root directory.

If the root directory exists but has a bad block number Pass1 will
accept it and setup an inoinfo structure for it. When Pass2 runs
and cannot read the root inode's content because of a bad (or
duplicate) block number, it removes the bad root inode and replaces
it. As part of creating the replacement root inode, it creates an
inoinfo entry for it. But Pass2 did delete the inoinfo entry that
Pass1 had set up for the root inode so ended up with two inoinfo
structures for it. The final step of Pass2 checks that all the ".."
entries are correct adding them if they are missing which resulted
in a second ".." entry being added to the root directory which
definitely did not go over well in the kernel name cache!

Reported by:  Peter Holm
Sponsored by: The FreeBSD Foundation
This commit is contained in:
Kirk McKusick 2022-09-03 14:46:50 -07:00
parent fb47649ebc
commit f4fc389524
3 changed files with 24 additions and 8 deletions

View File

@ -474,6 +474,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
union dinode *dp;
int lostdir;
ino_t oldlfdir;
struct inoinfo *inp;
struct inodesc idesc;
char tempname[BUFSIZ];
@ -581,10 +582,12 @@ linkup(ino_t orphan, ino_t parentdir, char *name)
inodirty(&ip);
inoinfo(lfdir)->ino_linkcnt++;
pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
if (parentdir != (ino_t)-1) {
inp = getinoinfo(parentdir);
if (parentdir != (ino_t)-1 && (inp->i_flags & INFO_NEW) == 0) {
printf("PARENT WAS I=%lu\n", (u_long)parentdir);
/*
* The parent directory, because of the ordering
* If the parent directory did not have to
* be replaced then because of the ordering
* guarantees, has had the link count incremented
* for the child, but no entry was made. This
* fixes the parent link count so that fsck does
@ -823,7 +826,12 @@ allocdir(ino_t parent, ino_t request, int mode)
inodirty(&ip);
if (ino == UFS_ROOTINO) {
inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
cacheino(dp, ino);
if ((inp = getinoinfo(ino)) == NULL)
inp = cacheino(dp, ino);
else
inp->i_flags = INFO_NEW;
inp->i_parent = parent;
inp->i_dotdot = parent;
irelse(&ip);
return(ino);
}
@ -832,8 +840,8 @@ allocdir(ino_t parent, ino_t request, int mode)
irelse(&ip);
return (0);
}
cacheino(dp, ino);
inp = getinoinfo(ino);
if ((inp = getinoinfo(ino)) == NULL)
inp = cacheino(dp, ino);
inp->i_parent = parent;
inp->i_dotdot = parent;
inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;

View File

@ -311,9 +311,15 @@ extern struct inoinfo {
ino_t i_parent; /* inode number of parent */
ino_t i_dotdot; /* inode number of `..' */
size_t i_isize; /* size of inode */
u_int i_flags; /* flags, see below */
u_int i_numblks; /* size of block array in bytes */
ufs2_daddr_t i_blks[1]; /* actually longer */
} **inphead, **inpsort;
/*
* flags for struct inoinfo
*/
#define INFO_NEW 0x0000001 /* replaced broken directory */
extern long dirhash, inplast;
extern unsigned long numdirs, listmax;
extern long countdirs; /* number of directories we actually found */
@ -446,7 +452,7 @@ void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size);
void blerase(int fd, ufs2_daddr_t blk, long size);
void blzero(int fd, ufs2_daddr_t blk, long size);
void brelse(struct bufarea *);
void cacheino(union dinode *dp, ino_t inumber);
struct inoinfo *cacheino(union dinode *dp, ino_t inumber);
void catch(int);
void catchquit(int);
void cgdirty(struct bufarea *);

View File

@ -698,12 +698,14 @@ freeinodebuf(void)
*
* Enter inodes into the cache.
*/
void
struct inoinfo *
cacheino(union dinode *dp, ino_t inumber)
{
struct inoinfo *inp, **inpp;
int i, blks;
if (getinoinfo(inumber) != NULL)
pfatal("cacheino: duplicate entry for ino %ld\n", inumber);
if (howmany(DIP(dp, di_size), sblock.fs_bsize) > UFS_NDADDR)
blks = UFS_NDADDR + UFS_NIADDR;
else if (DIP(dp, di_size) > 0)
@ -735,6 +737,7 @@ cacheino(union dinode *dp, ino_t inumber)
errx(EEXIT, "cannot increase directory list");
}
inpsort[inplast++] = inp;
return (inp);
}
/*
@ -750,7 +753,6 @@ getinoinfo(ino_t inumber)
continue;
return (inp);
}
errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
return ((struct inoinfo *)0);
}