aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/file.c')
-rw-r--r--kernel/file.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/kernel/file.c b/kernel/file.c
index df3a863..a677d4c 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pagemap.h>
+#include <linux/slab.h>
static int fuse_open(struct inode *inode, struct file *file)
@@ -25,10 +26,13 @@ static int fuse_open(struct inode *inode, struct file *file)
in.argsize = sizeof(arg);
in.arg = &arg;
request_send(fc, &in, &out);
+ if(!out.h.error)
+ invalidate_inode_pages(inode);
return out.h.error;
}
+
static int fuse_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
@@ -65,13 +69,96 @@ static int fuse_readpage(struct file *file, struct page *page)
return out.h.error;
}
+static int write_buffer(struct inode *inode, struct page *page,
+ unsigned offset, size_t count)
+{
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_write_in *arg;
+ size_t argsize;
+ char *buffer;
+
+ argsize = offsetof(struct fuse_write_in, buf) + count;
+ arg = kmalloc(argsize, GFP_KERNEL);
+ if(!arg)
+ return -ENOMEM;
+
+ arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+ arg->size = count;
+ buffer = kmap(page);
+ memcpy(arg->buf, buffer + offset, count);
+ kunmap(page);
+
+ in.h.opcode = FUSE_WRITE;
+ in.h.ino = inode->i_ino;
+ in.argsize = argsize;
+ in.arg = arg;
+ request_send(fc, &in, &out);
+ kfree(arg);
+
+ return out.h.error;
+}
+
+
+static int fuse_writepage(struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned count;
+ unsigned long end_index;
+ int err;
+
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+ if(page->index < end_index)
+ count = PAGE_CACHE_SIZE;
+ else {
+ count = inode->i_size & (PAGE_CACHE_SIZE - 1);
+ err = -EIO;
+ if(page->index > end_index || count == 0)
+ goto out;
+
+ }
+ err = write_buffer(inode, page, 0, count);
+ out:
+ UnlockPage(page);
+ return 0;
+}
+
+
+static int fuse_prepare_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ /* No op */
+ return 0;
+}
+
+static int fuse_commit_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ int err;
+ struct inode *inode = page->mapping->host;
+
+ err = write_buffer(inode, page, offset, to - offset);
+ if(!err) {
+ loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to;
+ if(pos > inode->i_size)
+ inode->i_size = pos;
+ }
+ return err;
+}
+
static struct file_operations fuse_file_operations = {
open: fuse_open,
read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
};
static struct address_space_operations fuse_file_aops = {
readpage: fuse_readpage,
+ writepage: fuse_writepage,
+ prepare_write: fuse_prepare_write,
+ commit_write: fuse_commit_write,
};
void fuse_init_file_inode(struct inode *inode)