direct IO + fixes
diff --git a/ChangeLog b/ChangeLog
index 5448cc9..14ce894 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2004-07-02  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Fix kernel hang on mkfifo under 2.4 kernels (spotted and patch
+	by Mattias Wadman)
+
+	* Added option for direct read/write (-r)
+
+	* Fix revalidate time setting for newly created inodes
+
+2004-07-01  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Change passing fuse include dir to 2.6 kernel make system more
+	robust (hopefully fixes problems seen on SuSE 9.1)
+
 2004-06-30  Miklos Szeredi <mszeredi@inf.bme.hu>
 
 	* Acquire inode->i_sem before open and release methods to prevent
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 027cb5e..7f241e6 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -153,6 +153,10 @@
 	unsigned int size;
 };
 
+struct fuse_write_out {
+	unsigned int size;
+};
+
 struct fuse_statfs_out {
 	struct fuse_kstatfs st;
 };
diff --git a/kernel/.cvsignore b/kernel/.cvsignore
index 9722e6c..a9f8088 100644
--- a/kernel/.cvsignore
+++ b/kernel/.cvsignore
@@ -5,3 +5,4 @@
 *.ko
 *.s
 .tmp_versions
+.*.d
diff --git a/kernel/Makefile.in b/kernel/Makefile.in
index 6ea6569..bcd792f 100644
--- a/kernel/Makefile.in
+++ b/kernel/Makefile.in
@@ -67,7 +67,8 @@
 
 else
 
-EXTRA_CFLAGS := -I$(PWD)/../include -DFUSE_VERSION=\"@VERSION@\"
+export FUSE_INCLUDE ?= $(shell pwd)/../include
+EXTRA_CFLAGS += -I$(FUSE_INCLUDE) -DFUSE_VERSION=\"@VERSION@\"
 
 obj-m := fuse.o
 fuse-objs := dev.o dir.o file.o inode.o util.o
@@ -75,7 +76,3 @@
 all-spec:
 	$(MAKE) -C @kernelsrc@ SUBDIRS=$(PWD) @KERNELMAKE_PARAMS@ modules
 endif
-
-
-
-
diff --git a/kernel/dir.c b/kernel/dir.c
index af41ab5..aa31295 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -182,8 +182,10 @@
 	if (err && err != -ENOENT)
 		return err;
 
-	entry->d_time = time_to_jiffies(outarg.entry_valid,
-					outarg.entry_valid_nsec);
+	if (inode)
+		entry->d_time = time_to_jiffies(outarg.entry_valid,
+						outarg.entry_valid_nsec);
+
 	entry->d_op = &fuse_dentry_operations;
 	*inodep = inode;
 	return 0;
@@ -210,6 +212,9 @@
 		return -EINVAL;
 	}
 
+	entry->d_time = time_to_jiffies(outarg->entry_valid,
+					outarg->entry_valid_nsec);
+
 	d_instantiate(entry, inode);
 	return 0;
 }
@@ -847,7 +852,7 @@
 static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 		      int rdev)
 {
-	return fuse_mknod(dir, entry, mode, rdev);
+	return _fuse_mknod(dir, entry, mode, rdev);
 }
 
 static int fuse_dentry_revalidate(struct dentry *entry, int flags)
diff --git a/kernel/file.c b/kernel/file.c
index 9a7da73..d7b61ea 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -9,6 +9,7 @@
 
 #include <linux/pagemap.h>
 #include <linux/slab.h>
+#include <asm/uaccess.h>
 #ifdef KERNEL_2_6
 #include <linux/backing-dev.h>
 #include <linux/writeback.h>
@@ -45,15 +46,16 @@
 		 	return err;
 	}
 
+	down(&inode->i_sem);
+	err = -ERESTARTSYS;
 	req = fuse_get_request(fc);
 	if (!req)
-		return -ERESTARTSYS;
+		goto out;
 
+	err = -ENOMEM;
 	req2 = fuse_request_alloc();
-	if (!req2) {
-		fuse_put_request(fc, req);
-		return -ENOMEM;
-	}
+	if (!req2)
+		goto out_put_request;
 
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.flags = file->f_flags & ~O_EXCL;
@@ -62,9 +64,7 @@
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
-	down(&inode->i_sem);
 	request_send(fc, req);
-	up(&inode->i_sem);
 	err = req->out.h.error;
 	if (!err && !(fc->flags & FUSE_KERNEL_CACHE)) {
 #ifdef KERNEL_2_6
@@ -77,7 +77,11 @@
 		fuse_request_free(req2);
 	else
 		file->private_data = req2;
+
+ out_put_request:
 	fuse_put_request(fc, req);
+ out:
+	up(&inode->i_sem);
 	return err;
 }
 
@@ -90,6 +94,7 @@
 	if (file->f_mode & FMODE_WRITE)
 		filemap_fdatawrite(inode->i_mapping);
 
+	down(&inode->i_sem);
 	inarg = &req->misc.open_in;
 	inarg->flags = file->f_flags & ~O_EXCL;
 	req->in.h.opcode = FUSE_RELEASE;
@@ -97,10 +102,9 @@
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(struct fuse_open_in);
 	req->in.args[0].value = inarg;
-	down(&inode->i_sem);
 	request_send(fc, req);
-	up(&inode->i_sem);
 	fuse_put_request(fc, req);
+	up(&inode->i_sem);
 
 	/* Return value is ignored by VFS */
 	return 0;
@@ -168,24 +172,21 @@
            care of this? */
 }
 
-static int fuse_readpage(struct file *file, struct page *page)
+static ssize_t fuse_send_read(struct inode *inode, char *buf, loff_t pos,
+			      size_t count)
 {
-	struct inode *inode = page->mapping->host;
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_req *req;
 	struct fuse_read_in inarg;
-	char *buffer;
-	int err;
-
-	buffer = kmap(page);
+	ssize_t res;
 	
 	req = fuse_get_request(fc);
 	if (!req)
 		return -ERESTARTSYS;
 	
 	memset(&inarg, 0, sizeof(inarg));
-	inarg.offset = (unsigned long long) page->index << PAGE_CACHE_SHIFT;
-	inarg.size = PAGE_CACHE_SIZE;
+	inarg.offset = pos;
+	inarg.size = count;
 	req->in.h.opcode = FUSE_READ;
 	req->in.h.ino = inode->i_ino;
 	req->in.numargs = 1;
@@ -193,25 +194,40 @@
 	req->in.args[0].value = &inarg;
 	req->out.argvar = 1;
 	req->out.numargs = 1;
-	req->out.args[0].size = PAGE_CACHE_SIZE;
-	req->out.args[0].value = buffer;
+	req->out.args[0].size = count;
+	req->out.args[0].value = buf;
 	request_send(fc, req);
-	err = req->out.h.error;
-	if (!err) {
-		size_t outsize = req->out.args[0].size;
-		if (outsize < PAGE_CACHE_SIZE) 
-			memset(buffer + outsize, 0, PAGE_CACHE_SIZE - outsize);
-		flush_dcache_page(page);
-		SetPageUptodate(page);
-	}
+	res = req->out.h.error;
+	if (!res)
+		res = req->out.args[0].size;
 	fuse_put_request(fc, req);
-	kunmap(page);
-	unlock_page(page);
-	return err;
+	return res;
 }
 
-static int fuse_is_block_uptodate(struct address_space *mapping,
-		struct inode *inode, size_t bl_index)
+
+static int fuse_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	char *buffer;
+	ssize_t res;
+	loff_t pos;
+
+	pos = (unsigned long long) page->index << PAGE_CACHE_SHIFT;
+	buffer = kmap(page);
+	res = fuse_send_read(inode, buffer, pos, PAGE_CACHE_SIZE);
+	if (res >= 0) {
+		if (res < PAGE_CACHE_SIZE) 
+			memset(buffer + res, 0, PAGE_CACHE_SIZE - res);
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+		res = 0;
+	}
+	kunmap(page);
+	unlock_page(page);
+	return res;
+}
+
+static int fuse_is_block_uptodate(struct inode *inode, size_t bl_index)
 {
 	size_t index = bl_index << FUSE_BLOCK_PAGE_SHIFT;
 	size_t end_index = ((bl_index + 1) << FUSE_BLOCK_PAGE_SHIFT) - 1;
@@ -221,7 +237,7 @@
 		end_index = file_end_index;
 
 	for (; index <= end_index; index++) {
-		struct page *page = find_get_page(mapping, index);
+		struct page *page = find_get_page(inode->i_mapping, index);
 
 		if (!page)
 			return 0;
@@ -238,9 +254,8 @@
 }
 
 
-static int fuse_cache_block(struct address_space *mapping,
-		struct inode *inode, char *bl_buf,
-		size_t bl_index)
+static int fuse_cache_block(struct inode *inode, char *bl_buf,
+			    size_t bl_index)
 {
 	size_t start_index = bl_index << FUSE_BLOCK_PAGE_SHIFT;
 	size_t end_index = ((bl_index + 1) << FUSE_BLOCK_PAGE_SHIFT) - 1;
@@ -256,7 +271,7 @@
 		struct page *page;
 		char *buffer;
 
-		page = grab_cache_page(mapping, index);
+		page = grab_cache_page(inode->i_mapping, index);
 		if (!page)
 			return -1;
 
@@ -277,41 +292,22 @@
 } 
 
 static int fuse_file_read_block(struct inode *inode, char *bl_buf,
-		size_t bl_index)
+				size_t bl_index)
 {
-	struct fuse_conn *fc = INO_FC(inode);
-	struct fuse_req *req = fuse_get_request(fc);
-	struct fuse_read_in inarg;
-	int err;
-
-	if (!req)
-		return -ERESTARTSYS;
-
-	memset(&inarg, 0, sizeof(inarg));
-	inarg.offset = (unsigned long long) bl_index << FUSE_BLOCK_SHIFT;
-	inarg.size = FUSE_BLOCK_SIZE;
-	req->in.h.opcode = FUSE_READ;
-	req->in.h.ino = inode->i_ino;
-	req->in.numargs = 1;
-	req->in.args[0].size = sizeof(inarg);
-	req->in.args[0].value = &inarg;
-	req->out.argvar = 1;
-	req->out.numargs = 1;
-	req->out.args[0].size = FUSE_BLOCK_SIZE;
-	req->out.args[0].value = bl_buf;
-	request_send(fc, req);
-	err = req->out.h.error;
-	if (!err) {
-		size_t outsize = req->out.args[0].size;
-		if (outsize < FUSE_BLOCK_SIZE)
-			memset(bl_buf + outsize, 0, FUSE_BLOCK_SIZE - outsize);
+	ssize_t res;
+	loff_t offset;
+	
+	offset = (unsigned long long) bl_index << FUSE_BLOCK_SHIFT;
+	res = fuse_send_read(inode, bl_buf, offset, FUSE_BLOCK_SIZE);
+	if (res >= 0) {
+		if (res < FUSE_BLOCK_SIZE)
+			memset(bl_buf + res, 0, FUSE_BLOCK_SIZE - res);
+		res = 0;
 	}
-	fuse_put_request(fc, req);
-	return err;
+	return res;
 }   
 
-static void fuse_file_bigread(struct address_space *mapping,
-			      struct inode *inode, loff_t pos, size_t count)
+static void fuse_file_bigread(struct inode *inode, loff_t pos, size_t count)
 {
 	size_t bl_index = pos >> FUSE_BLOCK_SHIFT;
 	size_t bl_end_index = (pos + count) >> FUSE_BLOCK_SHIFT;
@@ -325,47 +321,95 @@
 		char *bl_buf = kmalloc(FUSE_BLOCK_SIZE, GFP_KERNEL);
 		if (!bl_buf)
 			break;
-		res = fuse_is_block_uptodate(mapping, inode, bl_index);
+		res = fuse_is_block_uptodate(inode, bl_index);
 		if (!res)
 			res = fuse_file_read_block(inode, bl_buf, bl_index);
 		if (!res)
-			fuse_cache_block(mapping, inode, bl_buf, bl_index);
+			fuse_cache_block(inode, bl_buf, bl_index);
 		kfree(bl_buf);
 		bl_index++;
 	}
 }
 
-static ssize_t fuse_file_read(struct file *filp, char *buf,
-		size_t count, loff_t * ppos)
+static ssize_t fuse_read(struct file *file, char *buf, size_t count,
+			 loff_t *ppos)
 {
-	struct address_space *mapping = filp->f_dentry->d_inode->i_mapping;
-	struct inode *inode = mapping->host;
+	struct inode *inode = file->f_dentry->d_inode;
 	struct fuse_conn *fc = INO_FC(inode);
+	char *tmpbuf;
+	ssize_t res = 0;
+	loff_t pos = *ppos;
 
-	if (fc->flags & FUSE_LARGE_READ)
-		fuse_file_bigread(mapping, inode, *ppos, count);
+	tmpbuf = kmalloc(count < fc->max_read ? count : fc->max_read,
+			 GFP_KERNEL);
+	if (!tmpbuf)
+		return -ENOMEM;
 
-	return generic_file_read(filp, buf, count, ppos);
+	while (count) {
+		size_t nbytes = count < fc->max_read ? count : fc->max_read;
+		ssize_t res1;
+		res1 = fuse_send_read(inode, tmpbuf, pos, nbytes);
+		if (res1 < 0) {
+			if (!res)
+				res = res1;
+			break;
+		}
+		res += res1;
+		if (copy_to_user(buf, tmpbuf, res1)) {
+			res = -EFAULT;
+			break;
+		}
+		count -= res1;
+		buf += res1;
+		pos += res1;
+		if (res1 < nbytes)
+			break;
+	}
+	kfree(tmpbuf);
+
+	if (res > 0)
+		*ppos += res;
+
+	return res;
+}
+
+static ssize_t fuse_file_read(struct file *file, char *buf,
+			      size_t count, loff_t * ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	ssize_t res;
+
+	down(&inode->i_sem);
+	if (fc->flags & FUSE_DIRECT_IO) {
+		res = fuse_read(file, buf, count, ppos);
+	}
+	else {
+		if (fc->flags & FUSE_LARGE_READ)
+			fuse_file_bigread(inode, *ppos, count);
+		
+		res = generic_file_read(file, buf, count, ppos);
+	}
+	up(&inode->i_sem);
+
+	return res;
 }  
 
-static int write_buffer(struct inode *inode, struct page *page,
-			unsigned offset, size_t count)
+static ssize_t fuse_send_write(struct inode *inode, const char *buf,
+			       loff_t pos, size_t count)
 {
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_req *req;
 	struct fuse_write_in inarg;
-	char *buffer;
-	int err;
-
-	buffer = kmap(page);
-
+	struct fuse_write_out outarg;
+	ssize_t res;
+	
 	req = fuse_get_request(fc);
 	if (!req)
 		return -ERESTARTSYS;
-
+	
 	memset(&inarg, 0, sizeof(inarg));
-	inarg.offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) +
-		offset;
+	inarg.offset = pos;
 	inarg.size = count;
 	req->in.h.opcode = FUSE_WRITE;
 	req->in.h.ino = inode->i_ino;
@@ -373,14 +417,39 @@
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
 	req->in.args[1].size = count;
-	req->in.args[1].value = buffer + offset;
+	req->in.args[1].value = buf;
+	req->out.numargs = 1;
+	req->out.args[0].size = sizeof(outarg);
+	req->out.args[0].value = &outarg;
 	request_send(fc, req);
-	err = req->out.h.error;
+	res = req->out.h.error;
+	if (!res)
+		res = outarg.size;
 	fuse_put_request(fc, req);
+	return res;
+}
+
+static int write_buffer(struct inode *inode, struct page *page,
+			unsigned offset, size_t count)
+{
+	char *buffer;
+	ssize_t res;
+	loff_t pos;
+
+	pos = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
+	buffer = kmap(page);
+	res = fuse_send_write(inode, buffer + offset, pos, count);
+	if (res >= 0) {
+		if (res < count) {
+			printk("fuse: short write\n");
+			res = -EPROTO;
+		} else
+			res = 0;
+	}
 	kunmap(page);
-	if (err)
+	if (res)
 		SetPageError(page);
-	return err;
+	return res;
 }
 
 static int get_write_count(struct inode *inode, struct page *page)
@@ -405,7 +474,12 @@
 static void write_buffer_end(struct fuse_conn *fc, struct fuse_req *req)
 {
 	struct page *page = (struct page *) req->data;
-	
+	struct fuse_write_out *outarg = req->out.args[0].value;
+	if (!req->out.h.error && outarg->size != req->in.args[1].size) {
+		printk("fuse: short write\n");
+		req->out.h.error = -EPROTO;
+	}
+
 	lock_page(page);
 	if (req->out.h.error) {
 		SetPageError(page);
@@ -425,14 +499,14 @@
 {
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_req *req;
-	struct fuse_write_in *inarg = NULL;
+	struct fuse_write_in *inarg;
 	char *buffer;
 
 	req = fuse_get_request_nonblock(fc);
 	if (!req)
 		return -EWOULDBLOCK;
 
-	inarg = &req->misc.write_in;
+	inarg = &req->misc.write.in;
 	buffer = kmap(page);
 	inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
 	inarg->size = count;
@@ -443,6 +517,9 @@
 	req->in.args[0].value = inarg;
 	req->in.args[1].size = count;
 	req->in.args[1].value = buffer + offset;
+	req->out.numargs = 1;
+	req->out.args[0].size = sizeof(struct fuse_write_out);
+	req->out.args[0].value = &req->misc.write.out;
 	request_send_nonblock(fc, req, write_buffer_end, page);
 	return 0;
 }
@@ -509,10 +586,82 @@
 	return err;
 }
 
+static ssize_t fuse_write(struct file *file, const char *buf, size_t count,
+			  loff_t *ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	char *tmpbuf;
+	ssize_t res = 0;
+	loff_t pos = *ppos;
+
+	tmpbuf = kmalloc(count < fc->max_write ? count : fc->max_write,
+			 GFP_KERNEL);
+	if (!tmpbuf)
+		return -ENOMEM;
+
+	while (count) {
+		size_t nbytes = count < fc->max_write ? count : fc->max_write;
+		ssize_t res1;
+		if (copy_from_user(tmpbuf, buf, nbytes)) {
+			res = -EFAULT;
+			break;
+		}
+		res1 = fuse_send_write(inode, tmpbuf, pos, nbytes);
+		if (res1 < 0) {
+			res = res1;
+			break;
+		}
+		res += res1;
+		count -= res1;
+		buf += res1;
+		pos += res1;
+		if (res1 < nbytes)
+			break;
+	}
+	kfree(tmpbuf);
+
+	if (res > 0) {
+		if (pos > i_size_read(inode))
+			i_size_write(inode, pos);
+		*ppos = pos;
+	}
+
+	return res;
+}
+
+static ssize_t fuse_file_write(struct file *file, const char *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	
+	if (fc->flags & FUSE_DIRECT_IO) {
+		ssize_t res;
+		down(&inode->i_sem);
+		res = fuse_write(file, buf, count, ppos);
+		up(&inode->i_sem);
+		return res;
+	}
+	else 
+		return generic_file_write(file, buf, count, ppos);
+}
+			       
+static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+
+	if (fc->flags & FUSE_DIRECT_IO)
+		return -ENODEV;
+	else
+		return generic_file_mmap(file, vma);
+}
+
 static struct file_operations fuse_file_operations = {
 	.read		= fuse_file_read,
-	.write		= generic_file_write,
-	.mmap		= generic_file_mmap,
+	.write		= fuse_file_write,
+	.mmap		= fuse_file_mmap,
 	.open		= fuse_open,
 	.flush		= fuse_flush,
 	.release	= fuse_release,
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index f83ac6a..59bc0bc 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -62,6 +62,9 @@
     than for small. */
 #define FUSE_LARGE_READ          (1 << 3)
 
+/** Bypass the page cache for read and write operations  */
+#define FUSE_DIRECT_IO           (1 << 4)
+
 /** One input argument of a request */
 struct fuse_in_arg {
 	unsigned int size;
@@ -136,7 +139,11 @@
 
 	/** Data for asynchronous requests */
 	union {
-		struct fuse_write_in write_in;
+		struct {
+			struct fuse_write_in in;
+			struct fuse_write_out out;
+			
+		} write;
 		struct fuse_open_in open_in;
 		struct fuse_forget_in forget_in;
 	} misc;
@@ -162,6 +169,12 @@
 	/** The fuse mount flags for this mount */
 	unsigned int flags;
 
+	/** Maximum read size */
+	unsigned int max_read;
+
+	/** Maximum write size */
+	unsigned int max_write;
+
 	/** Readers of the connection are waiting on this */
 	wait_queue_head_t waitq;
 
diff --git a/kernel/inode.c b/kernel/inode.c
index afee16d..d94b3b2 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -37,6 +37,7 @@
 	unsigned int rootmode;
 	unsigned int uid;
 	unsigned int flags;
+	unsigned int max_read;
 };
 
 static void fuse_read_inode(struct inode *inode)
@@ -124,7 +125,8 @@
 }
 
 enum { opt_fd, opt_rootmode, opt_uid, opt_default_permissions, 
-       opt_allow_other, opt_kernel_cache, opt_large_read, opt_err };
+       opt_allow_other, opt_kernel_cache, opt_large_read, opt_direct_io,
+       opt_max_read, opt_err };
 
 static match_table_t tokens = {
 	{opt_fd, "fd=%u"},
@@ -134,6 +136,8 @@
 	{opt_allow_other, "allow_other"},
 	{opt_kernel_cache, "kernel_cache"},
 	{opt_large_read, "large_read"},
+	{opt_direct_io, "direct_io"},
+	{opt_max_read, "max_read" },
 	{opt_err, NULL}
 };
 
@@ -142,6 +146,7 @@
 	char *p;
 	memset(d, 0, sizeof(struct fuse_mount_data));
 	d->fd = -1;
+	d->max_read = ~0;
 
 	while ((p = strsep(&opt, ",")) != NULL) {
 		int token;
@@ -185,6 +190,17 @@
 		case opt_large_read:
 			d->flags |= FUSE_LARGE_READ;
 			break;
+			
+		case opt_direct_io:
+			d->flags |= FUSE_DIRECT_IO;
+			break;
+
+		case opt_max_read:
+			if (match_int(&args[0], &value))
+				return 0;
+			d->max_read = value;
+			break;
+
 		default:
 			return 0;
 		}
@@ -208,6 +224,10 @@
 		seq_puts(m, ",kernel_cache");
 	if (fc->flags & FUSE_LARGE_READ)
 		seq_puts(m, ",large_read");
+	if (fc->flags & FUSE_DIRECT_IO)
+		seq_puts(m, ",direct_io");
+	if (fc->max_read != ~0)
+		seq_printf(m, ",max_read=%u", fc->max_read);
 	return 0;
 }
 
@@ -314,6 +334,8 @@
 
 	fc->flags = d.flags;
 	fc->uid = d.uid;
+	fc->max_read = d.max_read;
+	fc->max_write = FUSE_MAX_IN / 2;
 	
 	/* fc is needed in fuse_init_file_inode which could be called
 	   from get_root_inode */
diff --git a/lib/fuse.c b/lib/fuse.c
index 4cd8915..0b7da30 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -1061,6 +1061,7 @@
 {
     int res;
     char *path;
+    struct fuse_write_out outarg;
 
     res = -ENOENT;
     path = get_path(f, in->ino);
@@ -1076,17 +1077,12 @@
         free(path);
     }
     
-    if (res > 0) {
-        if ((size_t) res != arg->size) {
-            fprintf(stderr, "short write: %u (should be %u)\n", res,
-                    arg->size);
-            res = -EINVAL;
-        }
-        else 
-            res = 0;
+    if (res >= 0) { 
+        outarg.size = res;
+        res = 0;
     }
 
-    send_reply(f, in, res, NULL, 0);
+    send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
 static int default_statfs(struct statfs *buf)
diff --git a/util/fusermount.c b/util/fusermount.c
index bd52e4a..e837eb2 100644
--- a/util/fusermount.c
+++ b/util/fusermount.c
@@ -46,6 +46,7 @@
     int default_permissions;
     int allow_other;
     int large_read;
+    int direct_io;
 };
 
 static const char *get_user_name()
@@ -318,6 +319,8 @@
         s += sprintf(s, ",allow_other");
     if (opts->large_read)
         s += sprintf(s, ",large_read");
+    if (opts->direct_io)
+        s += sprintf(s, ",direct_io");
 
     res = mount(dev, mnt, type, flags, optbuf);
     if(res == -1)
@@ -496,6 +499,7 @@
             " -x       allow other users to access the files (only for root)\n"
             " -n name  add 'name' as the filesystem name to mtab\n"
             " -l       issue large reads\n"
+            " -r       raw I/O\n"
             " -q       quiet: don't complain if unmount fails\n"
             " -z       lazy unmount\n",
             progname);
@@ -567,6 +571,10 @@
         case 'l':
             opts.large_read = 1;
             break;
+
+        case 'r':
+            opts.direct_io = 1;
+            break;
             
         case 'q':
             quiet = 1;