patch-2.1.46 linux/fs/ext2/ialloc.c

Next file: linux/fs/file_table.c
Previous file: linux/fs/exec.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.45/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
@@ -154,8 +154,26 @@
 	return 0;
 }
 
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
 void ext2_free_inode (struct inode * inode)
 {
+	int is_directory;
+	unsigned long ino;
 	struct super_block * sb;
 	struct buffer_head * bh;
 	struct buffer_head * bh2;
@@ -185,30 +203,40 @@
 		return;
 	}
 
-	ext2_debug ("freeing inode %lu\n", inode->i_ino);
+	ino = inode->i_ino;
+	ext2_debug ("freeing inode %lu\n", ino);
 
 	sb = inode->i_sb;
 	lock_super (sb);
-	if (inode->i_ino < EXT2_FIRST_INO(sb) ||
-	    inode->i_ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
+	if (ino < EXT2_FIRST_INO(sb) ||
+	    ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
 		ext2_error (sb, "free_inode",
 			    "reserved inode or nonexistent inode");
 		unlock_super (sb);
 		return;
 	}
 	es = sb->u.ext2_sb.s_es;
-	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
-	bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
+	block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
+	bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
 	bitmap_nr = load_inode_bitmap (sb, block_group);
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
+
+	is_directory = S_ISDIR(inode->i_mode);
+
+	/* Do this BEFORE marking the inode not in use */
+	if (sb->dq_op)
+		sb->dq_op->free_inode (inode, 1);
+	clear_inode (inode);
+
+	/* Ok, now we can actually update the inode bitmaps.. */
 	if (!ext2_clear_bit (bit, bh->b_data))
 		ext2_warning (sb, "ext2_free_inode",
-			      "bit already cleared for inode %lu", inode->i_ino);
+			      "bit already cleared for inode %lu", ino);
 	else {
 		gdp = get_group_desc (sb, block_group, &bh2);
 		gdp->bg_free_inodes_count =
 			cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1);
-		if (S_ISDIR(inode->i_mode))
+		if (is_directory)
 			gdp->bg_used_dirs_count =
 				cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1);
 		mark_buffer_dirty(bh2, 1);
@@ -221,10 +249,7 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
-	if (sb->dq_op)
-		sb->dq_op->free_inode (inode, 1);
 	sb->s_dirt = 1;
-	clear_inode (inode);
 	unlock_super (sb);
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov