From: Jens Axboe <axboe@suse.de>

THe direct-io code currently returns -EIO if someone tries to read past the
end of a block device.  Change it to shorten the read and to return the
number of bytes which were actually read.

(Written by me, fixed & tested by Jens)

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/direct-io.c |   18 +++++++++++++++---
 1 files changed, 15 insertions(+), 3 deletions(-)

diff -puN fs/direct-io.c~dio-handle-eof fs/direct-io.c
--- 25/fs/direct-io.c~dio-handle-eof	Wed Oct 20 15:13:10 2004
+++ 25-akpm/fs/direct-io.c	Wed Oct 20 15:13:10 2004
@@ -926,6 +926,8 @@ direct_io_worker(int rw, struct kiocb *i
 	ssize_t ret = 0;
 	ssize_t ret2;
 	size_t bytes;
+	size_t bytes_todo;
+	loff_t isize;
 
 	dio->bio = NULL;
 	dio->inode = inode;
@@ -973,16 +975,26 @@ direct_io_worker(int rw, struct kiocb *i
 	else
 		dio->pages_in_io = 0;
 
+	bytes_todo = 0;
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		dio->pages_in_io +=
 			((user_addr+iov[seg].iov_len +PAGE_SIZE-1)/PAGE_SIZE
 				- user_addr/PAGE_SIZE);
+		bytes_todo += iov[seg].iov_len;
 	}
 
-	for (seg = 0; seg < nr_segs; seg++) {
+	isize = i_size_read(inode);
+	if (bytes_todo > (isize - offset))
+		bytes_todo = isize - offset;
+
+	for (seg = 0; seg < nr_segs && bytes_todo; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
-		dio->size += bytes = iov[seg].iov_len;
+		bytes = iov[seg].iov_len;
+		if (bytes > bytes_todo)
+			bytes = bytes_todo;
+		bytes_todo -= bytes;
+		dio->size += bytes;
 
 		/* Index into the first page of the first block */
 		dio->first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits;
@@ -1003,7 +1015,7 @@ direct_io_worker(int rw, struct kiocb *i
 	
 		ret = do_direct_IO(dio);
 
-		dio->result += iov[seg].iov_len -
+		dio->result += bytes -
 			((dio->final_block_in_request - dio->block_in_file) <<
 					blkbits);
 
_