
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)
DESC
dio-handle-eof fix
EDESC
From: Jens Axboe <axboe@suse.de>

->head and ->tail were not initialized in the cleanup path, I'm guessing
this happens if we adjust the read to zero.  Seems best to simply check for
that condition and bail early, instead of initing ->head and tail earlier
and go through the whole path.


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

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

diff -puN fs/direct-io.c~dio-handle-eof fs/direct-io.c
--- 25/fs/direct-io.c~dio-handle-eof	2004-10-26 20:05:53.000000000 -0700
+++ 25-akpm/fs/direct-io.c	2004-10-26 22:52:08.732590768 -0700
@@ -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,28 @@ 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;
+	if (!bytes_todo)
+		return 0;
+
+	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 +1017,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);
 
_
