diff options
| author | Jeff Moyer <jmoyer@redhat.com> | 2006-02-03 03:04:27 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-03 08:32:07 -0800 | 
| commit | 35dc8161d0a6fa5e654bcb3d6240acc9ecb0a259 (patch) | |
| tree | 0c688ffc3adc127f5cf9f35ff93e6072e1f42438 /fs/direct-io.c | |
| parent | 835417967c10b6dfaffdffddba59196196e5d431 (diff) | |
[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 <jmoyer@redhat.com>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Suparna Bhattacharya <suparna@in.ibm.com>
Cc: Mingming Cao <cmm@us.ibm.com>
Cc: Joel Becker <Joel.Becker@oracle.com>
Cc: "Chen, Kenneth W" <kenneth.w.chen@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/direct-io.c')
| -rw-r--r-- | fs/direct-io.c | 9 | 
1 files changed, 8 insertions, 1 deletions
| diff --git a/fs/direct-io.c b/fs/direct-io.c index 30dbbd1df511..848044af7e16 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; | 
