patch-2.2.18 linux/fs/ext2/inode.c
Next file: linux/fs/ext2/ioctl.c
Previous file: linux/fs/ext2/ialloc.c
Back to the patch index
Back to the overall index
- Lines: 340
- Date:
Sun Oct 15 21:15:17 2000
- Orig file:
v2.2.17/fs/ext2/inode.c
- Orig date:
Fri Apr 21 12:46:42 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/fs/ext2/inode.c linux/fs/ext2/inode.c
@@ -47,14 +47,14 @@
*/
void ext2_delete_inode (struct inode * inode)
{
- if (inode->i_ino == EXT2_ACL_IDX_INO ||
+ if (is_bad_inode(inode) ||
+ inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
return;
inode->u.ext2_i.i_dtime = CURRENT_TIME;
- /* When we delete an inode, we increment its i_version. If it
- is ever read in from disk again, it will have a different
- i_version. */
- inode->u.ext2_i.i_version++;
+ /* When we delete an inode, we increment its i_generation.
+ If it is read in from disk again, the generation will differ. */
+ inode->i_generation++;
mark_inode_dirty(inode);
ext2_update_inode(inode, IS_SYNC(inode));
inode->i_size = 0;
@@ -63,18 +63,7 @@
ext2_free_inode (inode);
}
-#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
-
-static inline int block_bmap (struct buffer_head * bh, int nr)
-{
- int tmp;
-
- if (!bh)
- return 0;
- tmp = le32_to_cpu(((u32 *) bh->b_data)[nr]);
- brelse (bh);
- return tmp;
-}
+#define inode_bmap(inode, nr) le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])
/*
* ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
@@ -149,61 +138,90 @@
return result;
}
+/**
+ * ext2_block_to_path - parse the block number into array of offsets
+ * @inode: inode in question (we are only interested in its superblock)
+ * @i_block: block number to be parsed
+ * @offsets: array to store the offsets in
+ *
+ * To store the locations of file's data ext2 uses a data structure common
+ * for UNIX filesystems - tree of pointers anchored in the inode, with
+ * data blocks at leaves and indirect blocks in intermediate nodes.
+ * This function translates the block number into path in that tree -
+ * return value is the path length and @offsets[n] is the offset of
+ * pointer to (n+1)th node in the nth one. If @block is out of range
+ * (negative or too large) warning is printed and zero returned.
+ *
+ * Note: function doesn't find node addresses, so no IO is needed. All
+ * we need to know is the capacity of indirect blocks (taken from the
+ * inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext2_block_to_path(struct inode *inode, long i_block, int offsets[4])
+{
+ int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ const long direct_blocks = EXT2_NDIR_BLOCKS,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+
+ if (i_block < 0) {
+ ext2_warning (inode->i_sb, "ext2_block_to_path", "block < 0");
+ } else if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ } else if ( (i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = EXT2_IND_BLOCK;
+ offsets[n++] = i_block;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = EXT2_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = EXT2_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ } else {
+ ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");
+ }
+ return n;
+}
+
int ext2_bmap (struct inode * inode, int block)
{
+ int offsets[4], *p;
+ int depth = ext2_block_to_path(inode, block, offsets);
+ struct buffer_head *bh;
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);
- if (block < 0) {
- ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
- return 0;
- }
- 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");
- return 0;
- }
- if (block < EXT2_NDIR_BLOCKS)
- return inode_bmap (inode, block);
- block -= EXT2_NDIR_BLOCKS;
- if (block < addr_per_block) {
- i = inode_bmap (inode, EXT2_IND_BLOCK);
+ if (depth == 0)
+ goto fail;
+ i = inode_bmap (inode, *(p=offsets));
+ while (--depth) {
if (!i)
- return 0;
- return block_bmap (bread (inode->i_dev, i,
- inode->i_sb->s_blocksize), block);
+ break;
+ bh = bread (inode->i_dev, i, inode->i_sb->s_blocksize);
+ if (!bh)
+ goto fail;
+ ++p;
+ i = le32_to_cpu(((u32 *) bh->b_data)[*p]);
+ brelse (bh);
}
- block -= addr_per_block;
- if (block < (1 << (addr_per_block_bits * 2))) {
- i = inode_bmap (inode, EXT2_DIND_BLOCK);
- if (!i)
- return 0;
- i = block_bmap (bread (inode->i_dev, i,
- inode->i_sb->s_blocksize),
- block >> addr_per_block_bits);
- if (!i)
- return 0;
- return 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)
- return 0;
- i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
- block >> (addr_per_block_bits * 2));
- if (!i)
- return 0;
- i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
- (block >> addr_per_block_bits) & (addr_per_block - 1));
- if (!i)
- return 0;
- return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
- block & (addr_per_block - 1));
+ return i;
+fail:
+ return 0;
}
static struct buffer_head * inode_getblk (struct inode * inode, int nr,
@@ -218,7 +236,8 @@
repeat:
tmp = *p;
if (tmp) {
- struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ struct buffer_head * result;
+ result = getblk (inode->i_dev, le32_to_cpu(tmp), inode->i_sb->s_blocksize);
if (tmp == *p)
return result;
brelse (result);
@@ -236,7 +255,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;
}
}
@@ -257,7 +276,7 @@
brelse (result);
goto repeat;
}
- *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;
@@ -349,21 +368,12 @@
int create, int * err)
{
struct buffer_head * bh;
- 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 offsets[4], *p;
+ int depth = ext2_block_to_path(inode, block, offsets);
*err = -EIO;
- if (block < 0) {
- ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
- return NULL;
- }
- 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;
- }
+ if (depth == 0)
+ goto fail;
/*
* If this is a sequential block allocation, set the next_alloc_block
* to this block now so that all the indblock and data block
@@ -380,31 +390,14 @@
}
*err = -ENOSPC;
- b = block;
- if (block < EXT2_NDIR_BLOCKS)
- return inode_getblk (inode, block, create, b, err);
- 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);
- }
- block -= addr_per_block;
- if (block < (1 << (addr_per_block_bits * 2))) {
- bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
- 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);
- }
- block -= (1 << (addr_per_block_bits * 2));
- bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
- 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);
+ bh = inode_getblk (inode, *(p=offsets), create, block, err);
+ while (--depth) {
+ bh = block_getblk (inode, bh, *++p, create,
+ inode->i_sb->s_blocksize, block, err);
+ }
+ return bh;
+fail:
+ return NULL;
}
struct buffer_head * ext2_bread (struct inode * inode, int block,
@@ -519,9 +512,20 @@
inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ /* We now have enough fields to check if the inode was active or not.
+ * This is needed because nfsd might try to access dead inodes
+ * the test is that same one that e2fsck uses
+ * NeilBrown 1999oct15
+ */
+ if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+ /* this inode is deleted */
+ brelse (bh);
+ goto bad_inode;
+ }
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
inode->i_version = ++global_event;
+ inode->i_generation = le32_to_cpu(raw_inode->i_generation);
inode->u.ext2_i.i_new_inode = 0;
inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
@@ -543,8 +547,6 @@
<< 32;
#endif
}
- inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
- inode->i_generation = inode->u.ext2_i.i_version;
inode->u.ext2_i.i_block_group = block_group;
inode->u.ext2_i.i_next_alloc_block = 0;
inode->u.ext2_i.i_next_alloc_goal = 0;
@@ -553,11 +555,8 @@
"New inode has non-zero prealloc count!");
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
inode->i_rdev = to_kdev_t(le32_to_cpu(raw_inode->i_block[0]));
- else 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]);
+ inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
brelse (bh);
inode->i_op = NULL;
if (inode->i_ino == EXT2_ACL_IDX_INO ||
@@ -658,6 +657,7 @@
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+ raw_inode->i_generation = cpu_to_le32(inode->i_generation);
raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
@@ -674,14 +674,10 @@
raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
#endif
}
- raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
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)