From: Oleg Drokin This patch (originally by Chris Mason) fixes link/unlink races in reiserfs. 25-akpm/fs/reiserfs/namei.c | 30 ++++++++++++++++++++++++------ 1 files changed, 24 insertions(+), 6 deletions(-) diff -puN fs/reiserfs/namei.c~reiserfs-link-unlink-race-fix fs/reiserfs/namei.c --- 25/fs/reiserfs/namei.c~reiserfs-link-unlink-race-fix Thu Jul 31 13:33:34 2003 +++ 25-akpm/fs/reiserfs/namei.c Thu Jul 31 13:33:34 2003 @@ -822,6 +822,7 @@ static int reiserfs_unlink (struct inode int windex ; struct reiserfs_transaction_handle th ; int jbegin_count; + unsigned long savelink; inode = dentry->d_inode; @@ -858,11 +859,20 @@ static int reiserfs_unlink (struct inode inode->i_nlink = 1; } + inode->i_nlink--; + + /* + * we schedule before doing the add_save_link call, save the link + * count so we don't race + */ + savelink = inode->i_nlink; + + retval = reiserfs_cut_from_item (&th, &path, &(de.de_entry_key), dir, NULL, 0); - if (retval < 0) + if (retval < 0) { + inode->i_nlink++; goto end_unlink; - - inode->i_nlink--; + } inode->i_ctime = CURRENT_TIME; reiserfs_update_sd (&th, inode); @@ -871,7 +881,7 @@ static int reiserfs_unlink (struct inode dir->i_ctime = dir->i_mtime = CURRENT_TIME; reiserfs_update_sd (&th, dir); - if (!inode->i_nlink) + if (!savelink) /* prevent file from getting lost */ add_save_link (&th, inode, 0/* not truncate */); @@ -976,6 +986,12 @@ static int reiserfs_link (struct dentry reiserfs_write_unlock(dir->i_sb); return -EMLINK; } + if (inode->i_nlink == 0) { + return -ENOENT; + } + + /* inc before scheduling so reiserfs_unlink knows we are here */ + inode->i_nlink++; journal_begin(&th, dir->i_sb, jbegin_count) ; windex = push_journal_writer("reiserfs_link") ; @@ -988,13 +1004,13 @@ static int reiserfs_link (struct dentry reiserfs_update_inode_transaction(dir) ; if (retval) { + inode->i_nlink--; pop_journal_writer(windex) ; journal_end(&th, dir->i_sb, jbegin_count) ; reiserfs_write_unlock(dir->i_sb); return retval; } - inode->i_nlink++; inode->i_ctime = CURRENT_TIME; reiserfs_update_sd (&th, inode); @@ -1068,6 +1084,7 @@ static int reiserfs_rename (struct inode struct reiserfs_transaction_handle th ; int jbegin_count ; umode_t old_inode_mode; + unsigned long savelink = 1; /* two balancings: old name removal, new name insertion or "save" link, stat data updates: old directory and new directory and maybe block @@ -1246,6 +1263,7 @@ static int reiserfs_rename (struct inode new_dentry_inode->i_nlink--; } new_dentry_inode->i_ctime = new_dir->i_ctime; + savelink = new_dentry_inode->i_nlink; } if (S_ISDIR(old_inode_mode)) { @@ -1279,7 +1297,7 @@ static int reiserfs_rename (struct inode reiserfs_update_sd (&th, new_dir); if (new_dentry_inode) { - if (new_dentry_inode->i_nlink == 0) + if (savelink == 0) add_save_link (&th, new_dentry_inode, 0/* not truncate */); reiserfs_update_sd (&th, new_dentry_inode); } _