Commit ddc7ccd3 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] reiserfs: fix races between link and unlink on same

From: Oleg Drokin <green@namesys.com>

This patch (originally by Chris Mason) fixes link/unlink races in reiserfs.
parent 0dff0b1b
...@@ -822,6 +822,7 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry) ...@@ -822,6 +822,7 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
int windex ; int windex ;
struct reiserfs_transaction_handle th ; struct reiserfs_transaction_handle th ;
int jbegin_count; int jbegin_count;
unsigned long savelink;
inode = dentry->d_inode; inode = dentry->d_inode;
...@@ -858,11 +859,20 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry) ...@@ -858,11 +859,20 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
inode->i_nlink = 1; 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); 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; goto end_unlink;
}
inode->i_nlink--;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
reiserfs_update_sd (&th, inode); reiserfs_update_sd (&th, inode);
...@@ -871,7 +881,7 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry) ...@@ -871,7 +881,7 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_ctime = dir->i_mtime = CURRENT_TIME;
reiserfs_update_sd (&th, dir); reiserfs_update_sd (&th, dir);
if (!inode->i_nlink) if (!savelink)
/* prevent file from getting lost */ /* prevent file from getting lost */
add_save_link (&th, inode, 0/* not truncate */); add_save_link (&th, inode, 0/* not truncate */);
...@@ -976,6 +986,12 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct ...@@ -976,6 +986,12 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct
reiserfs_write_unlock(dir->i_sb); reiserfs_write_unlock(dir->i_sb);
return -EMLINK; 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) ; journal_begin(&th, dir->i_sb, jbegin_count) ;
windex = push_journal_writer("reiserfs_link") ; windex = push_journal_writer("reiserfs_link") ;
...@@ -988,13 +1004,13 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct ...@@ -988,13 +1004,13 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct
reiserfs_update_inode_transaction(dir) ; reiserfs_update_inode_transaction(dir) ;
if (retval) { if (retval) {
inode->i_nlink--;
pop_journal_writer(windex) ; pop_journal_writer(windex) ;
journal_end(&th, dir->i_sb, jbegin_count) ; journal_end(&th, dir->i_sb, jbegin_count) ;
reiserfs_write_unlock(dir->i_sb); reiserfs_write_unlock(dir->i_sb);
return retval; return retval;
} }
inode->i_nlink++;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
reiserfs_update_sd (&th, inode); reiserfs_update_sd (&th, inode);
...@@ -1068,6 +1084,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry, ...@@ -1068,6 +1084,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry,
struct reiserfs_transaction_handle th ; struct reiserfs_transaction_handle th ;
int jbegin_count ; int jbegin_count ;
umode_t old_inode_mode; umode_t old_inode_mode;
unsigned long savelink = 1;
/* two balancings: old name removal, new name insertion or "save" link, /* two balancings: old name removal, new name insertion or "save" link,
stat data updates: old directory and new directory and maybe block stat data updates: old directory and new directory and maybe block
...@@ -1246,6 +1263,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry, ...@@ -1246,6 +1263,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry,
new_dentry_inode->i_nlink--; new_dentry_inode->i_nlink--;
} }
new_dentry_inode->i_ctime = new_dir->i_ctime; new_dentry_inode->i_ctime = new_dir->i_ctime;
savelink = new_dentry_inode->i_nlink;
} }
if (S_ISDIR(old_inode_mode)) { if (S_ISDIR(old_inode_mode)) {
...@@ -1279,7 +1297,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry, ...@@ -1279,7 +1297,7 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry,
reiserfs_update_sd (&th, new_dir); reiserfs_update_sd (&th, new_dir);
if (new_dentry_inode) { if (new_dentry_inode) {
if (new_dentry_inode->i_nlink == 0) if (savelink == 0)
add_save_link (&th, new_dentry_inode, 0/* not truncate */); add_save_link (&th, new_dentry_inode, 0/* not truncate */);
reiserfs_update_sd (&th, new_dentry_inode); reiserfs_update_sd (&th, new_dentry_inode);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment