* [patch 01/12] fuse: fix allowing operations
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 02/12] fuse: fix race between getattr and write Miklos Szeredi
` (10 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_allow_fixes.patch --]
[-- Type: text/plain, Size: 2389 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
The following operation didn't check if sending the request was
allowed:
setattr
listxattr
statfs
Some other operations don't explicitly do the check, but VFS calls
->permission() which checks this.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:00.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
@@ -721,7 +721,7 @@ static int fuse_refresh_attributes(struc
* for which the owner of the mount has ptrace privilege. This
* excludes processes started by other users, suid or sgid processes.
*/
-static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
+int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
{
if (fc->flags & FUSE_ALLOW_OTHER)
return 1;
@@ -1005,6 +1005,9 @@ static int fuse_setattr(struct dentry *e
struct fuse_attr_out outarg;
int err;
+ if (!fuse_allow_task(fc, current))
+ return -EACCES;
+
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
err = inode_change_ok(inode, attr);
if (err)
@@ -1172,6 +1175,9 @@ static ssize_t fuse_listxattr(struct den
struct fuse_getxattr_out outarg;
ssize_t ret;
+ if (!fuse_allow_task(fc, current))
+ return -EACCES;
+
if (fc->no_listxattr)
return -EOPNOTSUPP;
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-09-25 21:19:00.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-09-25 21:19:13.000000000 +0200
@@ -565,3 +565,8 @@ void fuse_ctl_remove_conn(struct fuse_co
* Is file type valid?
*/
int fuse_valid_type(int m);
+
+/**
+ * Is task allowed to perform filesystem operation?
+ */
+int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task);
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-09-25 21:19:00.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-09-25 21:19:13.000000000 +0200
@@ -287,6 +287,11 @@ static int fuse_statfs(struct dentry *de
struct fuse_statfs_out outarg;
int err;
+ if (!fuse_allow_task(fc, current)) {
+ buf->f_type = FUSE_SUPER_MAGIC;
+ return 0;
+ }
+
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 02/12] fuse: fix race between getattr and write
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
2007-10-02 15:50 ` [patch 01/12] fuse: fix allowing operations Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-04 22:43 ` Andrew Morton
2007-10-02 15:50 ` [patch 03/12] fuse: add file handle to getattr operation Miklos Szeredi
` (9 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, Jakub Bogusz
[-- Attachment #1: fuse_attr_version.patch --]
[-- Type: text/plain, Size: 13970 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Getattr and lookup operations can be running in parallel to attribute
changing operations, such as write and setattr.
This means, that if for example getattr was slower than a write, the
cached size attribute could be set to a stale value.
To prevent this race, introduce a per-filesystem attribute version
counter. This counter is incremented whenever cached attributes are
modified, and the incremented value stored in the inode.
Before storing new attributes in the cache, getattr and lookup check,
using the version number, whether the attributes have been modified
during the request's lifetime. If so, the returned attributes are not
cached, because they might be stale.
Thanks to Jakub Bogusz for the bug report and test program.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
@@ -63,13 +63,21 @@ static u64 time_to_jiffies(unsigned long
* Set dentry and possibly attribute timeouts from the lookup/mk*
* replies
*/
-static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o)
+static void fuse_change_entry_timeout(struct dentry *entry,
+ struct fuse_entry_out *o)
{
fuse_dentry_settime(entry,
time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
- if (entry->d_inode)
- get_fuse_inode(entry->d_inode)->i_time =
- time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
+}
+
+static u64 attr_timeout(struct fuse_attr_out *o)
+{
+ return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
+}
+
+static u64 entry_attr_timeout(struct fuse_entry_out *o)
+{
+ return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
}
/*
@@ -140,6 +148,7 @@ static int fuse_dentry_revalidate(struct
struct fuse_req *req;
struct fuse_req *forget_req;
struct dentry *parent;
+ u64 attr_version;
/* For negative dentries, always do a fresh lookup */
if (!inode)
@@ -156,6 +165,10 @@ static int fuse_dentry_revalidate(struct
return 0;
}
+ spin_lock(&fc->lock);
+ attr_version = fc->attr_version;
+ spin_unlock(&fc->lock);
+
parent = dget_parent(entry);
fuse_lookup_init(req, parent->d_inode, entry, &outarg);
request_send(fc, req);
@@ -180,8 +193,10 @@ static int fuse_dentry_revalidate(struct
if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
return 0;
- fuse_change_attributes(inode, &outarg.attr);
- fuse_change_timeout(entry, &outarg);
+ fuse_change_attributes(inode, &outarg.attr,
+ entry_attr_timeout(&outarg),
+ attr_version);
+ fuse_change_entry_timeout(entry, &outarg);
}
return 1;
}
@@ -228,6 +243,7 @@ static struct dentry *fuse_lookup(struct
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req;
struct fuse_req *forget_req;
+ u64 attr_version;
if (entry->d_name.len > FUSE_NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
@@ -242,6 +258,10 @@ static struct dentry *fuse_lookup(struct
return ERR_PTR(PTR_ERR(forget_req));
}
+ spin_lock(&fc->lock);
+ attr_version = fc->attr_version;
+ spin_unlock(&fc->lock);
+
fuse_lookup_init(req, dir, entry, &outarg);
request_send(fc, req);
err = req->out.h.error;
@@ -253,7 +273,8 @@ static struct dentry *fuse_lookup(struct
err = -EIO;
if (!err && outarg.nodeid) {
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
- &outarg.attr);
+ &outarg.attr, entry_attr_timeout(&outarg),
+ attr_version);
if (!inode) {
fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
return ERR_PTR(-ENOMEM);
@@ -276,7 +297,7 @@ static struct dentry *fuse_lookup(struct
entry->d_op = &fuse_dentry_operations;
if (!err)
- fuse_change_timeout(entry, &outarg);
+ fuse_change_entry_timeout(entry, &outarg);
else
fuse_invalidate_entry_cache(entry);
return NULL;
@@ -363,7 +384,7 @@ static int fuse_create_open(struct inode
fuse_put_request(fc, req);
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
- &outentry.attr);
+ &outentry.attr, entry_attr_timeout(&outentry), 0);
if (!inode) {
flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
ff->fh = outopen.fh;
@@ -373,7 +394,7 @@ static int fuse_create_open(struct inode
}
fuse_put_request(fc, forget_req);
d_instantiate(entry, inode);
- fuse_change_timeout(entry, &outentry);
+ fuse_change_entry_timeout(entry, &outentry);
file = lookup_instantiate_filp(nd, entry, generic_file_open);
if (IS_ERR(file)) {
ff->fh = outopen.fh;
@@ -428,7 +449,7 @@ static int create_new_entry(struct fuse_
goto out_put_forget_req;
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
- &outarg.attr);
+ &outarg.attr, entry_attr_timeout(&outarg), 0);
if (!inode) {
fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
return -ENOMEM;
@@ -451,7 +472,7 @@ static int create_new_entry(struct fuse_
} else
d_instantiate(entry, inode);
- fuse_change_timeout(entry, &outarg);
+ fuse_change_entry_timeout(entry, &outarg);
fuse_invalidate_attr(dir);
return 0;
@@ -663,15 +684,43 @@ static int fuse_link(struct dentry *entr
return err;
}
-static int fuse_do_getattr(struct inode *inode)
+static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
+ struct kstat *stat)
+{
+ stat->dev = inode->i_sb->s_dev;
+ stat->ino = attr->ino;
+ stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
+ stat->nlink = attr->nlink;
+ stat->uid = attr->uid;
+ stat->gid = attr->gid;
+ stat->rdev = inode->i_rdev;
+ stat->atime.tv_sec = attr->atime;
+ stat->atime.tv_nsec = attr->atimensec;
+ stat->mtime.tv_sec = attr->mtime;
+ stat->mtime.tv_nsec = attr->mtimensec;
+ stat->ctime.tv_sec = attr->ctime;
+ stat->ctime.tv_nsec = attr->ctimensec;
+ stat->size = attr->size;
+ stat->blocks = attr->blocks;
+ stat->blksize = (1 << inode->i_blkbits);
+}
+
+static int fuse_do_getattr(struct inode *inode, struct kstat *stat)
{
int err;
struct fuse_attr_out arg;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req = fuse_get_req(fc);
+ struct fuse_req *req;
+ u64 attr_version;
+
+ req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);
+ spin_lock(&fc->lock);
+ attr_version = fc->attr_version;
+ spin_unlock(&fc->lock);
+
req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
req->out.numargs = 1;
@@ -685,30 +734,17 @@ static int fuse_do_getattr(struct inode
make_bad_inode(inode);
err = -EIO;
} else {
- struct fuse_inode *fi = get_fuse_inode(inode);
- fuse_change_attributes(inode, &arg.attr);
- fi->i_time = time_to_jiffies(arg.attr_valid,
- arg.attr_valid_nsec);
+ fuse_change_attributes(inode, &arg.attr,
+ attr_timeout(&arg),
+ attr_version);
+ if (stat)
+ fuse_fillattr(inode, &arg.attr, stat);
}
}
return err;
}
/*
- * Check if attributes are still valid, and if not send a GETATTR
- * request to refresh them.
- */
-static int fuse_refresh_attributes(struct inode *inode)
-{
- struct fuse_inode *fi = get_fuse_inode(inode);
-
- if (fi->i_time < get_jiffies_64())
- return fuse_do_getattr(inode);
- else
- return 0;
-}
-
-/*
* Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the requester process. This
* means, that the filesystem daemon is able to record the exact
@@ -795,11 +831,14 @@ static int fuse_permission(struct inode
*/
if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
- err = fuse_refresh_attributes(inode);
- if (err)
- return err;
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ if (fi->i_time < get_jiffies_64()) {
+ err = fuse_do_getattr(inode, NULL);
+ if (err)
+ return err;
- refreshed = true;
+ refreshed = true;
+ }
}
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
@@ -809,7 +848,7 @@ static int fuse_permission(struct inode
attributes. This is also needed, because the root
node will at first have no permissions */
if (err == -EACCES && !refreshed) {
- err = fuse_do_getattr(inode);
+ err = fuse_do_getattr(inode, NULL);
if (!err)
err = generic_permission(inode, mask, NULL);
}
@@ -825,7 +864,7 @@ static int fuse_permission(struct inode
if (refreshed)
return -EACCES;
- err = fuse_do_getattr(inode);
+ err = fuse_do_getattr(inode, NULL);
if (!err && !(inode->i_mode & S_IXUGO))
return -EACCES;
}
@@ -999,7 +1038,6 @@ static int fuse_setattr(struct dentry *e
{
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_req *req;
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
@@ -1053,8 +1091,7 @@ static int fuse_setattr(struct dentry *e
return -EIO;
}
- fuse_change_attributes(inode, &outarg.attr);
- fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec);
+ fuse_change_attributes(inode, &outarg.attr, attr_timeout(&outarg), 0);
return 0;
}
@@ -1069,8 +1106,10 @@ static int fuse_getattr(struct vfsmount
if (!fuse_allow_task(fc, current))
return -EACCES;
- err = fuse_refresh_attributes(inode);
- if (!err) {
+ if (fi->i_time < get_jiffies_64())
+ err = fuse_do_getattr(inode, stat);
+ else {
+ err = 0;
generic_fillattr(inode, stat);
stat->mode = fi->orig_i_mode;
}
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-09-25 21:19:00.000000000 +0200
+++ linux/fs/fuse/file.c 2007-09-25 21:19:13.000000000 +0200
@@ -478,6 +478,7 @@ static int fuse_buffered_write(struct fi
int err;
size_t nres;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
struct fuse_req *req;
@@ -499,6 +500,7 @@ static int fuse_buffered_write(struct fi
if (!err) {
pos += nres;
spin_lock(&fc->lock);
+ fi->attr_version = ++fc->attr_version;
if (pos > inode->i_size)
i_size_write(inode, pos);
spin_unlock(&fc->lock);
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-09-25 21:19:13.000000000 +0200
@@ -67,6 +67,9 @@ struct fuse_inode {
/** The sticky bit in inode->i_mode may have been removed, so
preserve the original mode */
mode_t orig_i_mode;
+
+ /** Version of last attribute change */
+ u64 attr_version;
};
/** FUSE specific file data */
@@ -387,6 +390,9 @@ struct fuse_conn {
/** Reserved request for the DESTROY message */
struct fuse_req *destroy_req;
+
+ /** Version counter for attribute changes */
+ u64 attr_version;
};
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
@@ -416,7 +422,8 @@ extern const struct file_operations fuse
* Get a filled in inode
*/
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
- int generation, struct fuse_attr *attr);
+ int generation, struct fuse_attr *attr,
+ u64 attr_valid, u64 attr_version);
/**
* Send FORGET command
@@ -477,7 +484,8 @@ void fuse_init_symlink(struct inode *ino
/**
* Change attributes of an inode
*/
-void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr);
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+ u64 attr_valid, u64 attr_version);
/**
* Initialize the client device
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-09-25 21:19:13.000000000 +0200
@@ -117,12 +117,22 @@ static void fuse_truncate(struct address
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
}
-void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+ u64 attr_valid, u64 attr_version)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
loff_t oldsize;
+ spin_lock(&fc->lock);
+ if (attr_version != 0 && fi->attr_version > attr_version) {
+ spin_unlock(&fc->lock);
+ return;
+ }
+ fi->attr_version = ++fc->attr_version;
+ fi->i_time = attr_valid;
+
inode->i_ino = attr->ino;
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
inode->i_nlink = attr->nlink;
@@ -145,7 +155,6 @@ void fuse_change_attributes(struct inode
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
inode->i_mode &= ~S_ISVTX;
- spin_lock(&fc->lock);
oldsize = inode->i_size;
i_size_write(inode, attr->size);
spin_unlock(&fc->lock);
@@ -194,7 +203,8 @@ static int fuse_inode_set(struct inode *
}
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
- int generation, struct fuse_attr *attr)
+ int generation, struct fuse_attr *attr,
+ u64 attr_valid, u64 attr_version)
{
struct inode *inode;
struct fuse_inode *fi;
@@ -222,7 +232,8 @@ struct inode *fuse_iget(struct super_blo
spin_lock(&fc->lock);
fi->nlookup ++;
spin_unlock(&fc->lock);
- fuse_change_attributes(inode, attr);
+ fuse_change_attributes(inode, attr, attr_valid, attr_version);
+
return inode;
}
@@ -474,6 +485,7 @@ static struct fuse_conn *new_conn(void)
}
fc->reqctr = 0;
fc->blocked = 1;
+ fc->attr_version = 1;
get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
}
out:
@@ -505,7 +517,7 @@ static struct inode *get_root_inode(stru
attr.mode = mode;
attr.ino = FUSE_ROOT_ID;
attr.nlink = 1;
- return fuse_iget(sb, 1, 0, &attr);
+ return fuse_iget(sb, 1, 0, &attr, 0, 0);
}
static const struct super_operations fuse_super_operations = {
--
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [patch 02/12] fuse: fix race between getattr and write
2007-10-02 15:50 ` [patch 02/12] fuse: fix race between getattr and write Miklos Szeredi
@ 2007-10-04 22:43 ` Andrew Morton
2007-10-04 22:53 ` Miklos Szeredi
0 siblings, 1 reply; 19+ messages in thread
From: Andrew Morton @ 2007-10-04 22:43 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: linux-kernel, jakub.bogusz
On Tue, 02 Oct 2007 17:50:28 +0200
Miklos Szeredi <miklos@szeredi.hu> wrote:
> @@ -228,6 +243,7 @@ static struct dentry *fuse_lookup(struct
> struct fuse_conn *fc = get_fuse_conn(dir);
> struct fuse_req *req;
> struct fuse_req *forget_req;
> + u64 attr_version;
>
> if (entry->d_name.len > FUSE_NAME_MAX)
> return ERR_PTR(-ENAMETOOLONG);
> @@ -242,6 +258,10 @@ static struct dentry *fuse_lookup(struct
> return ERR_PTR(PTR_ERR(forget_req));
> }
>
> + spin_lock(&fc->lock);
> + attr_version = fc->attr_version;
> + spin_unlock(&fc->lock);
You might want to do this (oft-repeated) operation in a little helper
function.
Because I suspect that the lock isn't needed if CONFIG_64BIT=y.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [patch 02/12] fuse: fix race between getattr and write
2007-10-04 22:43 ` Andrew Morton
@ 2007-10-04 22:53 ` Miklos Szeredi
0 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-04 22:53 UTC (permalink / raw)
To: akpm; +Cc: miklos, linux-kernel, jakub.bogusz
> > @@ -228,6 +243,7 @@ static struct dentry *fuse_lookup(struct
> > struct fuse_conn *fc = get_fuse_conn(dir);
> > struct fuse_req *req;
> > struct fuse_req *forget_req;
> > + u64 attr_version;
> >
> > if (entry->d_name.len > FUSE_NAME_MAX)
> > return ERR_PTR(-ENAMETOOLONG);
> > @@ -242,6 +258,10 @@ static struct dentry *fuse_lookup(struct
> > return ERR_PTR(PTR_ERR(forget_req));
> > }
> >
> > + spin_lock(&fc->lock);
> > + attr_version = fc->attr_version;
> > + spin_unlock(&fc->lock);
>
> You might want to do this (oft-repeated) operation in a little helper
> function.
>
> Because I suspect that the lock isn't needed if CONFIG_64BIT=y.
You're perfectly right, although fuse is not yet at the stage, where
I'd bother too much with scalability optimizations like that ;)
But it's a good cleanup, and I'll do an incremental patch on top of
this if that's OK.
Miklos
^ permalink raw reply [flat|nested] 19+ messages in thread
* [patch 03/12] fuse: add file handle to getattr operation
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
2007-10-02 15:50 ` [patch 01/12] fuse: fix allowing operations Miklos Szeredi
2007-10-02 15:50 ` [patch 02/12] fuse: fix race between getattr and write Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 04/12] fuse: clean up open file passing in setattr Miklos Szeredi
` (8 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_fgetattr.patch --]
[-- Type: text/plain, Size: 5596 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Add necessary protocol changes for supplying a file handle with the
getattr operation. Step the API version to 7.9.
This patch doesn't actually supply the file handle, because that needs
some kind of VFS support, which we haven't yet been able to agree
upon.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-25 21:19:00.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-25 21:19:13.000000000 +0200
@@ -6,7 +6,14 @@
See the file COPYING.
*/
-/* This file defines the kernel interface of FUSE */
+/*
+ * This file defines the kernel interface of FUSE
+ *
+ * Protocol changelog:
+ *
+ * 7.9:
+ * - new fuse_getattr_in input argument of GETATTR
+ */
#include <asm/types.h>
#include <linux/major.h>
@@ -15,7 +22,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 8
+#define FUSE_KERNEL_MINOR_VERSION 9
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -91,12 +98,18 @@ struct fuse_file_lock {
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
+#define FUSE_FILE_OPS (1 << 2)
/**
* Release flags
*/
#define FUSE_RELEASE_FLUSH (1 << 0)
+/**
+ * Getattr flags
+ */
+#define FUSE_GETATTR_FH (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -154,6 +167,12 @@ struct fuse_forget_in {
__u64 nlookup;
};
+struct fuse_getattr_in {
+ __u32 getattr_flags;
+ __u32 dummy;
+ __u64 fh;
+};
+
struct fuse_attr_out {
__u64 attr_valid; /* Cache timeout for the attributes */
__u32 attr_valid_nsec;
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
@@ -705,10 +705,12 @@ static void fuse_fillattr(struct inode *
stat->blksize = (1 << inode->i_blkbits);
}
-static int fuse_do_getattr(struct inode *inode, struct kstat *stat)
+static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
+ struct file *file)
{
int err;
- struct fuse_attr_out arg;
+ struct fuse_getattr_in inarg;
+ struct fuse_attr_out outarg;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
u64 attr_version;
@@ -721,24 +723,35 @@ static int fuse_do_getattr(struct inode
attr_version = fc->attr_version;
spin_unlock(&fc->lock);
+ memset(&inarg, 0, sizeof(inarg));
+ /* Directories have separate file-handle space */
+ if (file && S_ISREG(inode->i_mode)) {
+ struct fuse_file *ff = file->private_data;
+
+ inarg.getattr_flags |= FUSE_GETATTR_FH;
+ inarg.fh = ff->fh;
+ }
req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->in.numargs = 1;
+ req->in.args[0].size = sizeof(inarg);
+ req->in.args[0].value = &inarg;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(arg);
- req->out.args[0].value = &arg;
+ req->out.args[0].size = sizeof(outarg);
+ req->out.args[0].value = &outarg;
request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
- if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
+ if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
make_bad_inode(inode);
err = -EIO;
} else {
- fuse_change_attributes(inode, &arg.attr,
- attr_timeout(&arg),
+ fuse_change_attributes(inode, &outarg.attr,
+ attr_timeout(&outarg),
attr_version);
if (stat)
- fuse_fillattr(inode, &arg.attr, stat);
+ fuse_fillattr(inode, &outarg.attr, stat);
}
}
return err;
@@ -833,7 +846,7 @@ static int fuse_permission(struct inode
((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
struct fuse_inode *fi = get_fuse_inode(inode);
if (fi->i_time < get_jiffies_64()) {
- err = fuse_do_getattr(inode, NULL);
+ err = fuse_do_getattr(inode, NULL, NULL);
if (err)
return err;
@@ -848,7 +861,7 @@ static int fuse_permission(struct inode
attributes. This is also needed, because the root
node will at first have no permissions */
if (err == -EACCES && !refreshed) {
- err = fuse_do_getattr(inode, NULL);
+ err = fuse_do_getattr(inode, NULL, NULL);
if (!err)
err = generic_permission(inode, mask, NULL);
}
@@ -864,7 +877,7 @@ static int fuse_permission(struct inode
if (refreshed)
return -EACCES;
- err = fuse_do_getattr(inode, NULL);
+ err = fuse_do_getattr(inode, NULL, NULL);
if (!err && !(inode->i_mode & S_IXUGO))
return -EACCES;
}
@@ -1107,7 +1120,7 @@ static int fuse_getattr(struct vfsmount
return -EACCES;
if (fi->i_time < get_jiffies_64())
- err = fuse_do_getattr(inode, stat);
+ err = fuse_do_getattr(inode, stat, NULL);
else {
err = 0;
generic_fillattr(inode, stat);
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-09-25 21:19:13.000000000 +0200
@@ -570,7 +570,7 @@ static void fuse_send_init(struct fuse_c
arg->major = FUSE_KERNEL_VERSION;
arg->minor = FUSE_KERNEL_MINOR_VERSION;
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
- arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS;
+ arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_FILE_OPS;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 04/12] fuse: clean up open file passing in setattr
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (2 preceding siblings ...)
2007-10-02 15:50 ` [patch 03/12] fuse: add file handle to getattr operation Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 05/12] VFS: allow filesystems to implement atomic open+truncate Miklos Szeredi
` (7 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_fsetattr.patch --]
[-- Type: text/plain, Size: 1959 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Clean up supplying open file to the setattr operation. In addition to
being a cleanup it prepares for the changes in the way the open file
is passed to the setattr method.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
@@ -1032,11 +1032,6 @@ static void iattr_to_fattr(struct iattr
arg->atime = iattr->ia_atime.tv_sec;
arg->mtime = iattr->ia_mtime.tv_sec;
}
- if (ivalid & ATTR_FILE) {
- struct fuse_file *ff = iattr->ia_file->private_data;
- arg->valid |= FATTR_FH;
- arg->fh = ff->fh;
- }
}
/*
@@ -1047,7 +1042,8 @@ static void iattr_to_fattr(struct iattr
* vmtruncate() doesn't allow for this case, so do the rlimit checking
* and the actual truncation by hand.
*/
-static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+ struct file *file)
{
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1082,6 +1078,11 @@ static int fuse_setattr(struct dentry *e
memset(&inarg, 0, sizeof(inarg));
iattr_to_fattr(attr, &inarg);
+ if (file) {
+ struct fuse_file *ff = file->private_data;
+ inarg.valid |= FATTR_FH;
+ inarg.fh = ff->fh;
+ }
req->in.h.opcode = FUSE_SETATTR;
req->in.h.nodeid = get_node_id(inode);
req->in.numargs = 1;
@@ -1108,6 +1109,14 @@ static int fuse_setattr(struct dentry *e
return 0;
}
+static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+{
+ if (attr->ia_valid & ATTR_FILE)
+ return fuse_do_setattr(entry, attr, attr->ia_file);
+ else
+ return fuse_do_setattr(entry, attr, NULL);
+}
+
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
struct kstat *stat)
{
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 05/12] VFS: allow filesystems to implement atomic open+truncate
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (3 preceding siblings ...)
2007-10-02 15:50 ` [patch 04/12] fuse: clean up open file passing in setattr Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 06/12] fuse: improve utimes support Miklos Szeredi
` (6 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: vfs_atomic_open_truncate.patch --]
[-- Type: text/plain, Size: 1476 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Add a new attribute flag ATTR_OPEN, with the meaning: "truncation was
initiated by open() due to the O_TRUNC flag".
This way filesystems wanting to implement truncation within their
->open() method can ignore such truncate requests.
This is a quick & dirty hack, but it comes for free.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/namei.c
===================================================================
--- linux.orig/fs/namei.c 2007-09-25 21:32:04.000000000 +0200
+++ linux/fs/namei.c 2007-09-25 21:56:30.000000000 +0200
@@ -1667,8 +1667,10 @@ int may_open(struct nameidata *nd, int a
error = locks_verify_locked(inode);
if (!error) {
DQUOT_INIT(inode);
-
- error = do_truncate(dentry, 0, ATTR_MTIME|ATTR_CTIME, NULL);
+
+ error = do_truncate(dentry, 0,
+ ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
+ NULL);
}
put_write_access(inode);
if (error)
Index: linux/include/linux/fs.h
===================================================================
--- linux.orig/include/linux/fs.h 2007-09-25 21:32:05.000000000 +0200
+++ linux/include/linux/fs.h 2007-09-25 21:56:30.000000000 +0200
@@ -337,6 +337,7 @@ typedef void (dio_iodone_t)(struct kiocb
#define ATTR_KILL_SGID 4096
#define ATTR_FILE 8192
#define ATTR_KILL_PRIV 16384
+#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */
/*
* This is the Inode Attributes structure, used for notify_change(). It
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 06/12] fuse: improve utimes support
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (4 preceding siblings ...)
2007-10-02 15:50 ` [patch 05/12] VFS: allow filesystems to implement atomic open+truncate Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 07/12] fuse: add atomic open+truncate support Miklos Szeredi
` (5 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_utimes_update.patch --]
[-- Type: text/plain, Size: 2491 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Add two new flags for setattr: FATTR_ATIME_NOW and FATTR_MTIME_NOW.
These mean, that atime or mtime should be changed to the current time.
Also it is now possible to update atime or mtime individually, not
just together.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:13.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:14.000000000 +0200
@@ -1014,6 +1014,20 @@ static int fuse_dir_fsync(struct file *f
return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
}
+static bool update_mtime(unsigned ivalid)
+{
+ /* Always update if mtime is explicitly set */
+ if (ivalid & ATTR_MTIME_SET)
+ return true;
+
+ /* If it's an open(O_TRUNC) or an ftruncate(), don't update */
+ if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
+ return false;
+
+ /* In all other cases update */
+ return true;
+}
+
static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
{
unsigned ivalid = iattr->ia_valid;
@@ -1026,11 +1040,19 @@ static void iattr_to_fattr(struct iattr
arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid;
if (ivalid & ATTR_SIZE)
arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size;
- /* You can only _set_ these together (they may change by themselves) */
- if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
- arg->valid |= FATTR_ATIME | FATTR_MTIME;
+ if (ivalid & ATTR_ATIME) {
+ arg->valid |= FATTR_ATIME;
arg->atime = iattr->ia_atime.tv_sec;
+ arg->atimensec = iattr->ia_atime.tv_nsec;
+ if (!(ivalid & ATTR_ATIME_SET))
+ arg->valid |= FATTR_ATIME_NOW;
+ }
+ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+ arg->valid |= FATTR_MTIME;
arg->mtime = iattr->ia_mtime.tv_sec;
+ arg->mtimensec = iattr->ia_mtime.tv_nsec;
+ if (!(ivalid & ATTR_MTIME_SET))
+ arg->valid |= FATTR_MTIME_NOW;
}
}
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-25 21:19:13.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-25 21:19:14.000000000 +0200
@@ -83,6 +83,8 @@ struct fuse_file_lock {
#define FATTR_ATIME (1 << 4)
#define FATTR_MTIME (1 << 5)
#define FATTR_FH (1 << 6)
+#define FATTR_ATIME_NOW (1 << 7)
+#define FATTR_MTIME_NOW (1 << 8)
/**
* Flags returned by the OPEN request
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 07/12] fuse: add atomic open+truncate support
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (5 preceding siblings ...)
2007-10-02 15:50 ` [patch 06/12] fuse: improve utimes support Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 08/12] fuse: support BSD locking semantics Miklos Szeredi
` (4 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_atomic_open_truncate.patch --]
[-- Type: text/plain, Size: 3375 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
This patch allows fuse filesystems to implement open(..., O_TRUNC) as
a single request, instead of separate truncate and open requests.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-26 23:32:53.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-27 00:41:38.000000000 +0200
@@ -1083,6 +1083,9 @@ static int fuse_do_setattr(struct dentry
return err;
}
+ if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc)
+ return 0;
+
if (attr->ia_valid & ATTR_SIZE) {
unsigned long limit;
if (IS_SWAPFILE(inode))
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-09-26 23:32:53.000000000 +0200
+++ linux/fs/fuse/file.c 2007-09-27 00:41:41.000000000 +0200
@@ -28,7 +28,9 @@ static int fuse_send_open(struct inode *
return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
- inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+ inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
+ if (!fc->atomic_o_trunc)
+ inarg.flags &= ~O_TRUNC;
req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
req->in.h.nodeid = get_node_id(inode);
req->in.numargs = 1;
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-09-26 23:32:53.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-09-27 00:41:40.000000000 +0200
@@ -320,6 +320,9 @@ struct fuse_conn {
/** Do readpages asynchronously? Only set in INIT */
unsigned async_read : 1;
+ /** Do not send separate SETATTR request before open(O_TRUNC) */
+ unsigned atomic_o_trunc : 1;
+
/*
* The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-09-26 23:32:53.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-09-27 00:41:40.000000000 +0200
@@ -548,6 +548,8 @@ static void process_init_reply(struct fu
fc->async_read = 1;
if (!(arg->flags & FUSE_POSIX_LOCKS))
fc->no_lock = 1;
+ if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+ fc->atomic_o_trunc = 1;
} else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1;
@@ -570,7 +572,8 @@ static void fuse_send_init(struct fuse_c
arg->major = FUSE_KERNEL_VERSION;
arg->minor = FUSE_KERNEL_MINOR_VERSION;
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
- arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_FILE_OPS;
+ arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_FILE_OPS |
+ FUSE_ATOMIC_O_TRUNC;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-26 23:32:53.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-27 00:41:47.000000000 +0200
@@ -101,6 +101,7 @@ struct fuse_file_lock {
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
#define FUSE_FILE_OPS (1 << 2)
+#define FUSE_ATOMIC_O_TRUNC (1 << 3)
/**
* Release flags
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 08/12] fuse: support BSD locking semantics
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (6 preceding siblings ...)
2007-10-02 15:50 ` [patch 07/12] fuse: add atomic open+truncate support Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 09/12] fuse: add list of writable files to fuse_inode Miklos Szeredi
` (3 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_flock.patch --]
[-- Type: text/plain, Size: 4013 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
It is trivial to add support for flock(2) semantics to the existing
protocol, by setting the lock owner field to the file pointer, and
passing a new FUSE_LK_FLOCK flag with the locking request.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-09-25 21:19:14.000000000 +0200
+++ linux/fs/fuse/file.c 2007-09-25 21:19:14.000000000 +0200
@@ -699,7 +699,8 @@ static int convert_fuse_file_lock(const
}
static void fuse_lk_fill(struct fuse_req *req, struct file *file,
- const struct file_lock *fl, int opcode, pid_t pid)
+ const struct file_lock *fl, int opcode, pid_t pid,
+ int flock)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -712,6 +713,8 @@ static void fuse_lk_fill(struct fuse_req
arg->lk.end = fl->fl_end;
arg->lk.type = fl->fl_type;
arg->lk.pid = pid;
+ if (flock)
+ arg->lk_flags |= FUSE_LK_FLOCK;
req->in.h.opcode = opcode;
req->in.h.nodeid = get_node_id(inode);
req->in.numargs = 1;
@@ -731,7 +734,7 @@ static int fuse_getlk(struct file *file,
if (IS_ERR(req))
return PTR_ERR(req);
- fuse_lk_fill(req, file, fl, FUSE_GETLK, 0);
+ fuse_lk_fill(req, file, fl, FUSE_GETLK, 0, 0);
req->out.numargs = 1;
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
@@ -744,7 +747,7 @@ static int fuse_getlk(struct file *file,
return err;
}
-static int fuse_setlk(struct file *file, struct file_lock *fl)
+static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -761,7 +764,7 @@ static int fuse_setlk(struct file *file,
if (IS_ERR(req))
return PTR_ERR(req);
- fuse_lk_fill(req, file, fl, opcode, pid);
+ fuse_lk_fill(req, file, fl, opcode, pid, flock);
request_send(fc, req);
err = req->out.h.error;
/* locking is restartable */
@@ -787,11 +790,28 @@ static int fuse_file_lock(struct file *f
if (fc->no_lock)
err = posix_lock_file_wait(file, fl);
else
- err = fuse_setlk(file, fl);
+ err = fuse_setlk(file, fl, 0);
}
return err;
}
+static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ int err;
+
+ if (fc->no_lock) {
+ err = flock_lock_file_wait(file, fl);
+ } else {
+ /* emulate flock with POSIX locks */
+ fl->fl_owner = (fl_owner_t) file;
+ err = fuse_setlk(file, fl, 1);
+ }
+
+ return err;
+}
+
static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
{
struct inode *inode = mapping->host;
@@ -840,6 +860,7 @@ static const struct file_operations fuse
.release = fuse_release,
.fsync = fuse_fsync,
.lock = fuse_file_lock,
+ .flock = fuse_file_flock,
.splice_read = generic_file_splice_read,
};
@@ -852,6 +873,7 @@ static const struct file_operations fuse
.release = fuse_release,
.fsync = fuse_fsync,
.lock = fuse_file_lock,
+ .flock = fuse_file_flock,
/* no mmap and splice_read */
};
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-25 21:19:14.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-25 21:19:14.000000000 +0200
@@ -13,6 +13,7 @@
*
* 7.9:
* - new fuse_getattr_in input argument of GETATTR
+ * - add lk_flags in fuse_lk_in
*/
#include <asm/types.h>
@@ -113,6 +114,11 @@ struct fuse_file_lock {
*/
#define FUSE_GETATTR_FH (1 << 0)
+/**
+ * Lock flags
+ */
+#define FUSE_LK_FLOCK (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -295,6 +301,8 @@ struct fuse_lk_in {
__u64 fh;
__u64 owner;
struct fuse_file_lock lk;
+ __u32 lk_flags;
+ __u32 padding;
};
struct fuse_lk_out {
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 09/12] fuse: add list of writable files to fuse_inode
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (7 preceding siblings ...)
2007-10-02 15:50 ` [patch 08/12] fuse: support BSD locking semantics Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-04 22:51 ` Andrew Morton
2007-10-02 15:50 ` [patch 10/12] fuse: add helper for asynchronous writes Miklos Szeredi
` (2 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_write_files.patch --]
[-- Type: text/plain, Size: 3016 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Each WRITE request must carry a valid file descriptor. When a page is
written back from a memory mapping, the file through which the page
was dirtied is not available, so a new mechananism is needed to find a
suitable file in ->writepage(s).
A list of fuse_files is added to fuse_inode. The file is removed from
the list in fuse_release().
This patch is in preparation for writable mmap support.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-10-01 22:42:26.000000000 +0200
+++ linux/fs/fuse/file.c 2007-10-01 22:42:27.000000000 +0200
@@ -56,6 +56,7 @@ struct fuse_file *fuse_file_alloc(void)
kfree(ff);
ff = NULL;
}
+ INIT_LIST_HEAD(&ff->write_entry);
atomic_set(&ff->count, 0);
}
return ff;
@@ -150,12 +151,18 @@ int fuse_release_common(struct inode *in
{
struct fuse_file *ff = file->private_data;
if (ff) {
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
fuse_release_fill(ff, get_node_id(inode), file->f_flags,
isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
/* Hold vfsmount and dentry until release is finished */
ff->reserved_req->vfsmount = mntget(file->f_path.mnt);
ff->reserved_req->dentry = dget(file->f_path.dentry);
+
+ spin_lock(&fc->lock);
+ list_del(&ff->write_entry);
+ spin_unlock(&fc->lock);
/*
* Normally this will send the RELEASE request,
* however if some asynchronous READ or WRITE requests
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-10-01 22:42:24.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-10-01 22:43:15.000000000 +0200
@@ -70,6 +70,9 @@ struct fuse_inode {
/** Version of last attribute change */
u64 attr_version;
+
+ /** Files usable in writepage. Protected by fc->lock */
+ struct list_head write_files;
};
/** FUSE specific file data */
@@ -82,6 +85,9 @@ struct fuse_file {
/** Refcount */
atomic_t count;
+
+ /** Entry on inode's write_files list */
+ struct list_head write_entry;
};
/** One input argument of a request */
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-10-01 22:42:24.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-10-01 22:42:27.000000000 +0200
@@ -56,6 +56,7 @@ static struct inode *fuse_alloc_inode(st
fi->i_time = 0;
fi->nodeid = 0;
fi->nlookup = 0;
+ INIT_LIST_HEAD(&fi->write_files);
fi->forget_req = fuse_request_alloc();
if (!fi->forget_req) {
kmem_cache_free(fuse_inode_cachep, inode);
@@ -68,6 +69,7 @@ static struct inode *fuse_alloc_inode(st
static void fuse_destroy_inode(struct inode *inode)
{
struct fuse_inode *fi = get_fuse_inode(inode);
+ BUG_ON(!list_empty(&fi->write_files));
if (fi->forget_req)
fuse_request_free(fi->forget_req);
kmem_cache_free(fuse_inode_cachep, inode);
--
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [patch 09/12] fuse: add list of writable files to fuse_inode
2007-10-02 15:50 ` [patch 09/12] fuse: add list of writable files to fuse_inode Miklos Szeredi
@ 2007-10-04 22:51 ` Andrew Morton
2007-10-04 23:16 ` Miklos Szeredi
0 siblings, 1 reply; 19+ messages in thread
From: Andrew Morton @ 2007-10-04 22:51 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: linux-kernel
On Tue, 02 Oct 2007 17:50:35 +0200
Miklos Szeredi <miklos@szeredi.hu> wrote:
> From: Miklos Szeredi <mszeredi@suse.cz>
>
> Each WRITE request must carry a valid file descriptor. When a page is
> written back from a memory mapping, the file through which the page
> was dirtied is not available, so a new mechananism is needed to find a
> suitable file in ->writepage(s).
>
> A list of fuse_files is added to fuse_inode. The file is removed from
> the list in fuse_release().
>
> This patch is in preparation for writable mmap support.
>
> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
> ---
>
> Index: linux/fs/fuse/file.c
> ===================================================================
> --- linux.orig/fs/fuse/file.c 2007-10-01 22:42:26.000000000 +0200
> +++ linux/fs/fuse/file.c 2007-10-01 22:42:27.000000000 +0200
> @@ -56,6 +56,7 @@ struct fuse_file *fuse_file_alloc(void)
> kfree(ff);
> ff = NULL;
> }
> + INIT_LIST_HEAD(&ff->write_entry);
> atomic_set(&ff->count, 0);
> }
> return ff;
> @@ -150,12 +151,18 @@ int fuse_release_common(struct inode *in
> {
> struct fuse_file *ff = file->private_data;
> if (ff) {
> + struct fuse_conn *fc = get_fuse_conn(inode);
> +
> fuse_release_fill(ff, get_node_id(inode), file->f_flags,
> isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
>
> /* Hold vfsmount and dentry until release is finished */
> ff->reserved_req->vfsmount = mntget(file->f_path.mnt);
> ff->reserved_req->dentry = dget(file->f_path.dentry);
> +
> + spin_lock(&fc->lock);
> + list_del(&ff->write_entry);
> + spin_unlock(&fc->lock);
> /*
> * Normally this will send the RELEASE request,
> * however if some asynchronous READ or WRITE requests
> Index: linux/fs/fuse/fuse_i.h
> ===================================================================
> --- linux.orig/fs/fuse/fuse_i.h 2007-10-01 22:42:24.000000000 +0200
> +++ linux/fs/fuse/fuse_i.h 2007-10-01 22:43:15.000000000 +0200
> @@ -70,6 +70,9 @@ struct fuse_inode {
>
> /** Version of last attribute change */
> u64 attr_version;
> +
> + /** Files usable in writepage. Protected by fc->lock */
> + struct list_head write_files;
> };
>
> /** FUSE specific file data */
> @@ -82,6 +85,9 @@ struct fuse_file {
>
> /** Refcount */
> atomic_t count;
> +
> + /** Entry on inode's write_files list */
> + struct list_head write_entry;
> };
>
> /** One input argument of a request */
> Index: linux/fs/fuse/inode.c
> ===================================================================
> --- linux.orig/fs/fuse/inode.c 2007-10-01 22:42:24.000000000 +0200
> +++ linux/fs/fuse/inode.c 2007-10-01 22:42:27.000000000 +0200
> @@ -56,6 +56,7 @@ static struct inode *fuse_alloc_inode(st
> fi->i_time = 0;
> fi->nodeid = 0;
> fi->nlookup = 0;
> + INIT_LIST_HEAD(&fi->write_files);
> fi->forget_req = fuse_request_alloc();
> if (!fi->forget_req) {
> kmem_cache_free(fuse_inode_cachep, inode);
> @@ -68,6 +69,7 @@ static struct inode *fuse_alloc_inode(st
> static void fuse_destroy_inode(struct inode *inode)
> {
> struct fuse_inode *fi = get_fuse_inode(inode);
> + BUG_ON(!list_empty(&fi->write_files));
> if (fi->forget_req)
> fuse_request_free(fi->forget_req);
> kmem_cache_free(fuse_inode_cachep, inode);
hm. At no point in this patch series does anything actually get added to
these lists, so this patch is presently a no-op.
I'll assume that it will get used later. But it is a bit odd to add
infrastructure in a patch series, then not use it. Why not hold the patch
back and include it in the patch series which actually uses these lists for
something?
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [patch 09/12] fuse: add list of writable files to fuse_inode
2007-10-04 22:51 ` Andrew Morton
@ 2007-10-04 23:16 ` Miklos Szeredi
0 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-04 23:16 UTC (permalink / raw)
To: akpm; +Cc: miklos, linux-kernel
> hm. At no point in this patch series does anything actually get added to
> these lists, so this patch is presently a no-op.
>
> I'll assume that it will get used later. But it is a bit odd to add
> infrastructure in a patch series, then not use it. Why not hold the patch
> back and include it in the patch series which actually uses these lists for
> something?
My stupidity. I somehow thought the patch does actually do something
interesting when including it in this series, instead of holding it
back for the writable-mmap series.
Miklos
^ permalink raw reply [flat|nested] 19+ messages in thread
* [patch 10/12] fuse: add helper for asynchronous writes
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (8 preceding siblings ...)
2007-10-02 15:50 ` [patch 09/12] fuse: add list of writable files to fuse_inode Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 11/12] fuse: add support for mandatory locking Miklos Szeredi
2007-10-02 15:50 ` [patch 12/12] fuse: add blksize field to fuse_attr Miklos Szeredi
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_fill_write.patch --]
[-- Type: text/plain, Size: 3402 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
This patch adds a new helper function fuse_write_fill() which makes it
possible to send WRITE requests asynchronously.
A new flag for WRITE requests is also added which indicates that this
a write from the page cache, and not a "normal" file write.
This patch is in preparation for writable mmap support.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-09-25 21:19:14.000000000 +0200
+++ linux/fs/fuse/file.c 2007-09-25 21:19:15.000000000 +0200
@@ -443,30 +443,37 @@ out:
return err;
}
-static size_t fuse_send_write(struct fuse_req *req, struct file *file,
- struct inode *inode, loff_t pos, size_t count)
-{
- struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_file *ff = file->private_data;
- struct fuse_write_in inarg;
- struct fuse_write_out outarg;
-
- memset(&inarg, 0, sizeof(struct fuse_write_in));
- inarg.fh = ff->fh;
- inarg.offset = pos;
- inarg.size = count;
+static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
+ struct inode *inode, loff_t pos, size_t count,
+ int writepage)
+{
+ struct fuse_write_in *inarg = &req->misc.write.in;
+ struct fuse_write_out *outarg = &req->misc.write.out;
+
+ memset(inarg, 0, sizeof(struct fuse_write_in));
+ inarg->fh = ff->fh;
+ inarg->offset = pos;
+ inarg->size = count;
+ inarg->write_flags = writepage ? FUSE_WRITE_CACHE : 0;
req->in.h.opcode = FUSE_WRITE;
req->in.h.nodeid = get_node_id(inode);
req->in.argpages = 1;
req->in.numargs = 2;
req->in.args[0].size = sizeof(struct fuse_write_in);
- req->in.args[0].value = &inarg;
+ req->in.args[0].value = inarg;
req->in.args[1].size = count;
req->out.numargs = 1;
req->out.args[0].size = sizeof(struct fuse_write_out);
- req->out.args[0].value = &outarg;
+ req->out.args[0].value = outarg;
+}
+
+static size_t fuse_send_write(struct fuse_req *req, struct file *file,
+ struct inode *inode, loff_t pos, size_t count)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ fuse_write_fill(req, file->private_data, inode, pos, count, 0);
request_send(fc, req);
- return outarg.size;
+ return req->misc.write.out.size;
}
static int fuse_write_begin(struct file *file, struct address_space *mapping,
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-09-25 21:19:14.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-09-25 21:19:15.000000000 +0200
@@ -219,6 +219,10 @@ struct fuse_req {
struct fuse_init_in init_in;
struct fuse_init_out init_out;
struct fuse_read_in read_in;
+ struct {
+ struct fuse_write_in in;
+ struct fuse_write_out out;
+ } write;
struct fuse_lk_in lk_in;
} misc;
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-25 21:19:14.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-25 21:19:15.000000000 +0200
@@ -119,6 +119,13 @@ struct fuse_file_lock {
*/
#define FUSE_LK_FLOCK (1 << 0)
+/**
+ * WRITE flags
+ *
+ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
+ */
+#define FUSE_WRITE_CACHE (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 11/12] fuse: add support for mandatory locking
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (9 preceding siblings ...)
2007-10-02 15:50 ` [patch 10/12] fuse: add helper for asynchronous writes Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-02 15:50 ` [patch 12/12] fuse: add blksize field to fuse_attr Miklos Szeredi
11 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_mandlock_support.patch --]
[-- Type: text/plain, Size: 6811 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
For mandatory locking the userspace filesystem needs to know the lock
ownership for read, write and truncate operations.
This patch adds the necessary fields to the protocol.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-25 21:19:14.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-25 21:19:15.000000000 +0200
@@ -1108,6 +1108,11 @@ static int fuse_do_setattr(struct dentry
inarg.valid |= FATTR_FH;
inarg.fh = ff->fh;
}
+ if (attr->ia_valid & ATTR_SIZE) {
+ /* For mandatory locking in truncate */
+ inarg.valid |= FATTR_LOCKOWNER;
+ inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
+ }
req->in.h.opcode = FUSE_SETATTR;
req->in.h.nodeid = get_node_id(inode);
req->in.numargs = 1;
Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2007-09-25 21:19:15.000000000 +0200
+++ linux/fs/fuse/file.c 2007-09-25 21:19:15.000000000 +0200
@@ -189,7 +189,7 @@ static int fuse_release(struct inode *in
* Scramble the ID space with XTEA, so that the value of the files_struct
* pointer is not exposed to userspace.
*/
-static u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
+u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
{
u32 *k = fc->scramble_key;
u64 v = (unsigned long) id;
@@ -308,11 +308,19 @@ void fuse_read_fill(struct fuse_req *req
}
static size_t fuse_send_read(struct fuse_req *req, struct file *file,
- struct inode *inode, loff_t pos, size_t count)
+ struct inode *inode, loff_t pos, size_t count,
+ fl_owner_t owner)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_file *ff = file->private_data;
+
fuse_read_fill(req, ff, inode, pos, count, FUSE_READ);
+ if (owner != NULL) {
+ struct fuse_read_in *inarg = &req->misc.read_in;
+
+ inarg->read_flags |= FUSE_READ_LOCKOWNER;
+ inarg->lock_owner = fuse_lock_owner_id(fc, owner);
+ }
request_send(fc, req);
return req->out.args[0].size;
}
@@ -336,7 +344,8 @@ static int fuse_readpage(struct file *fi
req->out.page_zeroing = 1;
req->num_pages = 1;
req->pages[0] = page;
- fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE);
+ fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE,
+ NULL);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err)
@@ -447,6 +456,7 @@ static void fuse_write_fill(struct fuse_
struct inode *inode, loff_t pos, size_t count,
int writepage)
{
+ struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_write_in *inarg = &req->misc.write.in;
struct fuse_write_out *outarg = &req->misc.write.out;
@@ -459,7 +469,10 @@ static void fuse_write_fill(struct fuse_
req->in.h.nodeid = get_node_id(inode);
req->in.argpages = 1;
req->in.numargs = 2;
- req->in.args[0].size = sizeof(struct fuse_write_in);
+ if (fc->minor < 9)
+ req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE;
+ else
+ req->in.args[0].size = sizeof(struct fuse_write_in);
req->in.args[0].value = inarg;
req->in.args[1].size = count;
req->out.numargs = 1;
@@ -468,10 +481,16 @@ static void fuse_write_fill(struct fuse_
}
static size_t fuse_send_write(struct fuse_req *req, struct file *file,
- struct inode *inode, loff_t pos, size_t count)
+ struct inode *inode, loff_t pos, size_t count,
+ fl_owner_t owner)
{
struct fuse_conn *fc = get_fuse_conn(inode);
fuse_write_fill(req, file->private_data, inode, pos, count, 0);
+ if (owner != NULL) {
+ struct fuse_write_in *inarg = &req->misc.write.in;
+ inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
+ inarg->lock_owner = fuse_lock_owner_id(fc, owner);
+ }
request_send(fc, req);
return req->misc.write.out.size;
}
@@ -508,7 +527,7 @@ static int fuse_buffered_write(struct fi
req->num_pages = 1;
req->pages[0] = page;
req->page_offset = offset;
- nres = fuse_send_write(req, file, inode, pos, count);
+ nres = fuse_send_write(req, file, inode, pos, count, NULL);
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err && !nres)
@@ -609,9 +628,11 @@ static ssize_t fuse_direct_io(struct fil
nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset;
nbytes = min(count, nbytes);
if (write)
- nres = fuse_send_write(req, file, inode, pos, nbytes);
+ nres = fuse_send_write(req, file, inode, pos, nbytes,
+ current->files);
else
- nres = fuse_send_read(req, file, inode, pos, nbytes);
+ nres = fuse_send_read(req, file, inode, pos, nbytes,
+ current->files);
fuse_release_user_pages(req, !write);
if (req->out.h.error) {
if (!res)
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2007-09-25 21:19:15.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2007-09-25 21:19:15.000000000 +0200
@@ -591,3 +591,5 @@ int fuse_valid_type(int m);
* Is task allowed to perform filesystem operation?
*/
int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task);
+
+u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-25 21:19:15.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-25 21:19:15.000000000 +0200
@@ -14,6 +14,7 @@
* 7.9:
* - new fuse_getattr_in input argument of GETATTR
* - add lk_flags in fuse_lk_in
+ * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
*/
#include <asm/types.h>
@@ -86,6 +87,7 @@ struct fuse_file_lock {
#define FATTR_FH (1 << 6)
#define FATTR_ATIME_NOW (1 << 7)
#define FATTR_MTIME_NOW (1 << 8)
+#define FATTR_LOCKOWNER (1 << 9)
/**
* Flags returned by the OPEN request
@@ -123,8 +125,15 @@ struct fuse_file_lock {
* WRITE flags
*
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
+ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid
*/
#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+
+/**
+ * Read flags
+ */
+#define FUSE_READ_LOCKOWNER (1 << 1)
enum fuse_opcode {
FUSE_LOOKUP = 1,
@@ -219,7 +228,7 @@ struct fuse_setattr_in {
__u32 padding;
__u64 fh;
__u64 size;
- __u64 unused1;
+ __u64 lock_owner;
__u64 atime;
__u64 mtime;
__u64 unused2;
@@ -262,14 +271,18 @@ struct fuse_read_in {
__u64 fh;
__u64 offset;
__u32 size;
- __u32 padding;
+ __u32 read_flags;
+ __u64 lock_owner;
};
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+
struct fuse_write_in {
__u64 fh;
__u64 offset;
__u32 size;
__u32 write_flags;
+ __u64 lock_owner;
};
struct fuse_write_out {
--
^ permalink raw reply [flat|nested] 19+ messages in thread* [patch 12/12] fuse: add blksize field to fuse_attr
2007-10-02 15:50 [patch 00/12] fuse update Miklos Szeredi
` (10 preceding siblings ...)
2007-10-02 15:50 ` [patch 11/12] fuse: add support for mandatory locking Miklos Szeredi
@ 2007-10-02 15:50 ` Miklos Szeredi
2007-10-04 22:55 ` Andrew Morton
11 siblings, 1 reply; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-02 15:50 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
[-- Attachment #1: fuse_add_blksize_to_attributes.patch --]
[-- Type: text/plain, Size: 5517 bytes --]
From: Miklos Szeredi <mszeredi@suse.cz>
Allow the userspace filesystem to supply a blksize value to be
returned by stat() and friends. If the field is zero, it defaults to
the old PAGE_CACHE_SIZE value.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
Index: linux/include/linux/fuse.h
===================================================================
--- linux.orig/include/linux/fuse.h 2007-09-27 00:42:19.000000000 +0200
+++ linux/include/linux/fuse.h 2007-09-27 01:00:03.000000000 +0200
@@ -15,6 +15,7 @@
* - new fuse_getattr_in input argument of GETATTR
* - add lk_flags in fuse_lk_in
* - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
+ * - add blksize field to fuse_attr
*/
#include <asm/types.h>
@@ -53,6 +54,8 @@ struct fuse_attr {
__u32 uid;
__u32 gid;
__u32 rdev;
+ __u32 blksize;
+ __u32 padding;
};
struct fuse_kstatfs {
@@ -177,6 +180,8 @@ enum fuse_opcode {
/* The read buffer is required to be at least 8k, but may be much larger */
#define FUSE_MIN_READ_BUFFER 8192
+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
+
struct fuse_entry_out {
__u64 nodeid; /* Inode ID */
__u64 generation; /* Inode generation: nodeid:gen must
@@ -198,6 +203,8 @@ struct fuse_getattr_in {
__u64 fh;
};
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+
struct fuse_attr_out {
__u64 attr_valid; /* Cache timeout for the attributes */
__u32 attr_valid_nsec;
Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2007-09-27 00:42:19.000000000 +0200
+++ linux/fs/fuse/dir.c 2007-09-27 01:00:30.000000000 +0200
@@ -116,13 +116,19 @@ static void fuse_lookup_init(struct fuse
struct dentry *entry,
struct fuse_entry_out *outarg)
{
+ struct fuse_conn *fc = get_fuse_conn(dir);
+
+ memset(outarg, 0, sizeof(struct fuse_entry_out));
req->in.h.opcode = FUSE_LOOKUP;
req->in.h.nodeid = get_node_id(dir);
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(struct fuse_entry_out);
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(struct fuse_entry_out);
req->out.args[0].value = outarg;
}
@@ -356,6 +362,7 @@ static int fuse_create_open(struct inode
flags &= ~O_NOCTTY;
memset(&inarg, 0, sizeof(inarg));
+ memset(&outentry, 0, sizeof(outentry));
inarg.flags = flags;
inarg.mode = mode;
req->in.h.opcode = FUSE_CREATE;
@@ -366,7 +373,10 @@ static int fuse_create_open(struct inode
req->in.args[1].size = entry->d_name.len + 1;
req->in.args[1].value = entry->d_name.name;
req->out.numargs = 2;
- req->out.args[0].size = sizeof(outentry);
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(outentry);
req->out.args[0].value = &outentry;
req->out.args[1].size = sizeof(outopen);
req->out.args[1].value = &outopen;
@@ -431,9 +441,13 @@ static int create_new_entry(struct fuse_
return PTR_ERR(forget_req);
}
+ memset(&outarg, 0, sizeof(outarg));
req->in.h.nodeid = get_node_id(dir);
req->out.numargs = 1;
- req->out.args[0].size = sizeof(outarg);
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
request_send(fc, req);
err = req->out.h.error;
@@ -724,6 +738,7 @@ static int fuse_do_getattr(struct inode
spin_unlock(&fc->lock);
memset(&inarg, 0, sizeof(inarg));
+ memset(&outarg, 0, sizeof(outarg));
/* Directories have separate file-handle space */
if (file && S_ISREG(inode->i_mode)) {
struct fuse_file *ff = file->private_data;
@@ -737,7 +752,10 @@ static int fuse_do_getattr(struct inode
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(outarg);
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
request_send(fc, req);
err = req->out.h.error;
@@ -1102,6 +1120,7 @@ static int fuse_do_setattr(struct dentry
return PTR_ERR(req);
memset(&inarg, 0, sizeof(inarg));
+ memset(&outarg, 0, sizeof(outarg));
iattr_to_fattr(attr, &inarg);
if (file) {
struct fuse_file *ff = file->private_data;
@@ -1119,7 +1138,10 @@ static int fuse_do_setattr(struct dentry
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(outarg);
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
request_send(fc, req);
err = req->out.h.error;
Index: linux/fs/fuse/inode.c
===================================================================
--- linux.orig/fs/fuse/inode.c 2007-09-27 00:42:19.000000000 +0200
+++ linux/fs/fuse/inode.c 2007-09-27 00:42:19.000000000 +0200
@@ -148,6 +148,11 @@ void fuse_change_attributes(struct inode
inode->i_ctime.tv_sec = attr->ctime;
inode->i_ctime.tv_nsec = attr->ctimensec;
+ if (attr->blksize != 0)
+ inode->i_blkbits = ilog2(attr->blksize);
+ else
+ inode->i_blkbits = inode->i_sb->s_blocksize_bits;
+
/*
* Don't set the sticky bit in i_mode, unless we want the VFS
* to check permissions. This prevents failures due to the
--
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [patch 12/12] fuse: add blksize field to fuse_attr
2007-10-02 15:50 ` [patch 12/12] fuse: add blksize field to fuse_attr Miklos Szeredi
@ 2007-10-04 22:55 ` Andrew Morton
2007-10-04 23:15 ` Miklos Szeredi
0 siblings, 1 reply; 19+ messages in thread
From: Andrew Morton @ 2007-10-04 22:55 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: linux-kernel
On Tue, 02 Oct 2007 17:50:38 +0200
Miklos Szeredi <miklos@szeredi.hu> wrote:
> From: Miklos Szeredi <mszeredi@suse.cz>
>
> Allow the userspace filesystem to supply a blksize value to be
> returned by stat() and friends. If the field is zero, it defaults to
> the old PAGE_CACHE_SIZE value.
>
Why does fuse need this feature?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [patch 12/12] fuse: add blksize field to fuse_attr
2007-10-04 22:55 ` Andrew Morton
@ 2007-10-04 23:15 ` Miklos Szeredi
0 siblings, 0 replies; 19+ messages in thread
From: Miklos Szeredi @ 2007-10-04 23:15 UTC (permalink / raw)
To: akpm; +Cc: miklos, linux-kernel
> > From: Miklos Szeredi <mszeredi@suse.cz>
> >
> > Allow the userspace filesystem to supply a blksize value to be
> > returned by stat() and friends. If the field is zero, it defaults to
> > the old PAGE_CACHE_SIZE value.
> >
>
> Why does fuse need this feature?
There are cases, when the filesystem will be passed the buffer from a
single read or write call, namely:
1) in 'direct-io' mode (not O_DIRECT), read/write requests don't go
through the page cache, but go directly to the userspace fs
2) currently buffered writes are done with single page requests, but
if Nick's ->perform_write() patch goes it, it will be possible to
do larger write requests. But only if the original write() was
also bigger than a page.
In these cases the filesystem might want to give a hint to the app
about the optimal I/O size.
Miklos
^ permalink raw reply [flat|nested] 19+ messages in thread