patch-2.3.7 linux/fs/ext2/inode.c

Next file: linux/fs/ext2/symlink.c
Previous file: linux/fs/ext2/fsync.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.6/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/mm.h>
+#include <linux/smp_lock.h>
 
 static int ext2_update_inode(struct inode * inode, int do_sync);
 
@@ -59,7 +60,7 @@
 	ext2_free_inode (inode);
 }
 
-#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
+#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)]))
 
 static inline int block_bmap (struct buffer_head * bh, int nr)
 {
@@ -92,13 +93,12 @@
 #endif
 }
 
-static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
 {
 #ifdef EXT2FS_DEBUG
 	static unsigned long alloc_hits = 0, alloc_attempts = 0;
 #endif
 	unsigned long result;
-	struct buffer_head * bh;
 
 	wait_on_super (inode->i_sb);
 
@@ -112,19 +112,6 @@
 		ext2_debug ("preallocation hit (%lu/%lu).\n",
 			    ++alloc_hits, ++alloc_attempts);
 
-		/* It doesn't matter if we block in getblk() since
-		   we have already atomically allocated the block, and
-		   are only clearing it now. */
-		if (!(bh = getblk (inode->i_sb->s_dev, result,
-				   inode->i_sb->s_blocksize))) {
-			ext2_error (inode->i_sb, "ext2_alloc_block",
-				    "cannot get block %lu", result);
-			return 0;
-		}
-		memset(bh->b_data, 0, inode->i_sb->s_blocksize);
-		mark_buffer_uptodate(bh, 1);
-		mark_buffer_dirty(bh, 1);
-		brelse (bh);
 	} else {
 		ext2_discard_prealloc (inode);
 		ext2_debug ("preallocation miss (%lu/%lu).\n",
@@ -139,13 +126,76 @@
 #else
 	result = ext2_new_block (inode, goal, 0, 0, err);
 #endif
-
 	return result;
 }
 
 
 int ext2_bmap (struct inode * inode, int block)
 {
+	int i, ret;
+	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+
+	ret = 0;
+	lock_kernel();
+	if (block < 0) {
+		ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
+		goto out;
+	}
+	if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
+		(1 << (addr_per_block_bits * 2)) +
+		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
+		ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
+		goto out;
+	}
+	if (block < EXT2_NDIR_BLOCKS) {
+		ret = inode_bmap (inode, block);
+		goto out;
+	}
+	block -= EXT2_NDIR_BLOCKS;
+	if (block < addr_per_block) {
+		i = inode_bmap (inode, EXT2_IND_BLOCK);
+		if (!i)
+			goto out;
+		ret = block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize), block);
+		goto out;
+	}
+	block -= addr_per_block;
+	if (block < (1 << (addr_per_block_bits * 2))) {
+		i = inode_bmap (inode, EXT2_DIND_BLOCK);
+		if (!i)
+			goto out;
+		i = block_bmap (bread (inode->i_dev, i,
+				       inode->i_sb->s_blocksize),
+				block >> addr_per_block_bits);
+		if (!i)
+			goto out;
+		ret = block_bmap (bread (inode->i_dev, i,
+					  inode->i_sb->s_blocksize),
+				block & (addr_per_block - 1));
+	}
+	block -= (1 << (addr_per_block_bits * 2));
+	i = inode_bmap (inode, EXT2_TIND_BLOCK);
+	if (!i)
+		goto out;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			block >> (addr_per_block_bits * 2));
+	if (!i)
+		goto out;
+	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			(block >> addr_per_block_bits) & (addr_per_block - 1));
+	if (!i)
+		goto out;
+	ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+			   block & (addr_per_block - 1));
+out:
+	unlock_kernel();
+	return ret;
+}
+
+int ext2_bmap_create (struct inode * inode, int block)
+{
 	int i;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
@@ -201,7 +251,8 @@
 }
 
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
-					  int create, int new_block, int * err)
+	int create, int new_block, int * err, int metadata,
+	int *phys_block, int *created)
 {
 	u32 * p;
 	int tmp, goal = 0;
@@ -210,13 +261,18 @@
 
 	p = inode->u.ext2_i.i_data + nr;
 repeat:
-	tmp = *p;
+	tmp = le32_to_cpu(*p);
 	if (tmp) {
-		struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-		if (tmp == *p)
-			return result;
-		brelse (result);
-		goto repeat;
+		if (metadata) {
+			struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+			if (tmp == le32_to_cpu(*p))
+				return result;
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
+			return NULL;
+		}
 	}
 	*err = -EFBIG;
 	if (!create)
@@ -244,7 +300,7 @@
 	if (!goal) {
 		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (inode->u.ext2_i.i_data[tmp]) {
-				goal = inode->u.ext2_i.i_data[tmp];
+				goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]);
 				break;
 			}
 		}
@@ -259,13 +315,28 @@
 	tmp = ext2_alloc_block (inode, goal, err);
 	if (!tmp)
 		return NULL;
-	result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
-	if (*p) {
-		ext2_free_blocks (inode, tmp, 1);
-		brelse (result);
-		goto repeat;
+	if (metadata) {
+		result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			brelse (result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, inode->i_sb->s_blocksize);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			goto repeat;
+		}
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
 	}
-	*p = tmp;
+	*p = cpu_to_le32(tmp);
+
 	inode->u.ext2_i.i_next_alloc_block = new_block;
 	inode->u.ext2_i.i_next_alloc_goal = tmp;
 	inode->i_ctime = CURRENT_TIME;
@@ -277,10 +348,17 @@
 	return result;
 }
 
+/*
+ *   metadata / data
+ *   possibly create / access
+ *   can fail due to: - not present
+ *                    - out of space
+ *
+ *   NULL return in the data case is mandatory.
+ */
 static struct buffer_head * block_getblk (struct inode * inode,
-					  struct buffer_head * bh, int nr,
-					  int create, int blocksize, 
-					  int new_block, int * err)
+	  struct buffer_head * bh, int nr, int create, int blocksize, 
+	  int new_block, int * err, int metadata, int *phys_block, int *created)
 {
 	int tmp, goal = 0;
 	u32 * p;
@@ -302,13 +380,19 @@
 repeat:
 	tmp = le32_to_cpu(*p);
 	if (tmp) {
-		result = getblk (bh->b_dev, tmp, blocksize);
-		if (tmp == le32_to_cpu(*p)) {
+		if (metadata) {
+			result = getblk (bh->b_dev, tmp, blocksize);
+			if (tmp == le32_to_cpu(*p)) {
+				brelse (bh);
+				return result;
+			}
+			brelse (result);
+			goto repeat;
+		} else {
+			*phys_block = tmp;
 			brelse (bh);
-			return result;
+			return NULL;
 		}
-		brelse (result);
-		goto repeat;
 	}
 	*err = -EFBIG;
 	if (!create) {
@@ -343,7 +427,22 @@
 		brelse (bh);
 		return NULL;
 	}
-	result = getblk (bh->b_dev, tmp, blocksize);
+	if (metadata) {
+		result = getblk (bh->b_dev, tmp, blocksize);
+		if (*p) {
+			ext2_free_blocks (inode, tmp, 1);
+			brelse (result);
+			goto repeat;
+		}
+		memset(result->b_data, 0, inode->i_sb->s_blocksize);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
+	} else {
+		*phys_block = tmp;
+		result = NULL;
+		*err = 0;
+		*created = 1;
+	}
 	if (le32_to_cpu(*p)) {
 		ext2_free_blocks (inode, tmp, 1);
 		brelse (result);
@@ -364,24 +463,27 @@
 	return result;
 }
 
-struct buffer_head * ext2_getblk (struct inode * inode, long block,
-				  int create, int * err)
+int ext2_getblk_block (struct inode * inode, long block,
+				  int create, int * err, int * created)
 {
-	struct buffer_head * bh;
+	struct buffer_head * bh, *tmp;
 	unsigned long b;
 	unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+	int phys_block, ret;
 
+	lock_kernel();
+	ret = 0;
 	*err = -EIO;
 	if (block < 0) {
 		ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
-		return NULL;
+		goto abort;
 	}
 	if (block > EXT2_NDIR_BLOCKS + addr_per_block +
 		(1 << (addr_per_block_bits * 2)) +
 		((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
 		ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
-		return NULL;
+		goto abort;
 	}
 	/*
 	 * If this is a sequential block allocation, set the next_alloc_block
@@ -398,32 +500,72 @@
 		inode->u.ext2_i.i_next_alloc_goal++;
 	}
 
-	*err = -ENOSPC;
+	*err = 0; // -ENOSPC;
 	b = block;
-	if (block < EXT2_NDIR_BLOCKS)
-		return inode_getblk (inode, block, create, b, err);
+	*created = 0;
+	if (block < EXT2_NDIR_BLOCKS) {
+		/*
+		 * data page.
+		 */
+		tmp = inode_getblk (inode, block, create, b,
+					err, 0, &phys_block, created);
+		goto out;
+	}
 	block -= EXT2_NDIR_BLOCKS;
 	if (block < addr_per_block) {
-		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err);
-		return block_getblk (inode, bh, block, create,
-				     inode->i_sb->s_blocksize, b, err);
+		bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL);
+		tmp = block_getblk (inode, bh, block, create,
+		     inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+		goto out;
 	}
 	block -= addr_per_block;
 	if (block < (1 << (addr_per_block_bits * 2))) {
-		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
+		bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL);
 		bh = block_getblk (inode, bh, block >> addr_per_block_bits,
-				   create, inode->i_sb->s_blocksize, b, err);
-		return block_getblk (inode, bh, block & (addr_per_block - 1),
-				     create, inode->i_sb->s_blocksize, b, err);
+				   create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL);
+		tmp = block_getblk (inode, bh, block & (addr_per_block - 1),
+		     create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+		goto out;
 	}
 	block -= (1 << (addr_per_block_bits * 2));
-	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
+	bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL);
 	bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
-			   create, inode->i_sb->s_blocksize, b, err);
-	bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
-			   create, inode->i_sb->s_blocksize, b, err);
-	return block_getblk (inode, bh, block & (addr_per_block - 1), create,
-			     inode->i_sb->s_blocksize, b, err);
+			   create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL);
+	bh = block_getblk (inode, bh, (block >> addr_per_block_bits) &
+		(addr_per_block - 1), create, inode->i_sb->s_blocksize,
+		b, err, 1, NULL,NULL);
+	tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create,
+		inode->i_sb->s_blocksize, b, err, 0, &phys_block, created);
+
+out:
+	if (!phys_block)
+		goto abort;
+	if (*err)
+		goto abort;
+	ret = phys_block;
+abort:
+	unlock_kernel();
+	return ret;
+}
+
+struct buffer_head * ext2_getblk (struct inode * inode, long block,
+				  int create, int * err)
+{
+	struct buffer_head *tmp = NULL;
+	int phys_block;
+	int created;
+
+	phys_block = ext2_getblk_block (inode, block, create, err, &created);
+
+	if (phys_block) {
+		tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize);
+		if (created) {
+			memset(tmp->b_data, 0, inode->i_sb->s_blocksize);
+			mark_buffer_uptodate(tmp, 1);
+			mark_buffer_dirty(tmp, 1);
+		}
+	}
+	return tmp;
 }
 
 struct buffer_head * ext2_bread (struct inode * inode, int block, 
@@ -569,11 +711,14 @@
 	if (inode->u.ext2_i.i_prealloc_count)
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "New inode has non-zero prealloc count!");
-	if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
-	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]);
+
+	/*
+	 * NOTE! The in-memory inode i_blocks array is in little-endian order
+	 * even on big-endian machines: we do NOT byteswap the block numbers!
+	 */
+	for (block = 0; block < EXT2_N_BLOCKS; block++)
+		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+
 	if (inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		/* Nothing to do */ ;
@@ -689,11 +834,8 @@
 	raw_inode->i_generation = cpu_to_le32(inode->i_generation);
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
-	else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
-		for (block = 0; block < EXT2_N_BLOCKS; block++)
-			raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]);
+		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	mark_buffer_dirty(bh, 1);
 	if (do_sync) {
 		ll_rw_block (WRITE, 1, &bh);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)