From: Jeff Moyer Date: Fri, 3 Feb 2006 11:04:27 +0000 (-0800) Subject: [PATCH] fix O_DIRECT read of last block in a sparse file X-Git-Tag: v2.6.16-rc3~247 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35dc8161d0a6fa5e654bcb3d6240acc9ecb0a259;p=linux-2.6 [PATCH] fix O_DIRECT read of last block in a sparse file Currently, if you open a file O_DIRECT, truncate it to a size that is not a multiple of the disk block size, and then try to read the last block in the file, the read will return 0. The problem is in do_direct_IO, here: /* Handle holes */ if (!buffer_mapped(map_bh)) { char *kaddr; ... if (dio->block_in_file >= i_size_read(dio->inode)>>blkbits) { /* We hit eof */ page_cache_release(page); goto out; } We shift off any remaining bytes in the final block of the I/O, resulting in a 0-sized read. I've attached a patch that fixes this. I'm not happy about how ugly the math is getting, so suggestions are more than welcome. I've tested this with a simple program that performs the steps outlined for reproducing the problem above. Without the patch, we get a 0-sized result from read. With the patch, we get the correct return value from the short read. Signed-off-by: Jeff Moyer Cc: Badari Pulavarty Cc: Suparna Bhattacharya Cc: Mingming Cao Cc: Joel Becker Cc: "Chen, Kenneth W" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/fs/direct-io.c b/fs/direct-io.c index 30dbbd1df5..848044af7e 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -857,6 +857,7 @@ do_holes: /* Handle holes */ if (!buffer_mapped(map_bh)) { char *kaddr; + loff_t i_size_aligned; /* AKPM: eargh, -ENOTBLK is a hack */ if (dio->rw == WRITE) { @@ -864,8 +865,14 @@ do_holes: return -ENOTBLK; } + /* + * Be sure to account for a partial block as the + * last block in the file + */ + i_size_aligned = ALIGN(i_size_read(dio->inode), + 1 << blkbits); if (dio->block_in_file >= - i_size_read(dio->inode)>>blkbits) { + i_size_aligned >> blkbits) { /* We hit eof */ page_cache_release(page); goto out;