    G-safe-unlink.patch
    Fixes long-standing problem in reiserfs, when disk space gets leaked
    if crash occurred when some process hold a reference to unlinked file.
diff -rup linux-2.4.6/fs/reiserfs/inode.c linux-2.4.6.cleanup/fs/reiserfs/inode.c
--- linux-2.4.6/fs/reiserfs/inode.c	Wed Jul 11 23:05:13 2001
+++ linux-2.4.6.cleanup/fs/reiserfs/inode.c	Wed Jul 11 21:48:10 2001
@@ -44,14 +44,21 @@ void reiserfs_delete_inode (struct inode
 	journal_end(&th, inode->i_sb, jbegin_count) ;
 
 	up (&inode->i_sem); 
+
+	/* all items of file are deleted, so we can remove "save" link */
+	remove_save_link (inode);
+
     } else {
 	/* no object items are in the tree */
 	;
     }
+
     clear_inode (inode); /* note this must go after the journal_end to prevent deadlock */
-    unlock_kernel() ;
+
+    unlock_kernel ();
 }
 
+
 static void _make_cpu_key (struct cpu_key * key, int version, __u32 dirid, __u32 objectid, 
 			   loff_t offset, int type, int length)
 {
@@ -1664,11 +1671,21 @@ void reiserfs_truncate_file(struct inode
     ** (it will unmap bh if it packs).
     */
     prevent_flush_page_lock(page, p_s_inode) ;
-    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) ;
     windex = push_journal_writer("reiserfs_vfs_truncate_file") ;
+
+
     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);
+
     allow_flush_page_lock(page, p_s_inode) ;
 
     if (page) {
diff -rup linux-2.4.6/fs/reiserfs/namei.c linux-2.4.6.cleanup/fs/reiserfs/namei.c
--- linux-2.4.6/fs/reiserfs/namei.c	Wed Jul 11 23:05:13 2001
+++ linux-2.4.6.cleanup/fs/reiserfs/namei.c	Wed Jul 11 21:48:10 2001
@@ -711,11 +711,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") ;
 
@@ -756,6 +759,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);
+
     pop_journal_writer(windex) ;
     journal_end(&th, dir->i_sb, jbegin_count) ;
     reiserfs_check_path(&path) ;
@@ -787,7 +793,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") ;
@@ -797,7 +812,6 @@ int reiserfs_unlink (struct inode * dir,
 	retval = -ENOENT;
 	goto end_unlink;
     }
-    inode = dentry->d_inode;
 
     if (de.de_objectid != inode->i_ino) {
 	// FIXME: compare key of an object and a key found in the
@@ -825,6 +839,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);
+
     pop_journal_writer(windex) ;
     journal_end(&th, dir->i_sb, jbegin_count) ;
     reiserfs_check_path(&path) ;
@@ -1027,9 +1045,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;
 
@@ -1137,13 +1160,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;
 	}
 
@@ -1166,9 +1182,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 garantee 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--;
 	}
@@ -1176,21 +1193,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
@@ -1208,8 +1221,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);
 	reiserfs_update_sd (&th, new_inode);
+    }
 
     pop_journal_writer(windex) ;
     journal_end(&th, old_dir->i_sb, jbegin_count) ;
diff -rup linux-2.4.6/fs/reiserfs/stree.c linux-2.4.6.cleanup/fs/reiserfs/stree.c
--- linux-2.4.6/fs/reiserfs/stree.c	Wed Jul 11 23:05:13 2001
+++ linux-2.4.6.cleanup/fs/reiserfs/stree.c	Wed Jul 11 22:53:46 2001
@@ -727,8 +727,12 @@ int search_by_key (struct super_block * 
 	    continue;
 	}
 
-	RFALSE( ! key_in_buffer(p_s_search_path, p_s_key, p_s_sb),
-		"PAP-5130: key is not in the buffer");
+	/* only check that key is in 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 ) {
 	    print_cur_tb ("5140");
@@ -1401,7 +1405,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;
@@ -1413,16 +1417,17 @@ static void reiserfs_delete_solid_item (
     
     le_key2cpu_key (&cpu_key, key);
     
+
     while (1) {
 	retval = search_item (th->t_super, &cpu_key, &path);
 	if (retval == IO_ERROR) {
-	    reiserfs_warning ("vs-: reiserfs_delete_solid_item: "
+	    reiserfs_warning ("vs-5360: reiserfs_delete_solid_item: "
 			      "i/o failure occurred trying to delete %K\n", &cpu_key);
 	    break;
 	}
 	if (retval != ITEM_FOUND) {
 	    pathrelse (&path);
-	    reiserfs_warning ("vs-: reiserfs_delete_solid_item: %k not found",
+	    reiserfs_warning ("vs-5380: reiserfs_delete_solid_item: %k not found\n",
 			      key);
 	    break;
 	}
@@ -1442,7 +1447,7 @@ static void reiserfs_delete_solid_item (
 	}
 
 	// IO_ERROR, NO_DISK_SPACE, etc
-	reiserfs_warning ("vs-: reiserfs_delete_solid_item: "
+	reiserfs_warning ("vs-5400: reiserfs_delete_solid_item: "
 			  "could not delete %K due to fix_nodes failure\n", &cpu_key);
 	unfix_nodes (&tb);
 	break;
@@ -1783,6 +1788,15 @@ void reiserfs_do_truncate (struct reiser
 	pathrelse(&s_search_path);
 	return;
     }
+
+
+    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);
+
+
     /* Update key to search for the last file item. */
     set_cpu_key_k_offset (&s_item_key, n_file_size);
 
@@ -1819,7 +1833,6 @@ void reiserfs_do_truncate (struct reiser
 
 	  if (update_timestamps) {
 	      p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME;
-	      // FIXME: sd gets wrong size here
 	  } 
 	  reiserfs_update_sd(th, p_s_inode) ;
 
@@ -1838,6 +1851,10 @@ void reiserfs_do_truncate (struct reiser
 	p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME;
     }
     reiserfs_update_sd (th, p_s_inode);
+
+
+    if (update_timestamps)
+	remove_save_link (p_s_inode);
 
     pathrelse(&s_search_path) ;
 }
diff -rup linux-2.4.6/fs/reiserfs/super.c linux-2.4.6.cleanup/fs/reiserfs/super.c
--- linux-2.4.6/fs/reiserfs/super.c	Wed Jul 11 23:05:13 2001
+++ linux-2.4.6.cleanup/fs/reiserfs/super.c	Wed Jul 11 21:57:58 2001
@@ -664,6 +664,85 @@ int function2code (hashf_t func)
     return 0;
 }
 
+extern const struct key  MAX_KEY;
+
+/* look for uncompleted unlinks and truncates and complete them */
+static void finish_unfinished (struct super_block * s)
+{
+    INITIALIZE_PATH (path);
+    struct cpu_key save_link_key, key;
+    int retval;
+    struct item_head * ih;
+    struct buffer_head * bh;
+    int item_pos;
+    char * item;
+    int done;
+    struct inode * inode;
+
+
+    /* compose key to look for "save" links */
+    save_link_key.version = KEY_FORMAT_1;
+    save_link_key.on_disk_key = MAX_KEY;
+    save_link_key.key_length = 3;
+
+    done = 0;
+    while (1) {
+	retval = search_item (s, &save_link_key, &path);
+	if (retval != ITEM_NOT_FOUND) {
+	    reiserfs_warning ("vs-2140: finish_unfinished: search_by_key returned %d\n",
+			      retval);
+	    break;
+	}
+	
+	bh = get_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;
+
+	/* reiserfs_iget needs k_dirid and k_objectid only */
+	item = B_I_PITEM (bh, ih);
+	key.on_disk_key.k_dir_id = le32_to_cpu (*(__u32 *)item);
+	key.on_disk_key.k_objectid = le32_to_cpu (ih->ih_key.k_objectid);
+	pathrelse (&path);
+
+	inode = reiserfs_iget (s, &key);
+	if (!inode) {
+	    reiserfs_warning ("vs-2180: finish_unfinished: iget failed\n");
+	    break;
+	}
+
+	if (inode->i_nlink) {
+	    /* 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);
+	} else {
+	    /* 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);
+}
+
 //
 // a portion of this function, particularly the VFS interface portion,
 // was derived from minix or ext2's analog and evolved as the
@@ -803,11 +882,15 @@ struct super_block * reiserfs_read_super
 
 	journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
 	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))) {
+	if (strncmp (rs->s_magic,  reiser2fs_super_magic_string, 
+		     strlen ( reiser2fs_super_magic_string))) {
 	    reiserfs_warning("reiserfs: using 3.5.x disk format\n") ;
 	}
     }
@@ -844,6 +927,71 @@ struct super_block * reiserfs_read_super
 }
 
 
+/* 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)
+{
+    INITIALIZE_PATH (path);
+    int retval;
+    struct cpu_key key;
+    struct item_head ih;
+    __u32 link;
+
+
+    /* 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;
+    set_cpu_key_k_offset (&key, 1);
+    set_cpu_key_k_type (&key, TYPE_INDIRECT);  
+    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 returned %d\n", retval);
+	pathrelse (&path);
+	return;
+    }
+
+    /* compose "save" link itself */
+    make_le_item_head (&ih, &key, key.version, 1, TYPE_INDIRECT,
+		       4/*length*/, 0/*free space*/);
+    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);
+}
+
+
+/* this opens transaction unlike add_save_link */
+void remove_save_link (struct inode * inode)
+{
+    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;
+    set_le_key_k_offset (KEY_FORMAT_1, &key, 1);
+    set_le_key_k_type (KEY_FORMAT_1, &key, TYPE_INDIRECT);
+
+    reiserfs_delete_solid_item (&th, &key);
+
+    journal_end (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+}
+
 //
 // a portion of this function, particularly the VFS interface portion,
 // was derived from minix or ext2's analog and evolved as the
@@ -885,6 +1033,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-2.4.6/include/linux/reiserfs_fs.h linux-2.4.6.cleanup/include/linux/reiserfs_fs.h
--- linux-2.4.6/include/linux/reiserfs_fs.h	Wed Jul 11 23:05:13 2001
+++ linux-2.4.6.cleanup/include/linux/reiserfs_fs.h	Wed Jul 11 22:51:46 2001
@@ -252,6 +252,8 @@ struct unfm_nodeinfo {
 #define ITEM_VERSION_1 0
 #define ITEM_VERSION_2 1
 
+#define KEY_FORMAT_1 0
+#define KEY_FORMAT_2 1
 
 /* loff_t - long long */
 
@@ -1392,10 +1394,6 @@ extern struct item_operations * item_ops
 #define COMP_SHORT_KEYS comp_short_keys
 #define keys_of_same_object comp_short_keys
 
-/*#define COMP_KEYS(p_s_key1, p_s_key2)		comp_keys((unsigned long *)(p_s_key1), (unsigned long *)(p_s_key2))
-#define COMP_SHORT_KEYS(p_s_key1, p_s_key2)	comp_short_keys((unsigned long *)(p_s_key1), (unsigned long *)(p_s_key2))*/
-
-
 /* number of blocks pointed to by the indirect item */
 #define I_UNFM_NUM(p_s_ih)	( (p_s_ih)->ih_item_len / UNFM_P_SIZE )
 
@@ -1691,15 +1689,13 @@ int reiserfs_delete_item (struct reiserf
 			  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 *, 
 			   int update_timestamps);
-//
-//void lock_inode_to_convert (struct inode * p_s_inode);
-//void unlock_inode_after_convert (struct inode * p_s_inode);
-//void increment_i_read_sync_counter (struct inode * p_s_inode);
-//void decrement_i_read_sync_counter (struct inode * p_s_inode);
+
 
 
 #define block_size(inode) ((inode)->i_sb->s_blocksize)
@@ -1729,8 +1725,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, 
@@ -1738,7 +1732,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 */
@@ -1772,15 +1766,12 @@ int reiserfs_link (struct dentry * old_d
 int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir, struct dentry *new_dentry);
 
 /* super.c */
-inline void reiserfs_mark_buffer_dirty (struct buffer_head * bh, int flag);
-inline void reiserfs_mark_buffer_clean (struct buffer_head * bh);
 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);
-/*int read_super_block (struct super_block * s, int size);
-int read_bitmaps (struct super_block * s);
-int read_old_bitmaps (struct super_block * s);
-int read_old_super_block (struct super_block * s, int size);*/
+void add_save_link (struct reiserfs_transaction_handle * th,
+ 		    struct inode * inode);
+void remove_save_link (struct inode * inode);
 struct super_block * reiserfs_read_super (struct super_block * s, void * data, int silent);
 int reiserfs_statfs (struct super_block * s, struct statfs * buf);
 
