aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--include/linux/fuse.h4
-rw-r--r--kernel/.cvsignore1
-rw-r--r--kernel/Makefile.in7
-rw-r--r--kernel/dir.c11
-rw-r--r--kernel/file.c333
-rw-r--r--kernel/fuse_i.h15
-rw-r--r--kernel/inode.c24
-rw-r--r--lib/fuse.c14
-rw-r--r--util/fusermount.c8
10 files changed, 320 insertions, 111 deletions
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 @@ struct fuse_write_in {
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 @@ Makefile
*.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 @@ util.o: $(fuse_headers)
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 @@ fuse-objs := dev.o dir.o file.o inode.o util.o
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 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
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 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
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 struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
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 @@ static int fuse_open(struct inode *inode, struct file *file)
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 @@ static int fuse_open(struct inode *inode, struct file *file)
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 @@ static int fuse_open(struct inode *inode, struct file *file)
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 @@ static int fuse_release(struct inode *inode, struct file *file)
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 @@ static int fuse_release(struct inode *inode, struct file *file)
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 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
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 @@ static int fuse_readpage(struct file *file, struct page *page)
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);
+ res = req->out.h.error;
+ if (!res)
+ res = req->out.args[0].size;
+ fuse_put_request(fc, req);
+ return res;
+}
+
+
+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;
}
- 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_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 @@ static int fuse_is_block_uptodate(struct address_space *mapping,
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_is_block_uptodate(struct address_space *mapping,
}
-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 @@ static int fuse_cache_block(struct address_space *mapping,
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_cache_block(struct address_space *mapping,
}
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 @@ static void fuse_file_bigread(struct address_space *mapping,
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;
+
+ 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;
- return generic_file_read(filp, buf, count, ppos);
+ 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 @@ static int write_buffer(struct inode *inode, struct page *page,
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 int get_write_count(struct inode *inode, struct page *page)
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 @@ static int write_buffer_nonblock(struct inode *inode, struct page *page,
{
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 @@ static int write_buffer_nonblock(struct inode *inode, struct page *page,
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 @@ static int fuse_commit_write(struct file *file, struct page *page,
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 @@ permission checking is done in the kernel */
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 @@ struct fuse_req {
/** 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 @@ struct fuse_conn {
/** 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 @@ struct fuse_mount_data {
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 @@ static int fuse_statfs(struct super_block *sb, struct kstatfs *buf)
}
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 @@ static match_table_t tokens = {
{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 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
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 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
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 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
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 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
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 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
{
int res;
char *path;
+ struct fuse_write_out outarg;
res = -ENOENT;
path = get_path(f, in->ino);
@@ -1076,17 +1077,12 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
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 @@ struct fuse_opts {
int default_permissions;
int allow_other;
int large_read;
+ int direct_io;
};
static const char *get_user_name()
@@ -318,6 +319,8 @@ static int do_mount(const char *dev, const char *mnt, const char *type,
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 @@ static void usage()
" -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 @@ int main(int argc, char *argv[])
case 'l':
opts.large_read = 1;
break;
+
+ case 'r':
+ opts.direct_io = 1;
+ break;
case 'q':
quiet = 1;