summaryrefslogtreecommitdiffstats
path: root/contrib/sys
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>1998-06-12 20:48:30 +0000
committerjulian <julian@FreeBSD.org>1998-06-12 20:48:30 +0000
commit6b27bc77372b6e8a99262f56eb1791b65e4114d5 (patch)
treeefb17b32d19ced63d54262f6fabe0973135d45e0 /contrib/sys
parent02a56dd03f499041230d5e52ef0187e6af7cdce2 (diff)
downloadFreeBSD-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.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 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);
OpenPOWER on IntegriCloud