[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Whoopie! E2fsck failure -- caught in me wee trap



I have hacked together a version of ext2fs with a weird debugging hack to
try to catch inode corruption (not fix it, just notice it when it happens).
I have done some trivial stress-testing of a filesystem using this
debugging hack, and not hit any corruption or crashes so far.  But I
haven't tried any builds, which are everybody's test case for the ext2fs
bug.  Binaries (both static and dynamic, stripped and not) can be found in
http://www.ai.mit.edu/people/roland/hurd/, and I append the ext2fs source
patches for this debugging hack.

You should expect to see numerous messages like:

ext2fs: /dev/hd0s3e: warning: DEBUG: group 1: can't protect inodes [2016,2048) misaligned at 8323

when this ext2fs starts up.  Those are normal, but you should make a note
of them all (it's not a big deal if you don't; it can always be computed
later from the info printed out by dumpe2fs).  Hopefully, if this
filesystem tries to corrupt some inodes, it will crash in the attempt and
you can figure out where the bogus access came from.  If you run this
filesystem and it doesn't crash, but you later come across corrupted
inodes, notice whether the corrupted inodes fall in one of the ranges
mentioned in the debug warnings.  If they do, tough cookies and this
particular diagnostic hack just can't easily be made to catch those ones.

If you try this code, please tell me what happens--whatever happens.  If
you come across corrupted inodes that were *not* in a range mentioned in a
warning message, definitely tell me that too--that might tell us something
about the nature of the bug (or that this hack is not catching accesses the
way I expect it to at all).


Roland


Index: hyper.c
===================================================================
RCS file: /afs/sipb.mit.edu/project/hurddev/cvsroot/hurd/ext2fs/hyper.c,v
retrieving revision 1.29
diff -b -u -r1.29 hyper.c
--- hyper.c	1999/10/07 17:04:29	1.29
+++ hyper.c	1999/10/19 23:56:23
@@ -56,6 +56,7 @@
 
 static int ext2fs_clean;	/* fs clean before we started writing? */
 
+int inodes_per_page, *inode_prot_lock; /* XXX debugging hack */
 void
 get_hypermetadata (void)
 {
@@ -156,11 +157,42 @@
 
   allocate_mod_map ();
 
-  diskfs_end_catch_exception ();
-
   /* Cache a convenient pointer to the block group descriptors for allocation.
      These are stored in the filesystem blocks following the superblock.  */
   group_desc_image = (struct ext2_group_desc *) bptr (bptr_block (sblock) + 1);
+
+  diskfs_end_catch_exception ();
+
+  {
+    /* XXX debugging hack */
+    unsigned long inodes_per_group = sblock->s_inodes_per_group;
+    unsigned long gn;
+    for (gn = 0; gn < groups_count; ++gn) {
+      block_t block = group_desc(gn)->bg_inode_table;
+      uintptr_t page = bptr(block);
+      uintptr_t start = round_page(page), end = page+(inodes_per_group*
+						      EXT2_INODE_SIZE(sblock));
+      if (start!=page)
+	ext2_warning("DEBUG: group %lu: can't protect inodes [%u,%u) misaligned at %lu",
+		     gn,
+		     gn*inodes_per_group,
+		     gn*inodes_per_group+(start-page)/EXT2_INODE_SIZE(sblock),
+		     block);
+      if (trunc_page(end)!=end)
+	ext2_warning("DEBUG: group %lu: can't protect inodes [%u,%u) misaligned at %lu",
+		     gn,
+		     gn*inodes_per_group+(trunc_page(end)-start)/EXT2_INODE_SIZE(sblock),
+		     (gn+1)*inodes_per_group,
+		     block+(trunc_page(end)-start)/block_size);
+      if (mprotect ((void *)start,trunc_page(end)-start, PROT_READ))
+	ext2_warning ("mprotect: %m");
+    }
+    inodes_per_page = vm_page_size / EXT2_INODE_SIZE(sblock);
+    inode_prot_lock = calloc(sblock->s_inodes_count / inodes_per_page,
+			     sizeof(int));
+    assert(inode_prot_lock);
+    /* XXX debugging hack */
+  }
 
   /* A handy source of page-aligned zeros.  */
   zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ|PROT_WRITE,
Index: inode.c
===================================================================
RCS file: /afs/sipb.mit.edu/project/hurddev/cvsroot/hurd/ext2fs/inode.c,v
retrieving revision 1.50
diff -b -u -r1.50 inode.c
--- inode.c	1999/08/23 04:02:10	1.50
+++ inode.c	1999/10/19 23:57:04
@@ -388,6 +388,8 @@
     return 0;
 }
 
+int inodes_per_page, *inode_prot_lock; /* XXX debugging hack */
+
 /* Writes everything from NP's inode to the disk image, and returns a pointer
    to it, or NULL if nothing need be done.  */
 static struct ext2_inode *
@@ -395,7 +397,7 @@
 {
   error_t err;
   struct stat *st = &np->dn_stat;
-  struct ext2_inode *di = dino (np->cache_id);
+  struct ext2_inode *di = dino (np->cache_id); /* XXX reads disk_image! */
 
   if (np->dn->info.i_prealloc_count)
     ext2_discard_prealloc (np);
@@ -404,6 +406,7 @@
   if (np->dn_stat_dirty)
     {
       struct ext2_inode_info *info = &np->dn->info;
+      int prot;
 
       assert (!diskfs_readonly);
 
@@ -413,6 +416,22 @@
       if (err)
 	return NULL;
 
+      {
+	/* XXX debugging hack -- see hyper.c::get_hypermetadata  */
+	const unsigned int inum = np->cache_id;
+  unsigned long inodes_per_group = sblock->s_inodes_per_group;
+  unsigned long bg_num = (inum - 1) / inodes_per_group;
+  struct ext2_group_desc *bg = group_desc(bg_num);
+	void *page = (void*)trunc_page(di);
+	prot = (bptr_block(page) >= bg->bg_inode_table &&
+		bptr_block(page+vm_page_size) <= bg->bg_inode_table+inodes_per_group/inodes_per_block);
+	if (prot) {
+	  spin_lock(&inode_prot_lock[inum/inodes_per_page]);
+	  if (mprotect (page, vm_page_size, PROT_READ|PROT_WRITE))
+	    ext2_error ("write_node: mprotect: %m");
+	}
+      }
+
       di->i_generation = st->st_gen;
 
       /* We happen to know that the stat mode bits are the same
@@ -484,6 +503,12 @@
 	  for (block = 0; block < EXT2_N_BLOCKS; block++)
 	    di->i_block[block] = np->dn->info.i_data[block];
 	}
+
+      if (prot) {
+	if (mprotect (trunc_page(di), vm_page_size, PROT_READ))
+	  ext2_warning ("write_node: re-mprotect: %m");
+	spin_unlock(&inode_prot_lock[np->cache_id/inodes_per_page]);
+      }
 
       diskfs_end_catch_exception ();
       np->dn_stat_dirty = 0;


Reply to: