Hello, Alan,

this patch cures (to the best of our knowledge) problems with leaking
disk space on crash when some files were open but unlinked (and similar
problem with truncates). Journalling doesn't help here by itself, as
unlink and last close can take place in different transaction. To work
around, during unlink special "save-link" is created which is just an
information about what file to unlink stored in a tree. This link is
removed on inode deletion. Mount "replays" unremoved save-links. In
effect this is some specialised form of logical logging.

This relies on A-reiserfs-i_flags.patch I just sent.
Please, apply.

Nikita.
diff -rup linux/fs/reiserfs/inode.c linux.patched/fs/reiserfs/inode.c
--- linux/fs/reiserfs/inode.c   Fri Sep 21 15:22:34 2001
+++ linux.patched/fs/reiserfs/inode.c   Fri Sep 21 15:23:00 2001
@@ -37,16 +37,19 @@ void reiserfs_delete_inode (struct inode
 
        reiserfs_delete_object (&th, inode);
        pop_journal_writer(windex) ;
-       reiserfs_release_objectid (&th, inode->i_ino);
 
        journal_end(&th, inode->i_sb, jbegin_count) ;
 
-       up (&inode->i_sem); 
+       up (&inode->i_sem);
+ 
+       /* all items of file are deleted, so we can remove "save" link */
+       remove_save_link (inode, 0/* not truncate */);
     } else {
        /* no object items are in the tree */
        ;
     }
     clear_inode (inode); /* note this must go after the journal_end to prevent 
deadlock */
+
     inode->i_blocks = 0;
     unlock_kernel() ;
 }
@@ -1843,12 +1846,23 @@ void reiserfs_truncate_file(struct inode
     ** because the truncate might pack the item anyway 
     ** (it will unmap bh if it packs).
     */
-    journal_begin(&th, p_s_inode->i_sb,  JOURNAL_PER_BALANCE_CNT * 2 ) ;
+    /* it is enough to reserve space in transaction for 2 balancings:
+       one for "save" link adding and another for the first
+       cut_from_item. 1 is for update_sd */
+    journal_begin(&th, p_s_inode->i_sb,  JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ;
     reiserfs_update_inode_transaction(p_s_inode) ;
     windex = push_journal_writer("reiserfs_vfs_truncate_file") ;
+    if (update_timestamps)
+           /* we are doing real truncate: if the system crashes before the last
+              transaction of truncating gets committed - on reboot the file
+              either appears truncated properly or not truncated at all */
+           add_save_link (&th, p_s_inode, 1);
     reiserfs_do_truncate (&th, p_s_inode, page, update_timestamps) ;
     pop_journal_writer(windex) ;
-    journal_end(&th, p_s_inode->i_sb,  JOURNAL_PER_BALANCE_CNT * 2 ) ;
+    journal_end(&th, p_s_inode->i_sb,  JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ;
+
+    if (update_timestamps)
+       remove_save_link (p_s_inode, 1/* truncate */);
 
     if (page) {
         length = offset & (blocksize - 1) ;
diff -rup linux/fs/reiserfs/namei.c linux.patched/fs/reiserfs/namei.c
--- linux/fs/reiserfs/namei.c   Fri Sep 21 15:20:11 2001
+++ linux.patched/fs/reiserfs/namei.c   Fri Sep 21 15:23:00 2001
@@ -656,11 +656,14 @@ int reiserfs_rmdir (struct inode * dir, 
     struct inode * inode;
     int windex ;
     struct reiserfs_transaction_handle th ;
-    int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; 
+    int jbegin_count; 
     INITIALIZE_PATH (path);
     struct reiserfs_dir_entry de;
 
 
+    /* we will be doing 2 balancings and update 2 stat data */
+    jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2;
+
     journal_begin(&th, dir->i_sb, jbegin_count) ;
     windex = push_journal_writer("reiserfs_rmdir") ;
 
@@ -704,6 +707,9 @@ int reiserfs_rmdir (struct inode * dir, 
     dir->i_blocks = ((dir->i_size + 511) >> 9);
     reiserfs_update_sd (&th, dir);
 
+    /* prevent empty directory from getting lost */
+    add_save_link (&th, inode, 0/* not truncate */);
+
     pop_journal_writer(windex) ;
     journal_end(&th, dir->i_sb, jbegin_count) ;
     reiserfs_check_path(&path) ;
@@ -728,7 +734,16 @@ int reiserfs_unlink (struct inode * dir,
     INITIALIZE_PATH (path);
     int windex ;
     struct reiserfs_transaction_handle th ;
-    int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; 
+    int jbegin_count;
+
+    inode = dentry->d_inode;
+
+    /* in this transaction we will be doing at least one balancing and update
+       two stat datas */
+    jbegin_count = JOURNAL_PER_BALANCE_CNT + 2;
+    if (inode->i_nlink < 2)
+       /* reserve space in the transaction for adding "save" link */
+       jbegin_count += JOURNAL_PER_BALANCE_CNT;
 
     journal_begin(&th, dir->i_sb, jbegin_count) ;
     windex = push_journal_writer("reiserfs_unlink") ;
@@ -738,7 +753,6 @@ int reiserfs_unlink (struct inode * dir,
        retval = -ENOENT;
        goto end_unlink;
     }
-    inode = dentry->d_inode;
 
     reiserfs_update_inode_transaction(inode) ;
     reiserfs_update_inode_transaction(dir) ;
@@ -769,6 +783,10 @@ int reiserfs_unlink (struct inode * dir,
     dir->i_ctime = dir->i_mtime = CURRENT_TIME;
     reiserfs_update_sd (&th, dir);
 
+    if (!inode->i_nlink)
+       /* prevent file from getting lost */
+       add_save_link (&th, inode, 0/* not truncate */);
+
     pop_journal_writer(windex) ;
     journal_end(&th, dir->i_sb, jbegin_count) ;
     reiserfs_check_path(&path) ;
@@ -957,9 +975,14 @@ int reiserfs_rename (struct inode * old_
     struct inode * old_inode, * new_inode;
     int windex ;
     struct reiserfs_transaction_handle th ;
-    int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; 
+    int jbegin_count ; 
 
 
+    /* two balancings: old name removal, new name insertion or "save" link,
+       stat data updates: old directory and new directory and maybe block
+       containing ".." of renamed directory */
+    jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 3;
+
     old_inode = old_dentry->d_inode;
     new_inode = new_dentry->d_inode;
 
@@ -1075,13 +1098,6 @@ int reiserfs_rename (struct inode * old_
            reiserfs_restore_prepared_buffer (old_inode->i_sb, new_de.de_bh);
            if (S_ISDIR(old_inode->i_mode))
                reiserfs_restore_prepared_buffer (old_inode->i_sb, dot_dot_de.de_bh);
-#if 0
-           // FIXME: do we need this? shouldn't we simply continue?
-           run_task_queue(&tq_disk);
-           current->policy |= SCHED_YIELD;
-           /*current->counter = 0;*/
-           schedule();
-#endif
            continue;
        }
 
@@ -1104,9 +1120,10 @@ int reiserfs_rename (struct inode * old_
     new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
 
     if (new_inode) {
-       // adjust link number of the victim
+       /* if it is empty directory or file with link count == 1 - we have to
+           "save" link it to guarantee file body removal */
        if (S_ISDIR(new_inode->i_mode)) {
-         DEC_DIR_INODE_NLINK(new_inode)
+           new_inode->i_nlink = 0;
        } else {
          new_inode->i_nlink--;
        }
@@ -1114,21 +1131,17 @@ int reiserfs_rename (struct inode * old_
     }
 
     if (S_ISDIR(old_inode->i_mode)) {
-      //if (dot_dot_de.de_bh) {
-       // adjust ".." of renamed directory
+       /* adjust ".." of renamed directory */
        set_ino_in_dir_entry (&dot_dot_de, INODE_PKEY (new_dir));
        journal_mark_dirty (&th, new_dir->i_sb, dot_dot_de.de_bh);
 
-       DEC_DIR_INODE_NLINK(old_dir)
-       if (new_inode) {
-           if (S_ISDIR(new_inode->i_mode)) {
-               DEC_DIR_INODE_NLINK(new_inode)
-           } else {
-               new_inode->i_nlink--;
-           }
-       } else {
+       if (!new_inode)
+           /* there (in new_dir) was no directory, so it got new link (".." 
+               of renamed directory) */
            INC_DIR_INODE_NLINK(new_dir)
-       }
+
+       /* this is removal of ".." of the renames dir */
+       DEC_DIR_INODE_NLINK(old_dir);
     }
 
     // looks like in 2.3.99pre3 brelse is atomic. so we can use pathrelse
@@ -1146,8 +1159,12 @@ int reiserfs_rename (struct inode * old_
 
     reiserfs_update_sd (&th, old_dir);
     reiserfs_update_sd (&th, new_dir);
-    if (new_inode)
+
+    if (new_inode) {
+       if (new_inode->i_nlink == 0)
+           add_save_link (&th, new_inode, 0/* not truncate */);
        reiserfs_update_sd (&th, new_inode);
+    }
 
     pop_journal_writer(windex) ;
     journal_end(&th, old_dir->i_sb, jbegin_count) ;
diff -rup linux/fs/reiserfs/stree.c linux.patched/fs/reiserfs/stree.c
--- linux/fs/reiserfs/stree.c   Fri Sep 21 15:22:34 2001
+++ linux.patched/fs/reiserfs/stree.c   Fri Sep 21 15:23:00 2001
@@ -727,7 +727,11 @@ int search_by_key (struct super_block * 
            continue;
        }
 
-       RFALSE( ! key_in_buffer(p_s_search_path, p_s_key, p_s_sb),
+       /* only check that the key is in the buffer if p_s_key is not
+          equal to the MAX_KEY. Latter case is only possible in
+          "finish_unfinished()" processing during mount. */
+       RFALSE( COMP_KEYS( &MAX_KEY, p_s_key ) && 
+               ! key_in_buffer(p_s_search_path, p_s_key, p_s_sb),
                "PAP-5130: key is not in the buffer");
 #ifdef CONFIG_REISERFS_CHECK
        if ( cur_tb ) {
@@ -1384,7 +1388,7 @@ int reiserfs_delete_item (struct reiserf
 
 
 /* this deletes item which never gets split */
-static void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th,
+void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th,
                                        struct key * key)
 {
     struct tree_balance tb;
diff -rup linux/fs/reiserfs/super.c linux.patched/fs/reiserfs/super.c
--- linux/fs/reiserfs/super.c   Fri Sep 21 15:20:11 2001
+++ linux.patched/fs/reiserfs/super.c   Fri Sep 21 15:45:49 2001
@@ -62,6 +62,228 @@ void reiserfs_unlockfs(struct super_bloc
   reiserfs_allow_writes(s) ;
 }
 
+extern const struct key  MAX_KEY;
+
+/* this opens transaction unlike add_save_link */
+static void remove_save_link_only (struct super_block * s, struct key * key)
+{
+    struct reiserfs_transaction_handle th;
+
+
+    /* we are going to do one balancing only */
+    journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT);
+
+    reiserfs_delete_solid_item (&th, key);
+    if (is_indirect_le_key (KEY_FORMAT_1, key))
+       /* removals are protected by indirect items */
+       reiserfs_release_objectid (&th, le32_to_cpu (key->k_objectid));
+
+    journal_end (&th, s, JOURNAL_PER_BALANCE_CNT);
+}
+
+
+/* look for uncompleted unlinks and truncates and complete them */
+static void finish_unfinished (struct super_block * s)
+{
+    INITIALIZE_PATH (path);
+    struct cpu_key max_cpu_key, obj_key;
+    struct key save_link_key;
+    int retval;
+    struct item_head * ih;
+    struct buffer_head * bh;
+    int item_pos;
+    char * item;
+    int done;
+    struct inode * inode;
+    int truncate;
+
+
+    /* compose key to look for "save" links */
+    max_cpu_key.version = KEY_FORMAT_1;
+    max_cpu_key.on_disk_key = MAX_KEY;
+    max_cpu_key.key_length = 3;
+
+    done = 0;
+    while (1) {
+       retval = search_item (s, &max_cpu_key, &path);
+       if (retval != ITEM_NOT_FOUND) {
+           reiserfs_warning ("vs-2140: finish_unfinished: search_by_key returned 
+%d\n",
+                             retval);
+           break;
+       }
+       
+       bh = get_last_bh (&path);
+       item_pos = get_item_pos (&path);
+       if (item_pos != B_NR_ITEMS (bh)) {
+           reiserfs_warning ("vs-2060: finish_unfinished: wrong position found\n");
+           break;
+       }
+       item_pos --;
+       ih = B_N_PITEM_HEAD (bh, item_pos);
+
+       if (le32_to_cpu (ih->ih_key.k_dir_id) != MAX_KEY_OBJECTID)
+           /* there are no "save" links anymore */
+           break;
+
+       save_link_key = ih->ih_key;
+       if (is_direct_le_ih (ih))
+           truncate = 1;
+       else
+           truncate = 0;
+
+       /* reiserfs_iget needs k_dirid and k_objectid only */
+       item = B_I_PITEM (bh, ih);
+       obj_key.on_disk_key.k_dir_id = le32_to_cpu (*(__u32 *)item);
+       obj_key.on_disk_key.k_objectid = le32_to_cpu (ih->ih_key.k_objectid);
+       pathrelse (&path);
+
+       inode = reiserfs_iget (s, &obj_key);
+       if (!inode) {
+           /* the unlink almost completed, it just did not manage to remove
+               "save" link and release objectid */
+           reiserfs_warning ("vs-2180: finish_unfinished: iget failed for %K\n",
+                             &obj_key);
+           remove_save_link_only (s, &save_link_key);
+           continue;
+       }
+
+       if (truncate) {
+           inode -> u.reiserfs_i.i_flags |= i_link_saved_truncate_mask;
+           /* not completed truncate found. New size was committed together
+               with "save" link */
+           reiserfs_warning ("Truncating %k to %Ld ..",
+                             INODE_PKEY (inode), inode->i_size);
+           reiserfs_truncate_file (inode, 0/*don't update modification time*/);
+           remove_save_link (inode, truncate);
+       } else {
+           inode -> u.reiserfs_i.i_flags |= i_link_saved_unlink_mask;
+           /* not completed unlink (rmdir) found */
+           reiserfs_warning ("Removing %k..", INODE_PKEY (inode));
+           /* removal gets completed in iput */
+       }
+
+       iput (inode);
+       reiserfs_warning ("done\n");
+       done ++;
+    }
+    
+    pathrelse (&path);
+    if (done)
+       reiserfs_warning ("There were %d uncompleted unlinks/truncates. "
+                         "Completed\n", done);
+}
+
+/* to protect file being unlinked from getting lost we "safe" link files
+   being unlinked. This link will be deleted in the same transaction with last
+   item of file. mounting the filesytem we scan all these links and remove
+   files which almost got lost */
+void add_save_link (struct reiserfs_transaction_handle * th,
+                   struct inode * inode, int truncate)
+{
+    INITIALIZE_PATH (path);
+    int retval;
+    struct cpu_key key;
+    struct item_head ih;
+    __u32 link;
+
+
+    RFALSE( truncate && 
+           ( inode -> u.reiserfs_i.i_flags & i_link_saved_truncate_mask ),
+           "saved link already exists for truncated inode %lx",
+           ( long ) inode -> i_ino );
+    RFALSE( !truncate && 
+           ( inode -> u.reiserfs_i.i_flags & i_link_saved_unlink_mask ),
+           "saved link already exists for unlinked inode %lx",
+           ( long ) inode -> i_ino );
+
+    /* setup key of "save" link */
+    key.version = KEY_FORMAT_1;
+    key.on_disk_key.k_dir_id = MAX_KEY_OBJECTID;
+    key.on_disk_key.k_objectid = inode->i_ino;
+    if (!truncate) {
+       /* unlink, rmdir, rename */
+       set_cpu_key_k_offset (&key, 1);
+       set_cpu_key_k_type (&key, TYPE_INDIRECT);
+
+       /* item head of "safe" link */
+       make_le_item_head (&ih, &key, key.version, 1, TYPE_INDIRECT,
+                          4/*length*/, 0/*free space*/);
+    } else {
+       /* truncate */
+       set_cpu_key_k_offset (&key, 1 + inode->i_sb->s_blocksize);
+       set_cpu_key_k_type (&key, TYPE_DIRECT);
+
+       /* item head of "safe" link */
+       make_le_item_head (&ih, &key, key.version, 1 + inode->i_sb->s_blocksize, 
+TYPE_DIRECT,
+                          4/*length*/, 0xffff/*free space*/);
+    }
+    key.key_length = 3;
+
+    /* look for its place in the tree */
+    retval = search_item (inode->i_sb, &key, &path);
+    if (retval != ITEM_NOT_FOUND) {
+       reiserfs_warning ("vs-2100: add_save_link:"
+                         "search_by_key (%K) returned %d\n", &key, retval);
+       pathrelse (&path);
+       return;
+    }
+
+    /* body of "save" link */
+    link = cpu_to_le32 (INODE_PKEY (inode)->k_dir_id);
+
+    /* put "save" link inot tree */
+    retval = reiserfs_insert_item (th, &path, &key, &ih, (char *)&link);
+    if (retval)
+       reiserfs_warning ("vs-2120: add_save_link: insert_item returned %d\n",
+                         retval);
+    else {
+           if( truncate )
+                   inode -> u.reiserfs_i.i_flags |= i_link_saved_truncate_mask;
+           else
+                   inode -> u.reiserfs_i.i_flags |= i_link_saved_unlink_mask;
+    }
+}
+
+
+/* this opens transaction unlike add_save_link */
+void remove_save_link (struct inode * inode, int truncate)
+{
+    struct reiserfs_transaction_handle th;
+    struct key key;
+
+
+    /* we are going to do one balancing only */
+    journal_begin (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+
+    /* setup key of "save" link */
+    key.k_dir_id = cpu_to_le32 (MAX_KEY_OBJECTID);
+    key.k_objectid = INODE_PKEY (inode)->k_objectid;
+    if (!truncate) {
+       /* unlink, rmdir, rename */
+       set_le_key_k_offset (KEY_FORMAT_1, &key, 1);
+       set_le_key_k_type (KEY_FORMAT_1, &key, TYPE_INDIRECT);
+    } else {
+       /* truncate */
+       set_le_key_k_offset (KEY_FORMAT_1, &key, 1 + inode->i_sb->s_blocksize);
+       set_le_key_k_type (KEY_FORMAT_1, &key, TYPE_DIRECT);
+    }
+
+    if( ( truncate && 
+         ( inode -> u.reiserfs_i.i_flags & i_link_saved_truncate_mask ) ) ||
+       ( !truncate && 
+         ( inode -> u.reiserfs_i.i_flags & i_link_saved_unlink_mask ) ) )
+           reiserfs_delete_solid_item (&th, &key);
+    if (!truncate) {
+           reiserfs_release_objectid (&th, inode->i_ino);
+           inode -> u.reiserfs_i.i_flags &= ~i_link_saved_unlink_mask;
+    } else
+           inode -> u.reiserfs_i.i_flags &= ~i_link_saved_truncate_mask;
+
+    journal_end (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+}
+
+
+
 void reiserfs_put_super (struct super_block * s)
 {
   int i;
@@ -765,12 +987,16 @@ struct super_block * reiserfs_read_super
         set_sb_hash_function_code( rs, function2code(s->u.reiserfs_sb.s_hash_function 
) );
 
        journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
-       journal_end(&th, s, 1) ;
+       journal_end(&th, s, 1);
+       
+       /* look for files which were to be removed in previous session */
+       finish_unfinished (s);
+       
        s->s_dirt = 0;
     } else {
        struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
        if (strncmp (rs->s_magic,  REISER2FS_SUPER_MAGIC_STRING, 
-                    strlen ( REISER2FS_SUPER_MAGIC_STRING))) {
+                    strlen (REISER2FS_SUPER_MAGIC_STRING))) {
            reiserfs_warning("reiserfs: using 3.5.x disk format\n") ;
        }
     }
@@ -841,6 +1067,7 @@ static void __exit exit_reiserfs_fs(void
        reiserfs_proc_info_global_done();
         unregister_filesystem(&reiserfs_fs_type);
 }
+
 
 module_init(init_reiserfs_fs) ;
 module_exit(exit_reiserfs_fs) ;
diff -rup linux/include/linux/reiserfs_fs.h linux.patched/include/linux/reiserfs_fs.h
--- linux/include/linux/reiserfs_fs.h   Fri Sep 21 15:22:34 2001
+++ linux.patched/include/linux/reiserfs_fs.h   Fri Sep 21 15:23:00 2001
@@ -1848,7 +1848,8 @@ int reiserfs_delete_item (struct reiserf
                          struct inode * inode, 
                          struct buffer_head  * p_s_un_bh);
 
-
+void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th,
+                                                                struct key * key);
 void reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * 
p_s_inode);
 void reiserfs_do_truncate (struct reiserfs_transaction_handle *th, 
                           struct  inode * p_s_inode, struct page *, 
@@ -1873,8 +1874,6 @@ void make_cpu_key (struct cpu_key * cpu_
 void make_le_item_head (struct item_head * ih, const struct cpu_key * key, 
                        int version,
                        loff_t offset, int type, int length, int entry_count);
-/*void store_key (struct key * key);
-void forget_key (struct key * key);*/
 int reiserfs_get_block (struct inode * inode, long block,
                        struct buffer_head * bh_result, int create);
 struct inode * reiserfs_iget (struct super_block * s, 
@@ -1882,7 +1881,7 @@ struct inode * reiserfs_iget (struct sup
 void reiserfs_read_inode (struct inode * inode) ;
 void reiserfs_read_inode2(struct inode * inode, void *p) ;
 void reiserfs_delete_inode (struct inode * inode);
-extern int reiserfs_notify_change(struct dentry * dentry, struct iattr * attr);
+int reiserfs_notify_change(struct dentry * dentry, struct iattr * attr);
 void reiserfs_write_inode (struct inode * inode, int) ;
 
 /* nfsd support functions */
@@ -1919,6 +1918,9 @@ int reiserfs_rename (struct inode * old_
 void reiserfs_write_super (struct super_block * s);
 void reiserfs_put_super (struct super_block * s);
 int reiserfs_remount (struct super_block * s, int * flags, char * data);
+void add_save_link (struct reiserfs_transaction_handle * th,
+                                       struct inode * inode, int truncate);
+void remove_save_link (struct inode * inode, int truncate);
 struct super_block * reiserfs_read_super (struct super_block * s, void * data, int 
silent);
 int reiserfs_statfs (struct super_block * s, struct statfs * buf);
 

Reply via email to