aboutsummaryrefslogtreecommitdiff
path: root/contrib/sys/softupdates/ffs_softdep.c
diff options
context:
space:
mode:
authorJulian Elischer <julian@FreeBSD.org>1998-06-12 20:48:30 +0000
committerJulian Elischer <julian@FreeBSD.org>1998-06-12 20:48:30 +0000
commitaa75cb86b4d26d42ef12eb8e411181981eb201b2 (patch)
treeb0d429afd141a88f0c94cd2377c9e4d90ca2b08a /contrib/sys/softupdates/ffs_softdep.c
parent41dbe0c7af568c4122839ca6d0edc809f12f6b48 (diff)
downloadsrc-aa75cb86b4d26d42ef12eb8e411181981eb201b2.tar.gz
src-aa75cb86b4d26d42ef12eb8e411181981eb201b2.zip
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)
Notes
Notes: svn path=/head/; revision=36935
Diffstat (limited to 'contrib/sys/softupdates/ffs_softdep.c')
-rw-r--r--contrib/sys/softupdates/ffs_softdep.c65
1 files changed, 43 insertions, 22 deletions
diff --git a/contrib/sys/softupdates/ffs_softdep.c b/contrib/sys/softupdates/ffs_softdep.c
index 438314f87431..9d0b2d18570d 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);