aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/file.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2004-01-14 16:56:49 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2004-01-14 16:56:49 +0000
commit7c35cf9df254b3d315a0036f8102afa72f0a382d (patch)
tree6dd07298fb2f7365e73280dbf8a16c946ed17bac /kernel/file.c
parenta055d4ea6484742069fc1596bbd0e35fc7fd8782 (diff)
downloadlibfuse-7c35cf9df254b3d315a0036f8102afa72f0a382d.tar.gz
2.6 fixes
Diffstat (limited to 'kernel/file.c')
-rw-r--r--kernel/file.c138
1 files changed, 114 insertions, 24 deletions
diff --git a/kernel/file.c b/kernel/file.c
index e8fe696..0752943 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#ifdef KERNEL_2_6
#include <linux/backing-dev.h>
+#include <linux/writeback.h>
#endif
#ifndef KERNEL_2_6
@@ -57,17 +58,13 @@ static int fuse_release(struct inode *inode, struct file *file)
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in *in = NULL;
struct fuse_open_in *inarg = NULL;
+ unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_open_in);
- in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+ in = kmalloc(s, GFP_NOFS);
if(!in)
return -ENOMEM;
- memset(in, 0, sizeof(struct fuse_in));
-
- inarg = kmalloc(sizeof(struct fuse_open_in), GFP_NOFS);
- if(!inarg)
- goto out_free;
- memset(inarg, 0, sizeof(struct fuse_open_in));
-
+ memset(in, 0, s);
+ inarg = (struct fuse_open_in *) (in + 1);
inarg->flags = file->f_flags & ~O_EXCL;
in->h.opcode = FUSE_RELEASE;
@@ -78,8 +75,6 @@ static int fuse_release(struct inode *inode, struct file *file)
if(!request_send_noreply(fc, in))
return 0;
- out_free:
- kfree(inarg);
kfree(in);
return 0;
}
@@ -102,6 +97,10 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
in.args[0].value = &inarg;
request_send(fc, &in, &out);
return out.h.error;
+
+ /* FIXME: need to ensure, that all write requests issued
+ before this request are completed. Should userspace take
+ care of this? */
}
static int fuse_readpage(struct file *file, struct page *page)
@@ -302,39 +301,130 @@ static int write_buffer(struct inode *inode, struct page *page,
in.args[1].size = count;
in.args[1].value = buffer + offset;
request_send(fc, &in, &out);
-
kunmap(page);
+ if(out.h.error)
+ SetPageError(page);
return out.h.error;
}
-#ifdef KERNEL_2_6
-static int fuse_writepage(struct page *page, struct writeback_control *wbc)
-#else
-static int fuse_writepage(struct page *page)
-#endif
+static int get_write_count(struct inode *inode, struct page *page)
{
- struct inode *inode = page->mapping->host;
- unsigned count;
unsigned long end_index;
- int err;
+ int count;
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;
+ return 0;
+ }
+ return count;
+}
+#ifdef KERNEL_2_6
+
+static void write_buffer_end(struct fuse_conn *fc, struct fuse_in *in,
+ struct fuse_out *out, void *_page)
+{
+ struct page *page = (struct page *) _page;
+
+ lock_page(page);
+ if(out->h.error) {
+ SetPageError(page);
+ if(out->h.error == -ENOSPC)
+ set_bit(AS_ENOSPC, &page->mapping->flags);
+ else
+ set_bit(AS_EIO, &page->mapping->flags);
}
- err = write_buffer(inode, page, 0, count);
- out:
+ end_page_writeback(page);
+ kunmap(page);
unlock_page(page);
- return 0;
+ kfree(in);
}
+static int write_buffer_nonblock(struct inode *inode, struct page *page,
+ unsigned offset, size_t count)
+{
+ int err;
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in *in = NULL;
+ struct fuse_out *out = NULL;
+ struct fuse_write_in *inarg = NULL;
+ char *buffer;
+ unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_out) +
+ sizeof(struct fuse_write_in);
+
+ in = kmalloc(s, GFP_NOFS);
+ if(!in)
+ return -ENOMEM;
+ memset(in, 0, s);
+ out = (struct fuse_out *)(in + 1);
+ inarg = (struct fuse_write_in *)(out + 1);
+
+ buffer = kmap(page);
+
+ inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset;
+ inarg->size = count;
+
+ in->h.opcode = FUSE_WRITE;
+ in->h.ino = inode->i_ino;
+ in->numargs = 2;
+ in->args[0].size = sizeof(struct fuse_write_in);
+ in->args[0].value = inarg;
+ in->args[1].size = count;
+ in->args[1].value = buffer + offset;
+ err = request_send_nonblock(fc, in, out, write_buffer_end, page);
+ if(err) {
+ if(err != -EWOULDBLOCK)
+ SetPageError(page);
+ kunmap(page);
+ kfree(in);
+ }
+ return err;
+}
+
+static int fuse_writepage(struct page *page, struct writeback_control *wbc)
+{
+ int err;
+ struct inode *inode = page->mapping->host;
+ unsigned count = get_write_count(inode, page);
+
+ err = -EINVAL;
+ if(count) {
+ /* FIXME: check sync_mode, and wait for previous writes (or
+ signal userspace to do this) */
+ if(wbc->nonblocking) {
+ err = write_buffer_nonblock(inode, page, 0, count);
+ if(!err)
+ SetPageWriteback(page);
+ else if(err == -EWOULDBLOCK) {
+ __set_page_dirty_nobuffers(page);
+ err = 0;
+ }
+ } else
+ err = write_buffer(inode, page, 0, count);
+ }
+
+ unlock_page(page);
+ return err;
+}
+#else
+static int fuse_writepage(struct page *page)
+{
+ int err;
+ struct inode *inode = page->mapping->host;
+ int count = get_write_count(inode, page);
+ err = -EINVAL;
+ if(count)
+ err = write_buffer(inode, page, 0, count);
+
+ unlock_page(page);
+ return err;
+}
+#endif
static int fuse_prepare_write(struct file *file, struct page *page,
unsigned offset, unsigned to)