From: Chris Mason <mason@suse.com>

When filling holes via DIRECT_IO, we fall back to normal buffered io.  For
this to work properly, the direct io funcs have to return a value of zero to
the file write functions, so the file write functions know where to start
writing.  

In some cases, dio->result was getting returned by direct_io_worker, and that
wasn't always zero, causing some data not to be written.

From: <akpm@osdl.org>:

- Simplify things by setting `ret' later on, fix up subsequent damage to the
  dio_complete() args.

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

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

diff -puN fs/direct-io.c~direct-io-hole-fix fs/direct-io.c
--- 25/fs/direct-io.c~direct-io-hole-fix	2004-06-03 00:07:07.956123120 -0700
+++ 25-akpm/fs/direct-io.c	2004-06-03 00:07:07.961122360 -0700
@@ -990,13 +990,6 @@ direct_io_worker(int rw, struct kiocb *i
 		}
 	} /* end iovec loop */
 
-	if (ret == -ENOTBLK && rw == WRITE) {
-		/*
-		 * The remaining part of the request will be
-		 * be handled by buffered I/O when we return
-		 */
-		ret = 0;
-	}
 	/*
 	 * There may be some unwritten disk at the end of a part-written
 	 * fs-block-sized block.  Go zero that now.
@@ -1063,24 +1056,29 @@ direct_io_worker(int rw, struct kiocb *i
 			kfree(dio);
 		}
 	} else {
+		ssize_t transferred = 0;
+
 		finished_one_bio(dio);
 		ret2 = dio_await_completion(dio);
 		if (ret == 0)
 			ret = ret2;
 		if (ret == 0)
 			ret = dio->page_errors;
-		if (ret == 0 && dio->result) {
+		if (dio->result) {
 			loff_t i_size = i_size_read(inode);
 
-			ret = dio->result;
+			transferred = dio->result;
 			/*
 			 * Adjust the return value if the read crossed a
 			 * non-block-aligned EOF.
 			 */
-			if (rw == READ && (offset + ret > i_size))
-				ret = i_size - offset;
+			if (rw == READ && (offset + transferred > i_size))
+				transferred = i_size - offset;
 		}
-		dio_complete(dio, offset, ret);
+		dio_complete(dio, offset, transferred);
+		if (ret == 0)
+			ret = transferred;
+
 		/* We could have also come here on an AIO file extend */
 		if (!is_sync_kiocb(iocb) && rw == WRITE &&
 		    ret >= 0 && dio->result == dio->size)
@@ -1091,6 +1089,13 @@ direct_io_worker(int rw, struct kiocb *i
 			aio_complete(iocb, ret, 0);
 		kfree(dio);
 	}
+	if (ret == -ENOTBLK && rw == WRITE) {
+		/*
+		 * The entire request will be be handled by buffered I/O
+		 * when we return
+		 */
+		ret = 0;
+	}
 	return ret;
 }
 
_