From: Jens Axboe 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 ->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 --- 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-11-03 20:26:25.406766280 -0800 +++ 25-akpm/fs/direct-io.c 2004-11-03 20:26:25.410765672 -0800 @@ -928,6 +928,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; @@ -975,16 +977,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; @@ -1005,7 +1019,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); _