e2fsck: clear uninit flag on directory extents

Directories can't have uninitialized extents, so offer to clear the
uninit flag when we find this situation.  The actual directory blocks
will be checked in pass 2 and 3 regardless of the uninit flag.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index e05bf6f..dc15db2 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1806,6 +1806,21 @@
 			  (1 << (21 - ctx->fs->super->s_log_block_size))))
 			problem = PR_1_TOOBIG_DIR;
 
+		/*
+		 * Uninitialized blocks in a directory?  Clear the flag and
+		 * we'll interpret the blocks later.
+		 */
+		if (is_dir && problem == 0 &&
+		    (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+		    fix_problem(ctx, PR_1_UNINIT_DBLOCK, pctx)) {
+			extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
+			pb->inode_modified = 1;
+			pctx->errcode = ext2fs_extent_replace(ehandle, 0,
+							      &extent);
+			if (pctx->errcode)
+				return;
+		}
+
 		if (problem) {
 report_problem:
 			pctx->blk = extent.e_pblk;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 837d111..a7291e5 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -972,6 +972,11 @@
 	  N_("@d @i %i @b %b should be at @b %c.  "),
 	  PROMPT_FIX, 0 },
 
+	/* Extents/inlinedata flag set on a device or socket inode */
+	{ PR_1_UNINIT_DBLOCK,
+	  N_("@d @i %i has @x marked uninitialized at @b %c.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
 	/* Pass 1b errors */
 
 	/* Pass 1B: Rescan for duplicate/bad blocks */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index d3e66ad..0a3347f 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -565,6 +565,22 @@
 #define PR_1_EXTENT_INDEX_START_INVALID	0x01006D
 
 #define PR_1_EXTENT_END_OUT_OF_BOUNDS	0x01006E
+
+/* Inode has inline data, but superblock is missing INLINE_DATA feature. */
+#define PR_1_INLINE_DATA_FEATURE       0x01006F
+
+/* INLINE_DATA feature is set in a non-inline-data filesystem */
+#define PR_1_INLINE_DATA_SET	       0x010070
+
+/* file metadata collides with critical metadata */
+#define PR_1_CRITICAL_METADATA_COLLISION	0x010071
+
+/* Directory inode has a missing block (hole) */
+#define PR_1_COLLAPSE_DBLOCK		0x010072
+
+/* uninit directory block */
+#define PR_1_UNINIT_DBLOCK		0x010073
+
 /*
  * Pass 1b errors
  */
@@ -624,9 +640,6 @@
 /* Couldn't clone file (error) */
 #define PR_1D_CLONE_ERROR	0x013008
 
-/* Directory inode has a missing block (hole) */
-#define PR_1_COLLAPSE_DBLOCK		0x010072
-
 /*
  * Pass 2 errors
  */
diff --git a/tests/f_uninit_dir/expect.1 b/tests/f_uninit_dir/expect.1
new file mode 100644
index 0000000..f0065f1
--- /dev/null
+++ b/tests/f_uninit_dir/expect.1
@@ -0,0 +1,27 @@
+Pass 1: Checking inodes, blocks, and sizes
+Directory inode 12 has extent marked uninitialized at block 0.  Fix? yes
+
+Directory inode 14 has extent marked uninitialized at block 0.  Fix? yes
+
+Pass 2: Checking directory structure
+Directory inode 14, block #0, offset 0: directory corrupted
+Salvage? yes
+
+Missing '.' in directory inode 14.
+Fix? yes
+
+Setting filetype for entry '.' in ??? (14) to 2.
+Missing '..' in directory inode 14.
+Fix? yes
+
+Setting filetype for entry '..' in ??? (14) to 2.
+Pass 3: Checking directory connectivity
+'..' in /abc (14) is <The NULL inode> (0), should be / (2).
+Fix? yes
+
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 14/128 files (0.0% non-contiguous), 20/512 blocks
+Exit status is 1
diff --git a/tests/f_uninit_dir/expect.2 b/tests/f_uninit_dir/expect.2
new file mode 100644
index 0000000..7b28f43
--- /dev/null
+++ b/tests/f_uninit_dir/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 14/128 files (0.0% non-contiguous), 20/512 blocks
+Exit status is 0
diff --git a/tests/f_uninit_dir/image.gz b/tests/f_uninit_dir/image.gz
new file mode 100644
index 0000000..ac9131d
--- /dev/null
+++ b/tests/f_uninit_dir/image.gz
Binary files differ
diff --git a/tests/f_uninit_dir/name b/tests/f_uninit_dir/name
new file mode 100644
index 0000000..d7f5bee
--- /dev/null
+++ b/tests/f_uninit_dir/name
@@ -0,0 +1 @@
+fix uninit flag on directory extents and check the dir blocks