diff options
author | julian <julian@FreeBSD.org> | 1998-06-12 20:48:30 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 1998-06-12 20:48:30 +0000 |
commit | 6b27bc77372b6e8a99262f56eb1791b65e4114d5 (patch) | |
tree | efb17b32d19ced63d54262f6fabe0973135d45e0 /contrib/sys | |
parent | 02a56dd03f499041230d5e52ef0187e6af7cdce2 (diff) | |
download | FreeBSD-src-6b27bc77372b6e8a99262f56eb1791b65e4114d5.zip FreeBSD-src-6b27bc77372b6e8a99262f56eb1791b65e4114d5.tar.gz |
Fix the case when renaming to a file that you've just created and deleted,
that had an inode that has not yet been written to disk, when the inode of the
new file is also not yet written to disk, and your old directory entry is not
yet on disk but you need to remove it and the new name exists in memory
but has been deleted but the transaction to write the deleted name to disk
exists and has not yet been cancelled by the request to delete the non
existant name. I don't know how kirk could have missed such a glaring
problem for so long. :-) Especially since the inconsitency survived on
the disk for a whole 4 second on average before being fixed by other code.
This was not a crashing bug but just led to filesystem inconsitencies
if you crashed.
Submitted by: Kirk McKusick (mckusick@mckusick.com)
Diffstat (limited to 'contrib/sys')
-rw-r--r-- | contrib/sys/softupdates/ffs_softdep.c | 65 |
1 files changed, 43 insertions, 22 deletions
diff --git a/contrib/sys/softupdates/ffs_softdep.c b/contrib/sys/softupdates/ffs_softdep.c index 438314f..9d0b2d1 100644 --- a/contrib/sys/softupdates/ffs_softdep.c +++ b/contrib/sys/softupdates/ffs_softdep.c @@ -54,7 +54,7 @@ * SUCH DAMAGE. * * @(#)ffs_softdep.c 9.23 (McKusick) 2/20/98 - * $Id: ffs_softdep.c,v 1.9 1998/06/10 20:45:46 julian Exp $ + * $Id: ffs_softdep.c,v 1.10 1998/06/11 17:44:32 julian Exp $ */ /* @@ -2412,18 +2412,17 @@ softdep_setup_directory_change(bp, dp, ip, newinum, isrmdir) int isrmdir; /* indicates if doing RMDIR */ { int offset; - struct diradd *dap; + struct diradd *dap = NULL; struct dirrem *dirrem; + struct pagedep *pagedep; struct inodedep *inodedep; offset = blkoff(dp->i_fs, dp->i_offset); /* - * Whiteouts have no addition dependencies. + * Whiteouts do not need diradd dependencies. */ - if (newinum == WINO) { - dap = NULL; - } else { + if (newinum != WINO) { MALLOC(dap, struct diradd *, sizeof(struct diradd), M_DIRADD, M_WAITOK); bzero(dap, sizeof(struct diradd)); @@ -2434,31 +2433,53 @@ softdep_setup_directory_change(bp, dp, ip, newinum, isrmdir) } /* - * Allocate a new dirrem if appropriate and ACQUIRE_LOCK. + * Allocate a new dirrem and ACQUIRE_LOCK. */ dirrem = newdirrem(bp, dp, ip, isrmdir); + pagedep = dirrem->dm_pagedep; /* - * If the inode has already been written, then no addition - * dependency needs to be created. + * Whiteouts have no additional dependencies, + * so just put the dirrem on the correct list. */ - if (inodedep_lookup(dp->i_fs, newinum, 0, &inodedep) == 0 || - (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { - WORKITEM_FREE(dap, D_DIRADD); - dap = NULL; + if (newinum == WINO) { + if ((dirrem->dm_state & COMPLETE) == 0) { + LIST_INSERT_HEAD(&pagedep->pd_dirremhd, dirrem, + dm_next); + } else { + dirrem->dm_dirinum = pagedep->pd_ino; + add_to_worklist(&dirrem->dm_list); + } + FREE_LOCK(&lk); + return; } - if (dap) { - dap->da_previous = dirrem; - LIST_INSERT_HEAD( - &dirrem->dm_pagedep->pd_diraddhd[DIRADDHASH(offset)], + /* + * Link into its inodedep. Put it on the id_bufwait list if the inode + * is not yet written. If it is written, do the post-inode write + * processing to put it on the id_pendinghd list. + */ + dap->da_previous = dirrem; + if (inodedep_lookup(dp->i_fs, newinum, DEPALLOC, &inodedep) == 0 || + (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { + dap->da_state |= COMPLETE; + LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); + WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list); + } else { + LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap, da_pdlist); WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list); - } else if ((dirrem->dm_state & COMPLETE) == 0) { - LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, dirrem, - dm_next); - } else { - dirrem->dm_dirinum = dirrem->dm_pagedep->pd_ino; + } + /* + * If the previous inode was never written or its previous directory + * entry was never written, then we do not want to roll back to this + * previous value. Instead we want to roll back to zero and immediately + * free the unwritten or unreferenced inode. + */ + if (dirrem->dm_state & COMPLETE) { + dap->da_state &= ~DIRCHG; + dap->da_pagedep = pagedep; + dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } FREE_LOCK(&lk); |