EXT2FS patch: IO error handling

Stephen C. Tweedie (sct@dcs.ed.ac.uk)
31 May 1997 23:03:00 +0100


Hi all,

The patch below (against 2.0.30) hopefully fixes up the known problems
in ext2fs when we encounter bad blocks on the disk. I think I've
addressed all of the cases where we used to ext2_panic() on IO error.
The new code should just do an ext2_error() instead, and return the
-EIO up to the calling functions. ext2_error() will do the right
thing about deciding whether to panic, continue or do a read-only
remount.

I've tested this out two ways. First of all, the easiest way to
introduce massive numbers of IO errors is to mount /dev/fd0 and then
remove the floppy. The new kernel survives any amount of this.

The second thing I've done is to create a simple ramdisk driver, the
flawd, which simulates a flawed disk but under full user control. You
can set and clear IO error flags on the ramdisk device on a per-sector
basis using an attached flawctl command. I'm posting the flawd stuff
in a separate mail.

Anyway, I've had a 1MB flawd device running for a while now with
various combinations of block bitmap, inode bitmap and inode table
errors being injected and it seems to cope correctly. Errors in the
group descriptors or superblock aren't relevant: those are only ever
read during mount and are held in memory thereafter, so we never
panic on failure to read them.

I'll do a 2.1 patch shortly, but for now I'd like to get this thing
tested for possible inclusion in 2.0. The fact that a single bad disk
block can cause a full-blown kernel panic is bad behaviour at least,
and there's good reason to call it a real bug in the current 2.0
kernels.

Cheers,
Stephen.

----------------------------------------------------------------
--- linux-2.0/fs/Makefile.~1~ Wed May 8 16:28:01 1996
+++ linux-2.0/fs/Makefile Sat May 10 15:38:02 1997
@@ -13,7 +13,7 @@
O_OBJS = open.o read_write.o inode.o devices.o file_table.o buffer.o \
super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
- dcache.o $(BINFMTS)
+ dcache.o bad_inode.o $(BINFMTS)

MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
--- linux-2.0/fs/bad_inode.c.~1~ Sat May 10 16:14:47 1997
+++ linux-2.0/fs/bad_inode.c Sat May 10 16:08:47 1997
@@ -0,0 +1,77 @@
+/*
+ * linux/fs/bad_inode.c
+ *
+ * Copyright (C) 1997, Stephen Tweedie
+ *
+ * Provide stub functions for unreadable inodes
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+static int return_EIO()
+{
+ return -EIO;
+}
+
+#define EIO_ERROR ((void *) (return_EIO))
+
+static struct file_operations bad_file_ops =
+{
+ EIO_ERROR, /* lseek */
+ EIO_ERROR, /* read */
+ EIO_ERROR, /* write */
+ EIO_ERROR, /* readdir */
+ EIO_ERROR, /* select */
+ EIO_ERROR, /* ioctl */
+ EIO_ERROR, /* mmap */
+ EIO_ERROR, /* open */
+ EIO_ERROR, /* release */
+ EIO_ERROR, /* fsync */
+ EIO_ERROR, /* fasync */
+ EIO_ERROR, /* check_media_change */
+ EIO_ERROR /* revalidate */
+};
+
+static struct inode_operations bad_inode_ops =
+{
+ &bad_file_ops, /* default file operations */
+ EIO_ERROR, /* create */
+ EIO_ERROR, /* lookup */
+ EIO_ERROR, /* link */
+ EIO_ERROR, /* unlink */
+ EIO_ERROR, /* symlink */
+ EIO_ERROR, /* mkdir */
+ EIO_ERROR, /* rmdir */
+ EIO_ERROR, /* mknod */
+ EIO_ERROR, /* rename */
+ EIO_ERROR, /* readlink */
+ EIO_ERROR, /* follow_link */
+ EIO_ERROR, /* readpage */
+ EIO_ERROR, /* writepage */
+ EIO_ERROR, /* bmap */
+ EIO_ERROR, /* truncate */
+ EIO_ERROR, /* permission */
+ EIO_ERROR /* smap */
+};
+
+
+/*
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_bad_inode() to return a
+ * set of stubs which will return EIO errors as required.
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+void make_bad_inode(struct inode * inode)
+{
+ inode->i_mode = S_IFREG;
+ inode->i_flags = S_BAD_INODE;
+ inode->i_atime = CURRENT_TIME;
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_op = &bad_inode_ops;
+}
+
--- linux-2.0/fs/ext2/balloc.c.~1~ Thu Jan 4 12:14:20 1996
+++ linux-2.0/fs/ext2/balloc.c Sat May 31 12:19:26 1997
@@ -43,6 +43,10 @@
unsigned long desc;
struct ext2_group_desc * gdp;

+ /*
+ * This panic should never trigger on a bad filesystem: the caller
+ * should have verified the block/group number already.
+ */
if (block_group >= sb->u.ext2_sb.s_groups_count)
ext2_panic (sb, "get_group_desc",
"block_group >= groups_count - "
@@ -63,22 +67,37 @@
return gdp + desc;
}

-static void read_block_bitmap (struct super_block * sb,
- unsigned int block_group,
- unsigned long bitmap_nr)
+/*
+ * Read the bitmap for a given block_group, reading into the specified
+ * slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
+ unsigned int block_group,
+ unsigned long bitmap_nr)
{
struct ext2_group_desc * gdp;
struct buffer_head * bh;
+ int retval = 0;

gdp = get_group_desc (sb, block_group, NULL);
bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize);
- if (!bh)
- ext2_panic (sb, "read_block_bitmap",
+ if (!bh) {
+ ext2_error (sb, "read_block_bitmap",
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %lu",
block_group, (unsigned long) gdp->bg_block_bitmap);
+ retval = -EIO;
+ }
+ /*
+ * On IO error, just leave a zero in the superblock's block pointer for
+ * this group. The IO will be retried next time.
+ */
sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
+ return retval;
}

/*
@@ -91,11 +110,13 @@
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
*/
static int load__block_bitmap (struct super_block * sb,
unsigned int block_group)
{
- int i, j;
+ int i, j, retval = 0;
unsigned long block_bitmap_number;
struct buffer_head * block_bitmap;

@@ -114,7 +135,9 @@
else
return block_group;
} else {
- read_block_bitmap (sb, block_group, block_group);
+ retval = read_block_bitmap (sb, block_group, block_group);
+ if (retval < 0)
+ return retval;
return block_group;
}
}
@@ -134,6 +157,14 @@
}
sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
+
+ /*
+ * There's still one special case here --- if block_bitmap == 0
+ * then our last attempt to read the bitmap failed and we have
+ * just ended up caching that failure. Try again to read it.
+ */
+ if (!block_bitmap)
+ retval = read_block_bitmap (sb, block_group, 0);
} else {
if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
sb->u.ext2_sb.s_loaded_block_bitmaps++;
@@ -145,24 +176,71 @@
sb->u.ext2_sb.s_block_bitmap[j] =
sb->u.ext2_sb.s_block_bitmap[j - 1];
}
- read_block_bitmap (sb, block_group, 0);
+ retval = read_block_bitmap (sb, block_group, 0);
}
- return 0;
+ return retval;
}

+/*
+ * Load the block bitmap for a given block group. First of all do a couple
+ * of fast lookups for common cases and then pass the request onto the guts
+ * of the bitmap loader.
+ *
+ * Return the slot number of the group in the superblock bitmap cache's on
+ * success, or a -ve error code.
+ *
+ * There is still one inconsistancy here --- if the number of groups in this
+ * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of
+ * differentiating between a group for which we have never performed a bitmap
+ * IO request, and a group for which the last bitmap read request failed.
+ */
static inline int load_block_bitmap (struct super_block * sb,
unsigned int block_group)
{
- if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
- sb->u.ext2_sb.s_block_bitmap_number[0] == block_group)
- return 0;
+ int slot;

- if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED &&
- sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
- sb->u.ext2_sb.s_block_bitmap[block_group])
- return block_group;
+ /*
+ * Do the lookup for the slot. First of all, check if we're asking
+ * for the same slot as last time, and did we succeed that last time?
+ */
+ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
+ sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
+ sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ slot = 0;
+ }
+ /*
+ * Or can we do a fast lookup based on a loaded group on a filesystem
+ * small enough to be mapped directly into the superblock?
+ */
+ else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED &&
+ sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
+ sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ slot = block_group;
+ }
+ /*
+ * If not, then do a full lookup for this block group.
+ */
+ else {
+ slot = load__block_bitmap (sb, block_group);
+ }

- return load__block_bitmap (sb, block_group);
+ /*
+ * <0 means we just got an error
+ */
+ if (slot < 0)
+ return slot;
+
+ /*
+ * If it's a valid slot, we may still have cached a previous IO error,
+ * in which case the bh in the superblock cache will be zero.
+ */
+ if (!sb->u.ext2_sb.s_block_bitmap[slot])
+ return -EIO;
+
+ /*
+ * Must have been read in OK to get this far.
+ */
+ return slot;
}

void ext2_free_blocks (const struct inode * inode, unsigned long block,
@@ -199,12 +277,19 @@
block_group = (block - es->s_first_data_block) /
EXT2_BLOCKS_PER_GROUP(sb);
bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
- if (bit + count > EXT2_BLOCKS_PER_GROUP(sb))
- ext2_panic (sb, "ext2_free_blocks",
+ if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
+ ext2_error (sb, "ext2_free_blocks",
"Freeing blocks across group boundary - "
"Block = %lu, count = %lu",
block, count);
+ unlock_super (sb);
+ return;
+ }
+
bitmap_nr = load_block_bitmap (sb, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
gdp = get_group_desc (sb, block_group, &bh2);

@@ -214,11 +299,14 @@
in_range (block, gdp->bg_inode_table,
sb->u.ext2_sb.s_itb_per_group) ||
in_range (block + count - 1, gdp->bg_inode_table,
- sb->u.ext2_sb.s_itb_per_group)))
- ext2_panic (sb, "ext2_free_blocks",
+ sb->u.ext2_sb.s_itb_per_group))) {
+ ext2_error (sb, "ext2_free_blocks",
"Freeing blocks in system zones - "
"Block = %lu, count = %lu",
block, count);
+ unlock_super (sb);
+ return;
+ }

for (i = 0; i < count; i++) {
if (!clear_bit (bit + i, bh->b_data))
@@ -242,6 +330,7 @@
wait_on_buffer (bh);
}
sb->s_dirt = 1;
+error_return:
unlock_super (sb);
return;
}
@@ -301,6 +390,12 @@
goal_attempts++;
#endif
bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0) {
+ *err = -EIO;
+ unlock_super (sb);
+ return 0;
+ }
+
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];

ext2_debug ("goal is at %d:%d.\n", i, j);
@@ -372,7 +467,14 @@
unlock_super (sb);
return 0;
}
+
bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0) {
+ *err = -EIO;
+ unlock_super (sb);
+ return 0;
+ }
+
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
j = (r - bh->b_data) << 3;
@@ -415,10 +517,14 @@
if (test_opt (sb, CHECK_STRICT) &&
(tmp == gdp->bg_block_bitmap ||
tmp == gdp->bg_inode_bitmap ||
- in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group)))
- ext2_panic (sb, "ext2_new_block",
+ in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group))) {
+ ext2_error (sb, "ext2_new_block",
"Allocating block in system zone - "
"block = %u", tmp);
+ unlock_super (sb);
+ *err = -EIO;
+ return 0;
+ }

if (set_bit (j, bh->b_data)) {
ext2_warning (sb, "ext2_new_block",
@@ -511,7 +617,11 @@
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
desc_count += gdp->bg_free_blocks_count;
+
bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
sb->s_blocksize);
printk ("group %d: stored = %d, counted = %lu\n",
@@ -556,6 +666,9 @@
gdp = get_group_desc (sb, i, NULL);
desc_count += gdp->bg_free_blocks_count;
bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];

if (!test_bit (0, bh->b_data))
--- linux-2.0/fs/ext2/ialloc.c.~1~ Sat May 4 08:06:18 1996
+++ linux-2.0/fs/ext2/ialloc.c Fri May 30 23:59:52 1997
@@ -62,22 +62,37 @@
return gdp + desc;
}

-static void read_inode_bitmap (struct super_block * sb,
- unsigned long block_group,
- unsigned int bitmap_nr)
+/*
+ * Read the inode allocation bitmap for a given block_group, reading
+ * into the specified slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_inode_bitmap (struct super_block * sb,
+ unsigned long block_group,
+ unsigned int bitmap_nr)
{
struct ext2_group_desc * gdp;
struct buffer_head * bh;
+ int retval = 0;

gdp = get_group_desc (sb, block_group, NULL);
bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize);
- if (!bh)
- ext2_panic (sb, "read_inode_bitmap",
+ if (!bh) {
+ ext2_error (sb, "read_inode_bitmap",
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %lu",
block_group, (unsigned long) gdp->bg_inode_bitmap);
+ retval = -EIO;
+ }
+ /*
+ * On IO error, just leave a zero in the superblock's block pointer for
+ * this group. The IO will be retried next time.
+ */
sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
+ return retval;
}

/*
@@ -90,11 +105,13 @@
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
*/
static int load_inode_bitmap (struct super_block * sb,
unsigned int block_group)
{
- int i, j;
+ int i, j, retval = 0;
unsigned long inode_bitmap_number;
struct buffer_head * inode_bitmap;

@@ -114,7 +131,9 @@
else
return block_group;
} else {
- read_inode_bitmap (sb, block_group, block_group);
+ retval = read_inode_bitmap (sb, block_group, block_group);
+ if (retval < 0)
+ return retval;
return block_group;
}
}
@@ -135,6 +154,14 @@
}
sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap;
+
+ /*
+ * There's still one special case here --- if inode_bitmap == 0
+ * then our last attempt to read the bitmap failed and we have
+ * just ended up caching that failure. Try again to read it.
+ */
+ if (!inode_bitmap)
+ retval = read_inode_bitmap (sb, block_group, 0);
} else {
if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
sb->u.ext2_sb.s_loaded_inode_bitmaps++;
@@ -146,9 +173,9 @@
sb->u.ext2_sb.s_inode_bitmap[j] =
sb->u.ext2_sb.s_inode_bitmap[j - 1];
}
- read_inode_bitmap (sb, block_group, 0);
+ retval = read_inode_bitmap (sb, block_group, 0);
}
- return 0;
+ return retval;
}

void ext2_free_inode (struct inode * inode)
@@ -198,6 +225,11 @@
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
+ if (bitmap_nr < 0) {
+ unlock_super (sb);
+ return;
+ }
+
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
if (!clear_bit (bit, bh->b_data))
ext2_warning (sb, "ext2_free_inode",
@@ -352,6 +384,13 @@
return NULL;
}
bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0) {
+ unlock_super (sb);
+ iput(inode);
+ *err = -EIO;
+ return NULL;
+ }
+
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_INODES_PER_GROUP(sb))) <
@@ -465,6 +504,9 @@
gdp = get_group_desc (sb, i, NULL);
desc_count += gdp->bg_free_inodes_count;
bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
printk ("group %d: stored = %d, counted = %lu\n",
@@ -497,6 +539,9 @@
gdp = get_group_desc (sb, i, NULL);
desc_count += gdp->bg_free_inodes_count;
bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
if (gdp->bg_free_inodes_count != x)
--- linux-2.0/fs/ext2/inode.c.~1~ Sun Apr 13 22:00:30 1997
+++ linux-2.0/fs/ext2/inode.c Fri May 30 22:16:49 1997
@@ -432,9 +432,12 @@
group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
- if (!bh)
- ext2_panic (inode->i_sb, "ext2_read_inode",
+ if (!bh) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
"Descriptor not loaded");
+ goto bad_inode;
+ }
+
gdp = (struct ext2_group_desc *) bh->b_data;
/*
* Figure out the offset within the block group inode table
@@ -443,10 +446,13 @@
EXT2_INODE_SIZE(inode->i_sb);
block = gdp[desc].bg_inode_table +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
- if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
- ext2_panic (inode->i_sb, "ext2_read_inode",
- "unable to read i-node block - "
+ if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
+ "unable to read inode block - "
"inode=%lu, block=%lu", inode->i_ino, block);
+ goto bad_inode;
+ }
+
offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
raw_inode = (struct ext2_inode *) (bh->b_data + offset);

@@ -504,6 +510,11 @@
inode->i_flags |= S_APPEND;
if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ return;
}

static int ext2_update_inode(struct inode * inode, int do_sync)
@@ -543,10 +554,23 @@
EXT2_INODE_SIZE(inode->i_sb);
block = gdp[desc].bg_inode_table +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
- if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
- ext2_panic (inode->i_sb, "ext2_write_inode",
- "unable to read i-node block - "
+ if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+ ext2_error (inode->i_sb, "ext2_write_inode",
+ "unable to read inode block - "
"inode=%lu, block=%lu", inode->i_ino, block);
+ /*
+ * Unfortunately we're in a lose-lose situation. I think that
+ * keeping the inode in-core with the dirty bit set is
+ * the worse option, since that will soak up inodes until
+ * the end of the world. Clearing the dirty bit is nasty if
+ * we haven't succeeded in writing out, but it's less nasty
+ * than the alternative. -- sct
+ */
+ inode->i_dirt = 0;
+
+ return -EIO;
+ }
+
offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
raw_inode = (struct ext2_inode *) (bh->b_data + offset);

@@ -577,10 +601,11 @@
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
- printk ("IO error syncing ext2 inode ["
- "%s:%08lx]\n",
- kdevname(inode->i_dev), inode->i_ino);
- err = -1;
+ ext2_error (inode->i_sb,
+ "IO error syncing ext2 inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ err = -EIO;
}
}
brelse (bh);
--- linux-2.0/fs/namei.c.~1~ Sun Apr 13 22:00:37 1997
+++ linux-2.0/fs/namei.c Sat May 10 15:54:03 1997
@@ -282,6 +282,10 @@
return error;
} else
iput(base);
+ if ((inode->i_flags & S_BAD_INODE) != 0) {
+ iput(inode);
+ return -EIO;
+ }
*res_inode = inode;
return 0;
}
--- linux-2.0/include/linux/fs.h.~1~ Sun Apr 13 22:03:16 1997
+++ linux-2.0/include/linux/fs.h Sat May 10 15:55:17 1997
@@ -72,6 +72,7 @@
#define S_WRITE 128 /* Write on file/directory/symlink */
#define S_APPEND 256 /* Append-only file */
#define S_IMMUTABLE 512 /* Immutable file */
+#define S_BAD_INODE 1024 /* Placeholder for unreadable inodes */

/*
* Flags that can be altered by MS_REMOUNT
@@ -621,6 +622,7 @@
extern void insert_inode_hash(struct inode *);
extern void clear_inode(struct inode *);
extern struct inode * get_pipe_inode(void);
+extern void make_bad_inode(struct inode *);
extern int get_unused_fd(void);
extern void put_unused_fd(int);
extern struct file * get_empty_filp(void);