diff options
Diffstat (limited to 'fs/ioctl.c')
-rw-r--r-- | fs/ioctl.c | 75 |
1 files changed, 55 insertions, 20 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c index ac2d47e43926..82d9c42b8bac 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c | |||
@@ -258,7 +258,7 @@ int __generic_block_fiemap(struct inode *inode, | |||
258 | long long length = 0, map_len = 0; | 258 | long long length = 0, map_len = 0; |
259 | u64 logical = 0, phys = 0, size = 0; | 259 | u64 logical = 0, phys = 0, size = 0; |
260 | u32 flags = FIEMAP_EXTENT_MERGED; | 260 | u32 flags = FIEMAP_EXTENT_MERGED; |
261 | int ret = 0; | 261 | int ret = 0, past_eof = 0, whole_file = 0; |
262 | 262 | ||
263 | if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC))) | 263 | if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC))) |
264 | return ret; | 264 | return ret; |
@@ -266,6 +266,9 @@ int __generic_block_fiemap(struct inode *inode, | |||
266 | start_blk = logical_to_blk(inode, start); | 266 | start_blk = logical_to_blk(inode, start); |
267 | 267 | ||
268 | length = (long long)min_t(u64, len, i_size_read(inode)); | 268 | length = (long long)min_t(u64, len, i_size_read(inode)); |
269 | if (length < len) | ||
270 | whole_file = 1; | ||
271 | |||
269 | map_len = length; | 272 | map_len = length; |
270 | 273 | ||
271 | do { | 274 | do { |
@@ -282,11 +285,26 @@ int __generic_block_fiemap(struct inode *inode, | |||
282 | 285 | ||
283 | /* HOLE */ | 286 | /* HOLE */ |
284 | if (!buffer_mapped(&tmp)) { | 287 | if (!buffer_mapped(&tmp)) { |
288 | length -= blk_to_logical(inode, 1); | ||
289 | start_blk++; | ||
290 | |||
291 | /* | ||
292 | * we want to handle the case where there is an | ||
293 | * allocated block at the front of the file, and then | ||
294 | * nothing but holes up to the end of the file properly, | ||
295 | * to make sure that extent at the front gets properly | ||
296 | * marked with FIEMAP_EXTENT_LAST | ||
297 | */ | ||
298 | if (!past_eof && | ||
299 | blk_to_logical(inode, start_blk) >= | ||
300 | blk_to_logical(inode, 0)+i_size_read(inode)) | ||
301 | past_eof = 1; | ||
302 | |||
285 | /* | 303 | /* |
286 | * first hole after going past the EOF, this is our | 304 | * first hole after going past the EOF, this is our |
287 | * last extent | 305 | * last extent |
288 | */ | 306 | */ |
289 | if (length <= 0) { | 307 | if (past_eof && size) { |
290 | flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST; | 308 | flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST; |
291 | ret = fiemap_fill_next_extent(fieinfo, logical, | 309 | ret = fiemap_fill_next_extent(fieinfo, logical, |
292 | phys, size, | 310 | phys, size, |
@@ -294,15 +312,37 @@ int __generic_block_fiemap(struct inode *inode, | |||
294 | break; | 312 | break; |
295 | } | 313 | } |
296 | 314 | ||
297 | length -= blk_to_logical(inode, 1); | ||
298 | |||
299 | /* if we have holes up to/past EOF then we're done */ | 315 | /* if we have holes up to/past EOF then we're done */ |
300 | if (length <= 0) | 316 | if (length <= 0 || past_eof) |
301 | break; | 317 | break; |
302 | |||
303 | start_blk++; | ||
304 | } else { | 318 | } else { |
305 | if (length <= 0 && size) { | 319 | /* |
320 | * we have gone over the length of what we wanted to | ||
321 | * map, and it wasn't the entire file, so add the extent | ||
322 | * we got last time and exit. | ||
323 | * | ||
324 | * This is for the case where say we want to map all the | ||
325 | * way up to the second to the last block in a file, but | ||
326 | * the last block is a hole, making the second to last | ||
327 | * block FIEMAP_EXTENT_LAST. In this case we want to | ||
328 | * see if there is a hole after the second to last block | ||
329 | * so we can mark it properly. If we found data after | ||
330 | * we exceeded the length we were requesting, then we | ||
331 | * are good to go, just add the extent to the fieinfo | ||
332 | * and break | ||
333 | */ | ||
334 | if (length <= 0 && !whole_file) { | ||
335 | ret = fiemap_fill_next_extent(fieinfo, logical, | ||
336 | phys, size, | ||
337 | flags); | ||
338 | break; | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * if size != 0 then we know we already have an extent | ||
343 | * to add, so add it. | ||
344 | */ | ||
345 | if (size) { | ||
306 | ret = fiemap_fill_next_extent(fieinfo, logical, | 346 | ret = fiemap_fill_next_extent(fieinfo, logical, |
307 | phys, size, | 347 | phys, size, |
308 | flags); | 348 | flags); |
@@ -319,19 +359,14 @@ int __generic_block_fiemap(struct inode *inode, | |||
319 | start_blk += logical_to_blk(inode, size); | 359 | start_blk += logical_to_blk(inode, size); |
320 | 360 | ||
321 | /* | 361 | /* |
322 | * if we are past the EOF we need to loop again to see | 362 | * If we are past the EOF, then we need to make sure as |
323 | * if there is a hole so we can mark this extent as the | 363 | * soon as we find a hole that the last extent we found |
324 | * last one, and if not keep mapping things until we | 364 | * is marked with FIEMAP_EXTENT_LAST |
325 | * find a hole, or we run out of slots in the extent | ||
326 | * array | ||
327 | */ | 365 | */ |
328 | if (length <= 0) | 366 | if (!past_eof && |
329 | continue; | 367 | logical+size >= |
330 | 368 | blk_to_logical(inode, 0)+i_size_read(inode)) | |
331 | ret = fiemap_fill_next_extent(fieinfo, logical, phys, | 369 | past_eof = 1; |
332 | size, flags); | ||
333 | if (ret) | ||
334 | break; | ||
335 | } | 370 | } |
336 | cond_resched(); | 371 | cond_resched(); |
337 | } while (1); | 372 | } while (1); |