]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] reiserfs: fix races between link and unlink on same
authorAndrew Morton <akpm@osdl.org>
Thu, 7 Aug 2003 04:13:27 +0000 (21:13 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Thu, 7 Aug 2003 04:13:27 +0000 (21:13 -0700)
From: Oleg Drokin <green@namesys.com>

This patch (originally by Chris Mason) fixes link/unlink races in reiserfs.

fs/reiserfs/namei.c

index 93151fb285c02ed25881de6994d4b98f93f7c73d..85dddb7a83b34b16e637843c7947ebfbd8c1ac0b 100644 (file)
@@ -822,6 +822,7 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
     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 * dir, struct dentry *dentry)
        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, struct dentry *dentry)
     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 * old_dentry, struct inode * dir, struct
        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 * old_dentry, struct inode * dir, struct
     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 * old_dir, struct dentry *old_dentry,
     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 * old_dir, struct dentry *old_dentry,
            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 * old_dir, struct dentry *old_dentry,
     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);
     }