ocfs2: Read from an unwritten extent returns zeros

Return an optional extent flags field from our lookup functions and wire up
callers to treat unwritten regions as holes for the purpose of returning
zeros to the user.

Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 0eab0d3..412a288 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -3487,6 +3487,7 @@
 {
 	int i, numpages = 0, ret = 0;
 	unsigned int csize = OCFS2_SB(inode->i_sb)->s_clustersize;
+	unsigned int ext_flags;
 	struct super_block *sb = inode->i_sb;
 	struct address_space *mapping = inode->i_mapping;
 	unsigned long index;
@@ -3499,7 +3500,7 @@
 		goto out;
 
 	ret = ocfs2_extent_map_get_blocks(inode, isize >> sb->s_blocksize_bits,
-					  phys, NULL);
+					  phys, NULL, &ext_flags);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -3509,6 +3510,11 @@
 	if (*phys == 0)
 		goto out;
 
+	/* Tail is marked as unwritten, we can count on write to zero
+	 * in that case. */
+	if (ext_flags & OCFS2_EXT_UNWRITTEN)
+		goto out;
+
 	next_cluster_bytes = ocfs2_align_bytes_to_clusters(inode->i_sb, isize);
 	index = isize >> PAGE_CACHE_SHIFT;
 	do {
@@ -3579,9 +3585,6 @@
 		goto out;
 	}
 
-	/*
-	 * Truncate on an i_size boundary - nothing more to do.
-	 */
 	if (numpages == 0)
 		goto out;
 
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 014f4f5..eb67c90 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -137,6 +137,7 @@
 			   struct buffer_head *bh_result, int create)
 {
 	int err = 0;
+	unsigned int ext_flags;
 	u64 p_blkno, past_eof;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
@@ -153,7 +154,8 @@
 		goto bail;
 	}
 
-	err = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno, NULL);
+	err = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno, NULL,
+					  &ext_flags);
 	if (err) {
 		mlog(ML_ERROR, "Error %d from get_blocks(0x%p, %llu, 1, "
 		     "%llu, NULL)\n", err, inode, (unsigned long long)iblock,
@@ -171,7 +173,8 @@
 			"ino %lu, iblock %llu\n", inode->i_ino,
 			(unsigned long long)iblock);
 
-	if (p_blkno)
+	/* Treat the unwritten extent as a hole for zeroing purposes. */
+	if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN))
 		map_bh(bh_result, inode->i_sb, p_blkno);
 
 	if (!ocfs2_sparse_alloc(osb)) {
@@ -396,7 +399,7 @@
 		down_read(&OCFS2_I(inode)->ip_alloc_sem);
 	}
 
-	err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL);
+	err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, NULL);
 
 	if (!INODE_JOURNAL(inode)) {
 		up_read(&OCFS2_I(inode)->ip_alloc_sem);
@@ -438,6 +441,7 @@
 	int ret;
 	u64 p_blkno, inode_blocks;
 	int contig_blocks;
+	unsigned int ext_flags;
 	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
 	unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
 
@@ -458,7 +462,7 @@
 	/* This figures out the size of the next contiguous block, and
 	 * our logical offset */
 	ret = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno,
-					  &contig_blocks);
+					  &contig_blocks, &ext_flags);
 	if (ret) {
 		mlog(ML_ERROR, "get_blocks() failed iblock=%llu\n",
 		     (unsigned long long)iblock);
@@ -478,8 +482,10 @@
 	/*
 	 * get_more_blocks() expects us to describe a hole by clearing
 	 * the mapped bit on bh_result().
+	 *
+	 * Consider an unwritten extent as a hole.
 	 */
-	if (p_blkno)
+	if (p_blkno && !(ext_flags & OCFS2_EXT_UNWRITTEN))
 		map_bh(bh_result, inode->i_sb, p_blkno);
 	else {
 		/*
@@ -1111,7 +1117,8 @@
 		}
 	}
 
-	ret = ocfs2_extent_map_get_blocks(inode, v_blkno, &p_blkno, NULL);
+	ret = ocfs2_extent_map_get_blocks(inode, v_blkno, &p_blkno, NULL,
+					  NULL);
 	if (ret < 0) {
 
 		/*
@@ -1215,7 +1222,7 @@
 	 */
 	down_write(&OCFS2_I(inode)->ip_alloc_sem);
 
-	ret = ocfs2_get_clusters(inode, wc.w_cpos, &phys, NULL);
+	ret = ocfs2_get_clusters(inode, wc.w_cpos, &phys, NULL, NULL);
 	if (ret) {
 		mlog_errno(ret);
 		goto out_meta;
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index c914906..8d22e1e 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -379,7 +379,7 @@
 
 	status = ocfs2_extent_map_get_blocks(dir, (dir->i_blocks >>
 						   (sb->s_blocksize_bits - 9)),
-					     &p_blkno, NULL);
+					     &p_blkno, NULL, NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c
index ea0ce41..eef6c18 100644
--- a/fs/ocfs2/extent_map.c
+++ b/fs/ocfs2/extent_map.c
@@ -70,9 +70,11 @@
 }
 
 int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
-		       u32 *p_cluster, u32 *num_clusters)
+		       u32 *p_cluster, u32 *num_clusters,
+		       unsigned int *extent_flags)
 {
 	int ret, i;
+	unsigned int flags = 0;
 	struct buffer_head *di_bh = NULL;
 	struct buffer_head *eb_bh = NULL;
 	struct ocfs2_dinode *di;
@@ -142,8 +144,13 @@
 
 		if (num_clusters)
 			*num_clusters = ocfs2_rec_clusters(el, rec) - coff;
+
+		flags = rec->e_flags;
 	}
 
+	if (extent_flags)
+		*extent_flags = flags;
+
 out:
 	brelse(di_bh);
 	brelse(eb_bh);
@@ -155,7 +162,7 @@
  * all while the map is in the process of being updated.
  */
 int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
-				int *ret_count)
+				int *ret_count, unsigned int *extent_flags)
 {
 	int ret;
 	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
@@ -164,7 +171,8 @@
 
 	cpos = ocfs2_blocks_to_clusters(inode->i_sb, v_blkno);
 
-	ret = ocfs2_get_clusters(inode, cpos, &p_cluster, &num_clusters);
+	ret = ocfs2_get_clusters(inode, cpos, &p_cluster, &num_clusters,
+				 extent_flags);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h
index 625d0ee..0031c59 100644
--- a/fs/ocfs2/extent_map.h
+++ b/fs/ocfs2/extent_map.h
@@ -26,8 +26,8 @@
 #define _EXTENT_MAP_H
 
 int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster,
-		       u32 *num_clusters);
+		       u32 *num_clusters, unsigned int *extent_flags);
 int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
-				int *ret_count);
+				int *ret_count, unsigned int *extent_flags);
 
 #endif  /* _EXTENT_MAP_H */
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 3617601..f516619 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1127,6 +1127,7 @@
 				       size_t count)
 {
 	int ret = 0;
+	unsigned int extent_flags;
 	u32 cpos, clusters, extent_len, phys_cpos;
 	struct super_block *sb = inode->i_sb;
 
@@ -1134,13 +1135,14 @@
 	clusters = ocfs2_clusters_for_bytes(sb, pos + count) - cpos;
 
 	while (clusters) {
-		ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len);
+		ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len,
+					 &extent_flags);
 		if (ret < 0) {
 			mlog_errno(ret);
 			goto out;
 		}
 
-		if (phys_cpos == 0) {
+		if (phys_cpos == 0 || (extent_flags & OCFS2_EXT_UNWRITTEN)) {
 			ret = 1;
 			break;
 		}
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 78c99b5..310049b 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -1105,7 +1105,8 @@
 		return NULL;
 	}
 
-	tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL);
+	tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
+					     NULL);
 	if (tmperr < 0) {
 		mlog_errno(tmperr);
 		goto fail;
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 2e2e04f..db77e09 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -670,7 +670,7 @@
 	       (inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9))) {
 
 		status = ocfs2_extent_map_get_blocks(inode, v_blkno,
-						     &p_blkno, &p_blocks);
+						     &p_blkno, &p_blocks, NULL);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 5755e07..395859e 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -1511,7 +1511,8 @@
 		goto bail;
 	}
 
-	status = ocfs2_extent_map_get_blocks(inode, 0, &p_blkno, &p_blocks);
+	status = ocfs2_extent_map_get_blocks(inode, 0, &p_blkno, &p_blocks,
+					     NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
index f4416e7..d921a28 100644
--- a/fs/ocfs2/slot_map.c
+++ b/fs/ocfs2/slot_map.c
@@ -197,7 +197,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_extent_map_get_blocks(inode, 0ULL, &blkno, NULL);
+	status = ocfs2_extent_map_get_blocks(inode, 0ULL, &blkno, NULL, NULL);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;