fi->i_time = 0;
fi->nodeid = 0;
fi->nlookup = 0;
+ fi->attr_version = 0;
+ INIT_LIST_HEAD(&fi->write_files);
fi->forget_req = fuse_request_alloc();
if (!fi->forget_req) {
kmem_cache_free(fuse_inode_cachep, inode);
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);
return 0;
}
-void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+static void fuse_truncate(struct address_space *mapping, loff_t offset)
+{
+ /* See vmtruncate() */
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+ truncate_inode_pages(mapping, offset);
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+}
+
+
+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);
- if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
- invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ 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_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
inode->i_nlink = attr->nlink;
inode->i_uid = attr->uid;
inode->i_gid = attr->gid;
- spin_lock(&fc->lock);
- i_size_write(inode, attr->size);
- spin_unlock(&fc->lock);
inode->i_blocks = attr->blocks;
inode->i_atime.tv_sec = attr->atime;
inode->i_atime.tv_nsec = attr->atimensec;
inode->i_mtime.tv_nsec = attr->mtimensec;
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
+ * check in may_delete().
+ */
+ fi->orig_i_mode = inode->i_mode;
+ if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
+ inode->i_mode &= ~S_ISVTX;
+
+ oldsize = inode->i_size;
+ i_size_write(inode, attr->size);
+ spin_unlock(&fc->lock);
+
+ if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
+ if (attr->size < oldsize)
+ fuse_truncate(inode->i_mapping, attr->size);
+ invalidate_inode_pages2(inode->i_mapping);
+ }
}
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
}
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;
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;
}
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq);
wake_up_all(&fc->blocked_waitq);
+ wake_up_all(&fc->reserved_req_waitq);
mutex_lock(&fuse_mutex);
list_del(&fc->entry);
fuse_ctl_remove_conn(fc);
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);
static struct fuse_conn *new_conn(void)
{
struct fuse_conn *fc;
+ int err;
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (fc) {
atomic_set(&fc->count, 1);
init_waitqueue_head(&fc->waitq);
init_waitqueue_head(&fc->blocked_waitq);
+ init_waitqueue_head(&fc->reserved_req_waitq);
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io);
atomic_set(&fc->num_waiting, 0);
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn;
+ err = bdi_init(&fc->bdi);
+ if (err) {
+ kfree(fc);
+ fc = NULL;
+ goto out;
+ }
fc->reqctr = 0;
fc->blocked = 1;
+ fc->attr_version = 1;
get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
}
+out:
return fc;
}
if (fc->destroy_req)
fuse_request_free(fc->destroy_req);
mutex_destroy(&fc->inst_mutex);
+ bdi_destroy(&fc->bdi);
kfree(fc);
}
}
attr.mode = mode;
attr.ino = FUSE_ROOT_ID;
- return fuse_iget(sb, 1, 0, &attr);
+ attr.nlink = 1;
+ return fuse_iget(sb, 1, 0, &attr, 0, 0);
}
static const struct super_operations fuse_super_operations = {
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;
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_ATOMIC_O_TRUNC;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
}
#endif
-static decl_subsys(fuse, NULL, NULL);
-static decl_subsys(connections, NULL, NULL);
-
-static void fuse_inode_init_once(void *foo, struct kmem_cache *cachep,
- unsigned long flags)
+static void fuse_inode_init_once(struct kmem_cache *cachep, void *foo)
{
struct inode * inode = foo;
kmem_cache_destroy(fuse_inode_cachep);
}
+static struct kobject *fuse_kobj;
+static struct kobject *connections_kobj;
+
static int fuse_sysfs_init(void)
{
int err;
- kobj_set_kset_s(&fuse_subsys, fs_subsys);
- err = subsystem_register(&fuse_subsys);
- if (err)
+ fuse_kobj = kobject_create_and_add("fuse", fs_kobj);
+ if (!fuse_kobj) {
+ err = -ENOMEM;
goto out_err;
+ }
- kobj_set_kset_s(&connections_subsys, fuse_subsys);
- err = subsystem_register(&connections_subsys);
- if (err)
+ connections_kobj = kobject_create_and_add("connections", fuse_kobj);
+ if (!connections_kobj) {
+ err = -ENOMEM;
goto out_fuse_unregister;
+ }
return 0;
out_fuse_unregister:
- subsystem_unregister(&fuse_subsys);
+ kobject_put(fuse_kobj);
out_err:
return err;
}
static void fuse_sysfs_cleanup(void)
{
- subsystem_unregister(&connections_subsys);
- subsystem_unregister(&fuse_subsys);
+ kobject_put(connections_kobj);
+ kobject_put(fuse_kobj);
}
static int __init fuse_init(void)