2009-04-30 17:58:14

by Josef Bacik

[permalink] [raw]
Subject: [PATCH] fiemap: fix problem with setting FIEMAP_EXTENT_LAST

This patch fixes a problem where the generic block based fiemap stuff would not
properly set FIEMAP_EXTENT_LAST on the last extent. I've reworked things to
keep track if we go past the EOF, and mark the last extent properly. The
problem was reported by and tested by Eric Sandeen.

Tested-by: Eric Sandeen <[email protected]>
Signed-off-by: Josef Bacik <[email protected]>
---
fs/ioctl.c | 75 ++++++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/fs/ioctl.c b/fs/ioctl.c
index ac2d47e..82d9c42 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -258,7 +258,7 @@ int __generic_block_fiemap(struct inode *inode,
long long length = 0, map_len = 0;
u64 logical = 0, phys = 0, size = 0;
u32 flags = FIEMAP_EXTENT_MERGED;
- int ret = 0;
+ int ret = 0, past_eof = 0, whole_file = 0;

if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)))
return ret;
@@ -266,6 +266,9 @@ int __generic_block_fiemap(struct inode *inode,
start_blk = logical_to_blk(inode, start);

length = (long long)min_t(u64, len, i_size_read(inode));
+ if (length < len)
+ whole_file = 1;
+
map_len = length;

do {
@@ -282,11 +285,26 @@ int __generic_block_fiemap(struct inode *inode,

/* HOLE */
if (!buffer_mapped(&tmp)) {
+ length -= blk_to_logical(inode, 1);
+ start_blk++;
+
+ /*
+ * we want to handle the case where there is an
+ * allocated block at the front of the file, and then
+ * nothing but holes up to the end of the file properly,
+ * to make sure that extent at the front gets properly
+ * marked with FIEMAP_EXTENT_LAST
+ */
+ if (!past_eof &&
+ blk_to_logical(inode, start_blk) >=
+ blk_to_logical(inode, 0)+i_size_read(inode))
+ past_eof = 1;
+
/*
* first hole after going past the EOF, this is our
* last extent
*/
- if (length <= 0) {
+ if (past_eof && size) {
flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
ret = fiemap_fill_next_extent(fieinfo, logical,
phys, size,
@@ -294,15 +312,37 @@ int __generic_block_fiemap(struct inode *inode,
break;
}

- length -= blk_to_logical(inode, 1);
-
/* if we have holes up to/past EOF then we're done */
- if (length <= 0)
+ if (length <= 0 || past_eof)
break;
-
- start_blk++;
} else {
- if (length <= 0 && size) {
+ /*
+ * we have gone over the length of what we wanted to
+ * map, and it wasn't the entire file, so add the extent
+ * we got last time and exit.
+ *
+ * This is for the case where say we want to map all the
+ * way up to the second to the last block in a file, but
+ * the last block is a hole, making the second to last
+ * block FIEMAP_EXTENT_LAST. In this case we want to
+ * see if there is a hole after the second to last block
+ * so we can mark it properly. If we found data after
+ * we exceeded the length we were requesting, then we
+ * are good to go, just add the extent to the fieinfo
+ * and break
+ */
+ if (length <= 0 && !whole_file) {
+ ret = fiemap_fill_next_extent(fieinfo, logical,
+ phys, size,
+ flags);
+ break;
+ }
+
+ /*
+ * if size != 0 then we know we already have an extent
+ * to add, so add it.
+ */
+ if (size) {
ret = fiemap_fill_next_extent(fieinfo, logical,
phys, size,
flags);
@@ -319,19 +359,14 @@ int __generic_block_fiemap(struct inode *inode,
start_blk += logical_to_blk(inode, size);

/*
- * if we are past the EOF we need to loop again to see
- * if there is a hole so we can mark this extent as the
- * last one, and if not keep mapping things until we
- * find a hole, or we run out of slots in the extent
- * array
+ * If we are past the EOF, then we need to make sure as
+ * soon as we find a hole that the last extent we found
+ * is marked with FIEMAP_EXTENT_LAST
*/
- if (length <= 0)
- continue;
-
- ret = fiemap_fill_next_extent(fieinfo, logical, phys,
- size, flags);
- if (ret)
- break;
+ if (!past_eof &&
+ logical+size >=
+ blk_to_logical(inode, 0)+i_size_read(inode))
+ past_eof = 1;
}
cond_resched();
} while (1);
--
1.6.0.6


2009-04-30 22:45:39

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] fiemap: fix problem with setting FIEMAP_EXTENT_LAST

On Thu, 30 Apr 2009 13:44:51 -0400
Josef Bacik <[email protected]> wrote:

> This patch fixes a problem where the generic block based fiemap stuff would not
> properly set FIEMAP_EXTENT_LAST on the last extent. I've reworked things to
> keep track if we go past the EOF, and mark the last extent properly. The
> problem was reported by and tested by Eric Sandeen.
>

bleeearrggh. __generic_block_fiemap() needs to be dragged out, shot,
stabbed and doused in gasoline.

- uses stupid helper macros (blk_to_logical, logical_to_blk), thus
carefully obscuring the types of their incoming args and return value.

- has kerneldoc which documents non-existent arguments and fails to
document the actual arguments.

- has a local variable called `tmp'.

- Uses random and seemingly irrational mixture of u64's and `long
long's, thus carefully confusing readers who would prefer to see
loff_t, sector_t, etc so they have a fighting chance of understanding
what the hell the thing does.

- exists as useful counterexample for Documentation/CodingStyle

- has undocumented locals called "length", "len" and "map_len", to
avoid any possibility of confusion.

- has a local called `start_blk' which has a suspiciously-small
32-bit type. Because of all the above intense obfuscation efforts I
just cannot be assed working out whether or not if this is an actual
bug.


Who the heck merged this turkey?

<checks>

Oh good, it wasn't me.

your patch:

- adds a string of booleans, unhelpfully typed with plain old `int'.

- seems to think that sentences start with lower-case letters.


Sigh.

Does this bugfix need to be backported into 2.6.29? Earlier? If so, why?

Thanks.