- Permit zero length directories as a handled inconsistency. This allows
directory truncation to proceed before the link has been cleared. This is accomplished by detecting a directory with no . or .. links and clearing the named directory entry in the parent. - Add a new function ino_remref() which handles the details of removing a reference to an inode as a result of a lost directory. There were some minor errors in various subcases of this routine.
This commit is contained in:
parent
48162ca1e0
commit
24d37c1eec
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=209716
@ -807,6 +807,44 @@ blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int *frags)
|
||||
return (nblk == blk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the directory entry at diroff that should point to child. Minimal
|
||||
* checking is done and it is assumed that this path was verified with isat.
|
||||
*/
|
||||
static void
|
||||
ino_clrat(ino_t parent, off_t diroff, ino_t child)
|
||||
{
|
||||
union dinode *dip;
|
||||
struct direct *dp;
|
||||
ufs2_daddr_t blk;
|
||||
uint8_t *block;
|
||||
ufs_lbn_t lbn;
|
||||
int blksize;
|
||||
int frags;
|
||||
int doff;
|
||||
|
||||
if (debug)
|
||||
printf("Clearing inode %d from parent %d at offset %jd\n",
|
||||
child, parent, diroff);
|
||||
|
||||
lbn = lblkno(fs, diroff);
|
||||
doff = blkoff(fs, diroff);
|
||||
dip = ino_read(parent);
|
||||
blk = ino_blkatoff(dip, parent, lbn, &frags);
|
||||
blksize = sblksize(fs, DIP(dip, di_size), lbn);
|
||||
block = dblk_read(blk, blksize);
|
||||
dp = (struct direct *)&block[doff];
|
||||
if (dp->d_ino != child)
|
||||
errx(1, "Inode %d does not exist in %d at %jd",
|
||||
child, parent, diroff);
|
||||
dp->d_ino = 0;
|
||||
dblk_dirty(blk);
|
||||
/*
|
||||
* The actual .. reference count will already have been removed
|
||||
* from the parent by the .. remref record.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether a pointer to an inode exists within a directory
|
||||
* at a specified offset. Returns the mode of the found entry.
|
||||
@ -1134,6 +1172,57 @@ ino_setskip(struct suj_ino *sino, ino_t parent)
|
||||
sino->si_skipparent = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot)
|
||||
{
|
||||
struct suj_ino *sino;
|
||||
struct suj_rec *srec;
|
||||
struct jrefrec *rrec;
|
||||
|
||||
/*
|
||||
* Lookup this inode to see if we have a record for it.
|
||||
*/
|
||||
sino = ino_lookup(child, 0);
|
||||
/*
|
||||
* Tell any child directories we've already removed their
|
||||
* parent link cnt. Don't try to adjust our link down again.
|
||||
*/
|
||||
if (sino != NULL && isdotdot == 0)
|
||||
ino_setskip(sino, parent);
|
||||
/*
|
||||
* No valid record for this inode. Just drop the on-disk
|
||||
* link by one.
|
||||
*/
|
||||
if (sino == NULL || sino->si_hasrecs == 0) {
|
||||
ino_decr(child);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Use ino_adjust() if ino_check() has already processed this
|
||||
* child. If we lose the last non-dot reference to a
|
||||
* directory it will be discarded.
|
||||
*/
|
||||
if (sino->si_linkadj) {
|
||||
sino->si_nlink--;
|
||||
if (isdotdot)
|
||||
sino->si_dotlinks--;
|
||||
ino_adjust(sino);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If we haven't yet processed this inode we need to make
|
||||
* sure we will successfully discover the lost path. If not
|
||||
* use nlinkadj to remember.
|
||||
*/
|
||||
TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
|
||||
rrec = (struct jrefrec *)srec->sr_rec;
|
||||
if (rrec->jr_parent == parent &&
|
||||
rrec->jr_diroff == diroff)
|
||||
return;
|
||||
}
|
||||
sino->si_nlinkadj++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the children of a directory when the directory is discarded.
|
||||
*/
|
||||
@ -1141,13 +1230,11 @@ static void
|
||||
ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
|
||||
{
|
||||
struct suj_ino *sino;
|
||||
struct suj_rec *srec;
|
||||
struct jrefrec *rrec;
|
||||
struct direct *dp;
|
||||
off_t diroff;
|
||||
uint8_t *block;
|
||||
int skipparent;
|
||||
int isparent;
|
||||
int isdotdot;
|
||||
int dpoff;
|
||||
int size;
|
||||
|
||||
@ -1165,53 +1252,15 @@ ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
|
||||
continue;
|
||||
if (dp->d_namlen == 1 && dp->d_name[0] == '.')
|
||||
continue;
|
||||
isparent = dp->d_namlen == 2 && dp->d_name[0] == '.' &&
|
||||
isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' &&
|
||||
dp->d_name[1] == '.';
|
||||
if (isparent && skipparent == 1)
|
||||
if (isdotdot && skipparent == 1)
|
||||
continue;
|
||||
if (debug)
|
||||
printf("Directory %d removing ino %d name %s\n",
|
||||
ino, dp->d_ino, dp->d_name);
|
||||
/*
|
||||
* Lookup this inode to see if we have a record for it.
|
||||
* If not, we've already adjusted it assuming this path
|
||||
* was valid and we have to adjust once more.
|
||||
*/
|
||||
sino = ino_lookup(dp->d_ino, 0);
|
||||
if (sino == NULL || sino->si_hasrecs == 0) {
|
||||
ino_decr(ino);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Use ino_adjust() so if we lose the last non-dot reference
|
||||
* to a directory it can be discarded.
|
||||
*/
|
||||
if (sino->si_linkadj) {
|
||||
sino->si_nlink--;
|
||||
if (isparent)
|
||||
sino->si_dotlinks--;
|
||||
ino_adjust(sino);
|
||||
}
|
||||
/*
|
||||
* Tell any child directories we've already removed their
|
||||
* parent. Don't try to adjust our link down again.
|
||||
*/
|
||||
if (isparent == 0)
|
||||
ino_setskip(sino, ino);
|
||||
/*
|
||||
* If we haven't yet processed this inode we need to make
|
||||
* sure we will successfully discover the lost path. If not
|
||||
* use nlinkadj to remember.
|
||||
*/
|
||||
diroff = lblktosize(fs, lbn) + dpoff;
|
||||
TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
|
||||
rrec = (struct jrefrec *)srec->sr_rec;
|
||||
if (rrec->jr_parent == ino &&
|
||||
rrec->jr_diroff == diroff)
|
||||
break;
|
||||
}
|
||||
if (srec == NULL)
|
||||
sino->si_nlinkadj++;
|
||||
ino_remref(ino, dp->d_ino, diroff, isdotdot);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1293,18 +1342,38 @@ ino_adjust(struct suj_ino *sino)
|
||||
struct suj_ino *stmp;
|
||||
union dinode *ip;
|
||||
nlink_t nlink;
|
||||
int recmode;
|
||||
int reqlink;
|
||||
int isdot;
|
||||
int mode;
|
||||
ino_t ino;
|
||||
|
||||
nlink = sino->si_nlink;
|
||||
ino = sino->si_ino;
|
||||
mode = sino->si_mode & IFMT;
|
||||
/*
|
||||
* If it's a directory with no dot links, it was truncated before
|
||||
* the name was cleared. We need to clear the dirent that
|
||||
* points at it.
|
||||
*/
|
||||
if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) {
|
||||
sino->si_nlink = nlink = 0;
|
||||
TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
|
||||
rrec = (struct jrefrec *)srec->sr_rec;
|
||||
if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino,
|
||||
&recmode, &isdot) == 0)
|
||||
continue;
|
||||
ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino);
|
||||
break;
|
||||
}
|
||||
if (srec == NULL)
|
||||
errx(1, "Directory %d name not found", ino);
|
||||
}
|
||||
/*
|
||||
* If it's a directory with no real names pointing to it go ahead
|
||||
* and truncate it. This will free any children.
|
||||
*/
|
||||
if ((sino->si_mode & IFMT) == IFDIR &&
|
||||
nlink - sino->si_dotlinks == 0) {
|
||||
if (mode == IFDIR && nlink - sino->si_dotlinks == 0) {
|
||||
sino->si_nlink = nlink = 0;
|
||||
/*
|
||||
* Mark any .. links so they know not to free this inode
|
||||
|
Loading…
Reference in New Issue
Block a user