Add test/debugging code to ext4fixup

Add debugging test code to specify where to bail partway through
Add a script to drive the test code for automated testing.  This
found 4 bugs!

Change-Id: I14dc8b1e2c9d2d2f332346958d488feaf768d396
diff --git a/ext4_utils/ext4fixup.c b/ext4_utils/ext4fixup.c
index cb59637..5e04602 100644
--- a/ext4_utils/ext4fixup.c
+++ b/ext4_utils/ext4fixup.c
@@ -56,6 +56,12 @@
 #define STATE_UPDATING_INUMS 2
 #define STATE_UPDATING_SB    3
 
+/* Used for automated testing of this programs ability to stop and be restarted wthout error */
+static int bail_phase = 0;
+static int bail_loc = 0;
+static int bail_count = 0;
+static int count = 0;
+
 /* global flags */
 static int verbose = 0;
 static int no_write = 0;
@@ -432,6 +438,9 @@
                     critical_error("failed to write all of block group descriptors");
             }
         }
+        if ((bail_phase == 4) && ((unsigned int)bail_count == i)) {
+            critical_error("bailing at phase 4\n");
+        }
     }
 
     return 0;
@@ -663,6 +672,7 @@
 
     dirp = (struct ext4_dir_entry_2 *)dirbuf;
     while (dirp < (struct ext4_dir_entry_2 *)(dirbuf + dirsize)) {
+        count++;
         leftover_space = (char *)(dirbuf + dirsize) - (char *)dirp;
         if (((mode == SANITY_CHECK_PASS) || (mode == UPDATE_INODE_NUMS)) &&
             (leftover_space <= 8) && prev_dirp) {
@@ -743,6 +753,10 @@
             }
         }
 
+        if ((bail_phase == mode) && (bail_loc == 1) && (bail_count == count)) {
+            critical_error("Bailing at phase %d, loc 1 and count %d\n", mode, count);
+        }
+
         /* Point dirp at the next entry */
         prev_dirp = dirp;
         dirp = (struct ext4_dir_entry_2*)((char *)dirp + dirp->rec_len);
@@ -751,6 +765,9 @@
     /* Write out all the blocks for this directory */
     for (i = 0; i < num_blocks; i++) {
         write_block(fd, block_list[i], dirbuf + (i * info.block_size));
+        if ((bail_phase == mode) && (bail_loc == 2) && (bail_count <= count)) {
+            critical_error("Bailing at phase %d, loc 2 and count %d\n", mode, count);
+        }
     }
 
     free(block_list);
@@ -760,10 +777,11 @@
 
 int ext4fixup(char *fsdev)
 {
-    return ext4fixup_internal(fsdev, 0, 0);
+    return ext4fixup_internal(fsdev, 0, 0, 0, 0, 0);
 }
 
-int ext4fixup_internal(char *fsdev, int v_flag, int n_flag)
+int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
+                       int stop_phase, int stop_loc, int stop_count)
 {
     int fd;
     struct ext4_inode root_inode;
@@ -776,6 +794,10 @@
     verbose = v_flag;
     no_write = n_flag;
 
+    bail_phase = stop_phase;
+    bail_loc = stop_loc;
+    bail_count = stop_count;
+
     fd = open(fsdev, O_RDWR);
 
     if (fd < 0)
@@ -845,12 +867,14 @@
     }
 
     if (get_fs_fixup_state(fd) == STATE_MARKING_INUMS) {
+        count = 0; /* Reset debugging counter */
         if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, MARK_INODE_NUMS)) {
             set_fs_fixup_state(fd, STATE_UPDATING_INUMS);
         }
     }
 
     if (get_fs_fixup_state(fd) == STATE_UPDATING_INUMS) {
+        count = 0; /* Reset debugging counter */
         if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, UPDATE_INODE_NUMS)) {
             set_fs_fixup_state(fd, STATE_UPDATING_SB);
         }
diff --git a/ext4_utils/ext4fixup.h b/ext4_utils/ext4fixup.h
index 94395e7..6ea2113 100644
--- a/ext4_utils/ext4fixup.h
+++ b/ext4_utils/ext4fixup.h
@@ -15,5 +15,6 @@
  */
 
 int ext4fixup(char *fsdev);
-int ext4fixup_internal(char *fsdev, int v_flag, int n_flag);
+int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
+                       int stop_phase, int stop_loc, int stop_count);
 
diff --git a/ext4_utils/ext4fixup_main.c b/ext4_utils/ext4fixup_main.c
index 8b06971..47c7e65 100644
--- a/ext4_utils/ext4fixup_main.c
+++ b/ext4_utils/ext4fixup_main.c
@@ -32,10 +32,11 @@
     int no_write = 0;
     char *fsdev;
     char *me;
+    int stop_phase = 0, stop_loc = 0, stop_count = 0;
 
     me = basename(argv[0]);
 
-    while ((opt = getopt(argc, argv, "vn")) != -1) {
+    while ((opt = getopt(argc, argv, "vnd:")) != -1) {
         switch (opt) {
         case 'v':
             verbose = 1;
@@ -43,6 +44,9 @@
         case 'n':
             no_write = 1;
             break;
+        case 'd':
+            sscanf(optarg, "%d,%d,%d", &stop_phase, &stop_loc, &stop_count);
+            break;
         }
     }
 
@@ -60,5 +64,5 @@
         exit(EXIT_FAILURE);
     }
 
-    return ext4fixup_internal(fsdev, verbose, no_write);
+    return ext4fixup_internal(fsdev, verbose, no_write, stop_phase, stop_loc, stop_count);
 }
diff --git a/ext4_utils/test_ext4fixup b/ext4_utils/test_ext4fixup
new file mode 100755
index 0000000..a920a59
--- /dev/null
+++ b/ext4_utils/test_ext4fixup
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+typeset -i I ITERATIONS PHASE LOC COUNT MAXCOUNT
+
+ME=`basename $0`
+
+if [ "$#" -ne 3 ]
+then
+  echo "$ME: Usage: $ME <iterations> <maxcount> <filesystem_image>" >&2
+  exit 1;
+fi
+
+ITERATIONS="$1"
+MAXCOUNT="$2"
+ORIG_FS_IMAGE="$3"
+FIXED_FS_IMAGE="/tmp/fixedfsimage.$$"
+NEW_FS_IMAGE="/tmp/newfsimage.$$"
+
+if [ ! -f "$ORIG_FS_IMAGE" ]
+then
+  echo "$ME: Filesystem image $NEW_FS_IMAGE does not exist" >&2
+  exit 1
+fi
+
+trap "rm -f $NEW_FS_IMAGE $FIXED_FS_IMAGE" 0 1 2 3 15
+
+rm -f "$NEW_FS_IMAGE" "$FIXED_FS_IMAGE"
+
+# Create the fixed image to compare against
+cp "$ORIG_FS_IMAGE" "$FIXED_FS_IMAGE"
+ext4fixup "$FIXED_FS_IMAGE"
+
+if [ "$?" -ne 0 ]
+then
+  echo "$ME: ext4fixup failed!\n"
+  exit 1
+fi
+
+I=0
+while [ "$I" -lt "$ITERATIONS" ]
+do
+  # There is also a phase 4, which is writing out the updated superblocks and
+  # block group descriptors.  Test the with a separate script.
+  let PHASE="$RANDOM"%3         # 0 to 2
+  let PHASE++                   # 1 to 3
+  let LOC="$RANDOM"%2           # 0 to 1
+  let LOC++                     # 1 to 2
+  let COUNT="$RANDOM"%"$MAXCOUNT"
+
+  # Make a copy of the original image to fixup
+  cp "$ORIG_FS_IMAGE" "$NEW_FS_IMAGE"
+
+  # Run the fixup tool, but die partway through to see if we can recover
+  ext4fixup -d "$PHASE,$LOC,$COUNT" "$NEW_FS_IMAGE" 2>/dev/null
+ 
+  # run it again without -d to have it finish the job
+  ext4fixup "$NEW_FS_IMAGE"
+
+  if cmp "$FIXED_FS_IMAGE" "$NEW_FS_IMAGE"
+  then
+    :
+  else
+    echo "$ME: test failed with parameters $PHASE, $LOC, $COUNT"
+    exit 1
+  fi
+
+  rm -f "$NEW_FS_IMAGE"
+
+  let I++
+done
+