Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse fixes from Miklos Szeredi: "This adds ctime update in the new cached writeback mode and also fixes/simplifies the mtime update handling. Support for rename flags (aka renameat2) is also added to the userspace API" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: add renameat2 support fuse: clear MS_I_VERSION fuse: clear FUSE_I_CTIME_DIRTY flag on setattr fuse: trust kernel i_ctime only fuse: remove .update_time fuse: allow ctime flushing to userspace fuse: fuse: add time_gran to INIT_OUT fuse: add .write_inode fuse: clean up fsync fuse: fuse: fallocate: use file_update_time() fuse: update mtime on open(O_TRUNC) in atomic_o_trunc mode fuse: update mtime on truncate(2) fuse: do not use uninitialized i_mode fuse: fix mtime update error in fsync fuse: check fallocate mode fuse: add __exit to fuse_ctl_cleanup
This commit is contained in:
Коммит
256cf4c438
|
@ -348,7 +348,7 @@ int __init fuse_ctl_init(void)
|
||||||
return register_filesystem(&fuse_ctl_fs_type);
|
return register_filesystem(&fuse_ctl_fs_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fuse_ctl_cleanup(void)
|
void __exit fuse_ctl_cleanup(void)
|
||||||
{
|
{
|
||||||
unregister_filesystem(&fuse_ctl_fs_type);
|
unregister_filesystem(&fuse_ctl_fs_type);
|
||||||
}
|
}
|
||||||
|
|
146
fs/fuse/dir.c
146
fs/fuse/dir.c
|
@ -679,6 +679,14 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
|
||||||
return create_new_entry(fc, req, dir, entry, S_IFLNK);
|
return create_new_entry(fc, req, dir, entry, S_IFLNK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void fuse_update_ctime(struct inode *inode)
|
||||||
|
{
|
||||||
|
if (!IS_NOCMTIME(inode)) {
|
||||||
|
inode->i_ctime = current_fs_time(inode->i_sb);
|
||||||
|
mark_inode_dirty_sync(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -713,6 +721,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
fuse_invalidate_attr(dir);
|
fuse_invalidate_attr(dir);
|
||||||
fuse_invalidate_entry_cache(entry);
|
fuse_invalidate_entry_cache(entry);
|
||||||
|
fuse_update_ctime(inode);
|
||||||
} else if (err == -EINTR)
|
} else if (err == -EINTR)
|
||||||
fuse_invalidate_entry(entry);
|
fuse_invalidate_entry(entry);
|
||||||
return err;
|
return err;
|
||||||
|
@ -743,23 +752,26 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fuse_rename(struct inode *olddir, struct dentry *oldent,
|
static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
||||||
struct inode *newdir, struct dentry *newent)
|
struct inode *newdir, struct dentry *newent,
|
||||||
|
unsigned int flags, int opcode, size_t argsize)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct fuse_rename_in inarg;
|
struct fuse_rename2_in inarg;
|
||||||
struct fuse_conn *fc = get_fuse_conn(olddir);
|
struct fuse_conn *fc = get_fuse_conn(olddir);
|
||||||
struct fuse_req *req = fuse_get_req_nopages(fc);
|
struct fuse_req *req;
|
||||||
|
|
||||||
|
req = fuse_get_req_nopages(fc);
|
||||||
if (IS_ERR(req))
|
if (IS_ERR(req))
|
||||||
return PTR_ERR(req);
|
return PTR_ERR(req);
|
||||||
|
|
||||||
memset(&inarg, 0, sizeof(inarg));
|
memset(&inarg, 0, argsize);
|
||||||
inarg.newdir = get_node_id(newdir);
|
inarg.newdir = get_node_id(newdir);
|
||||||
req->in.h.opcode = FUSE_RENAME;
|
inarg.flags = flags;
|
||||||
|
req->in.h.opcode = opcode;
|
||||||
req->in.h.nodeid = get_node_id(olddir);
|
req->in.h.nodeid = get_node_id(olddir);
|
||||||
req->in.numargs = 3;
|
req->in.numargs = 3;
|
||||||
req->in.args[0].size = sizeof(inarg);
|
req->in.args[0].size = argsize;
|
||||||
req->in.args[0].value = &inarg;
|
req->in.args[0].value = &inarg;
|
||||||
req->in.args[1].size = oldent->d_name.len + 1;
|
req->in.args[1].size = oldent->d_name.len + 1;
|
||||||
req->in.args[1].value = oldent->d_name.name;
|
req->in.args[1].value = oldent->d_name.name;
|
||||||
|
@ -771,15 +783,22 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
|
||||||
if (!err) {
|
if (!err) {
|
||||||
/* ctime changes */
|
/* ctime changes */
|
||||||
fuse_invalidate_attr(oldent->d_inode);
|
fuse_invalidate_attr(oldent->d_inode);
|
||||||
|
fuse_update_ctime(oldent->d_inode);
|
||||||
|
|
||||||
|
if (flags & RENAME_EXCHANGE) {
|
||||||
|
fuse_invalidate_attr(newent->d_inode);
|
||||||
|
fuse_update_ctime(newent->d_inode);
|
||||||
|
}
|
||||||
|
|
||||||
fuse_invalidate_attr(olddir);
|
fuse_invalidate_attr(olddir);
|
||||||
if (olddir != newdir)
|
if (olddir != newdir)
|
||||||
fuse_invalidate_attr(newdir);
|
fuse_invalidate_attr(newdir);
|
||||||
|
|
||||||
/* newent will end up negative */
|
/* newent will end up negative */
|
||||||
if (newent->d_inode) {
|
if (!(flags & RENAME_EXCHANGE) && newent->d_inode) {
|
||||||
fuse_invalidate_attr(newent->d_inode);
|
fuse_invalidate_attr(newent->d_inode);
|
||||||
fuse_invalidate_entry_cache(newent);
|
fuse_invalidate_entry_cache(newent);
|
||||||
|
fuse_update_ctime(newent->d_inode);
|
||||||
}
|
}
|
||||||
} else if (err == -EINTR) {
|
} else if (err == -EINTR) {
|
||||||
/* If request was interrupted, DEITY only knows if the
|
/* If request was interrupted, DEITY only knows if the
|
||||||
|
@ -795,6 +814,36 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fuse_rename(struct inode *olddir, struct dentry *oldent,
|
||||||
|
struct inode *newdir, struct dentry *newent)
|
||||||
|
{
|
||||||
|
return fuse_rename_common(olddir, oldent, newdir, newent, 0,
|
||||||
|
FUSE_RENAME, sizeof(struct fuse_rename_in));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_rename2(struct inode *olddir, struct dentry *oldent,
|
||||||
|
struct inode *newdir, struct dentry *newent,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(olddir);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (fc->no_rename2 || fc->minor < 23)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = fuse_rename_common(olddir, oldent, newdir, newent, flags,
|
||||||
|
FUSE_RENAME2, sizeof(struct fuse_rename2_in));
|
||||||
|
if (err == -ENOSYS) {
|
||||||
|
fc->no_rename2 = 1;
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static int fuse_link(struct dentry *entry, struct inode *newdir,
|
static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||||
struct dentry *newent)
|
struct dentry *newent)
|
||||||
{
|
{
|
||||||
|
@ -829,6 +878,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||||
inc_nlink(inode);
|
inc_nlink(inode);
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
|
fuse_update_ctime(inode);
|
||||||
} else if (err == -EINTR) {
|
} else if (err == -EINTR) {
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
}
|
}
|
||||||
|
@ -846,6 +896,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
|
||||||
attr->size = i_size_read(inode);
|
attr->size = i_size_read(inode);
|
||||||
attr->mtime = inode->i_mtime.tv_sec;
|
attr->mtime = inode->i_mtime.tv_sec;
|
||||||
attr->mtimensec = inode->i_mtime.tv_nsec;
|
attr->mtimensec = inode->i_mtime.tv_nsec;
|
||||||
|
attr->ctime = inode->i_ctime.tv_sec;
|
||||||
|
attr->ctimensec = inode->i_ctime.tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
stat->dev = inode->i_sb->s_dev;
|
stat->dev = inode->i_sb->s_dev;
|
||||||
|
@ -1504,7 +1556,7 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
|
static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
|
||||||
bool trust_local_mtime)
|
bool trust_local_cmtime)
|
||||||
{
|
{
|
||||||
unsigned ivalid = iattr->ia_valid;
|
unsigned ivalid = iattr->ia_valid;
|
||||||
|
|
||||||
|
@ -1523,13 +1575,18 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
|
||||||
if (!(ivalid & ATTR_ATIME_SET))
|
if (!(ivalid & ATTR_ATIME_SET))
|
||||||
arg->valid |= FATTR_ATIME_NOW;
|
arg->valid |= FATTR_ATIME_NOW;
|
||||||
}
|
}
|
||||||
if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) {
|
if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_cmtime)) {
|
||||||
arg->valid |= FATTR_MTIME;
|
arg->valid |= FATTR_MTIME;
|
||||||
arg->mtime = iattr->ia_mtime.tv_sec;
|
arg->mtime = iattr->ia_mtime.tv_sec;
|
||||||
arg->mtimensec = iattr->ia_mtime.tv_nsec;
|
arg->mtimensec = iattr->ia_mtime.tv_nsec;
|
||||||
if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime)
|
if (!(ivalid & ATTR_MTIME_SET) && !trust_local_cmtime)
|
||||||
arg->valid |= FATTR_MTIME_NOW;
|
arg->valid |= FATTR_MTIME_NOW;
|
||||||
}
|
}
|
||||||
|
if ((ivalid & ATTR_CTIME) && trust_local_cmtime) {
|
||||||
|
arg->valid |= FATTR_CTIME;
|
||||||
|
arg->ctime = iattr->ia_ctime.tv_sec;
|
||||||
|
arg->ctimensec = iattr->ia_ctime.tv_nsec;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1597,39 +1654,38 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req,
|
||||||
/*
|
/*
|
||||||
* Flush inode->i_mtime to the server
|
* Flush inode->i_mtime to the server
|
||||||
*/
|
*/
|
||||||
int fuse_flush_mtime(struct file *file, bool nofail)
|
int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
|
||||||
{
|
{
|
||||||
struct inode *inode = file->f_mapping->host;
|
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
struct fuse_req *req = NULL;
|
struct fuse_req *req;
|
||||||
struct fuse_setattr_in inarg;
|
struct fuse_setattr_in inarg;
|
||||||
struct fuse_attr_out outarg;
|
struct fuse_attr_out outarg;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (nofail) {
|
req = fuse_get_req_nopages(fc);
|
||||||
req = fuse_get_req_nofail_nopages(fc, file);
|
if (IS_ERR(req))
|
||||||
} else {
|
return PTR_ERR(req);
|
||||||
req = fuse_get_req_nopages(fc);
|
|
||||||
if (IS_ERR(req))
|
|
||||||
return PTR_ERR(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&inarg, 0, sizeof(inarg));
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
memset(&outarg, 0, sizeof(outarg));
|
memset(&outarg, 0, sizeof(outarg));
|
||||||
|
|
||||||
inarg.valid |= FATTR_MTIME;
|
inarg.valid = FATTR_MTIME;
|
||||||
inarg.mtime = inode->i_mtime.tv_sec;
|
inarg.mtime = inode->i_mtime.tv_sec;
|
||||||
inarg.mtimensec = inode->i_mtime.tv_nsec;
|
inarg.mtimensec = inode->i_mtime.tv_nsec;
|
||||||
|
if (fc->minor >= 23) {
|
||||||
|
inarg.valid |= FATTR_CTIME;
|
||||||
|
inarg.ctime = inode->i_ctime.tv_sec;
|
||||||
|
inarg.ctimensec = inode->i_ctime.tv_nsec;
|
||||||
|
}
|
||||||
|
if (ff) {
|
||||||
|
inarg.valid |= FATTR_FH;
|
||||||
|
inarg.fh = ff->fh;
|
||||||
|
}
|
||||||
fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
|
fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
|
||||||
fuse_request_send(fc, req);
|
fuse_request_send(fc, req);
|
||||||
err = req->out.h.error;
|
err = req->out.h.error;
|
||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
|
|
||||||
if (!err)
|
|
||||||
clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1653,7 +1709,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
|
||||||
bool is_wb = fc->writeback_cache;
|
bool is_wb = fc->writeback_cache;
|
||||||
loff_t oldsize;
|
loff_t oldsize;
|
||||||
int err;
|
int err;
|
||||||
bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode);
|
bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);
|
||||||
|
|
||||||
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
|
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
|
||||||
attr->ia_valid |= ATTR_FORCE;
|
attr->ia_valid |= ATTR_FORCE;
|
||||||
|
@ -1678,11 +1734,13 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
|
||||||
if (is_truncate) {
|
if (is_truncate) {
|
||||||
fuse_set_nowrite(inode);
|
fuse_set_nowrite(inode);
|
||||||
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
||||||
|
if (trust_local_cmtime && attr->ia_size != inode->i_size)
|
||||||
|
attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&inarg, 0, sizeof(inarg));
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
memset(&outarg, 0, sizeof(outarg));
|
memset(&outarg, 0, sizeof(outarg));
|
||||||
iattr_to_fattr(attr, &inarg, trust_local_mtime);
|
iattr_to_fattr(attr, &inarg, trust_local_cmtime);
|
||||||
if (file) {
|
if (file) {
|
||||||
struct fuse_file *ff = file->private_data;
|
struct fuse_file *ff = file->private_data;
|
||||||
inarg.valid |= FATTR_FH;
|
inarg.valid |= FATTR_FH;
|
||||||
|
@ -1711,9 +1769,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
|
||||||
|
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
/* the kernel maintains i_mtime locally */
|
/* the kernel maintains i_mtime locally */
|
||||||
if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) {
|
if (trust_local_cmtime) {
|
||||||
inode->i_mtime = attr->ia_mtime;
|
if (attr->ia_valid & ATTR_MTIME)
|
||||||
clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
|
inode->i_mtime = attr->ia_mtime;
|
||||||
|
if (attr->ia_valid & ATTR_CTIME)
|
||||||
|
inode->i_ctime = attr->ia_ctime;
|
||||||
|
/* FIXME: clear I_DIRTY_SYNC? */
|
||||||
}
|
}
|
||||||
|
|
||||||
fuse_change_attributes_common(inode, &outarg.attr,
|
fuse_change_attributes_common(inode, &outarg.attr,
|
||||||
|
@ -1810,8 +1871,10 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
|
||||||
fc->no_setxattr = 1;
|
fc->no_setxattr = 1;
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
if (!err)
|
if (!err) {
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
|
fuse_update_ctime(inode);
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1941,20 +2004,11 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
|
||||||
fc->no_removexattr = 1;
|
fc->no_removexattr = 1;
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
if (!err)
|
if (!err) {
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
return err;
|
fuse_update_ctime(inode);
|
||||||
}
|
|
||||||
|
|
||||||
static int fuse_update_time(struct inode *inode, struct timespec *now,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
if (flags & S_MTIME) {
|
|
||||||
inode->i_mtime = *now;
|
|
||||||
set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state);
|
|
||||||
BUG_ON(!S_ISREG(inode->i_mode));
|
|
||||||
}
|
}
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct inode_operations fuse_dir_inode_operations = {
|
static const struct inode_operations fuse_dir_inode_operations = {
|
||||||
|
@ -1964,6 +2018,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
|
||||||
.unlink = fuse_unlink,
|
.unlink = fuse_unlink,
|
||||||
.rmdir = fuse_rmdir,
|
.rmdir = fuse_rmdir,
|
||||||
.rename = fuse_rename,
|
.rename = fuse_rename,
|
||||||
|
.rename2 = fuse_rename2,
|
||||||
.link = fuse_link,
|
.link = fuse_link,
|
||||||
.setattr = fuse_setattr,
|
.setattr = fuse_setattr,
|
||||||
.create = fuse_create,
|
.create = fuse_create,
|
||||||
|
@ -1996,7 +2051,6 @@ static const struct inode_operations fuse_common_inode_operations = {
|
||||||
.getxattr = fuse_getxattr,
|
.getxattr = fuse_getxattr,
|
||||||
.listxattr = fuse_listxattr,
|
.listxattr = fuse_listxattr,
|
||||||
.removexattr = fuse_removexattr,
|
.removexattr = fuse_removexattr,
|
||||||
.update_time = fuse_update_time,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct inode_operations fuse_symlink_inode_operations = {
|
static const struct inode_operations fuse_symlink_inode_operations = {
|
||||||
|
|
|
@ -223,6 +223,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
|
||||||
i_size_write(inode, 0);
|
i_size_write(inode, 0);
|
||||||
spin_unlock(&fc->lock);
|
spin_unlock(&fc->lock);
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
|
if (fc->writeback_cache)
|
||||||
|
file_update_time(file);
|
||||||
}
|
}
|
||||||
if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
|
if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
|
||||||
fuse_link_write_file(file);
|
fuse_link_write_file(file);
|
||||||
|
@ -232,18 +234,26 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
int err;
|
int err;
|
||||||
|
bool lock_inode = (file->f_flags & O_TRUNC) &&
|
||||||
|
fc->atomic_o_trunc &&
|
||||||
|
fc->writeback_cache;
|
||||||
|
|
||||||
err = generic_file_open(inode, file);
|
err = generic_file_open(inode, file);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (lock_inode)
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
|
||||||
err = fuse_do_open(fc, get_node_id(inode), file, isdir);
|
err = fuse_do_open(fc, get_node_id(inode), file, isdir);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
fuse_finish_open(inode, file);
|
if (!err)
|
||||||
|
fuse_finish_open(inode, file);
|
||||||
|
|
||||||
return 0;
|
if (lock_inode)
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
|
static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
|
||||||
|
@ -314,10 +324,7 @@ static int fuse_release(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
/* see fuse_vma_close() for !writeback_cache case */
|
/* see fuse_vma_close() for !writeback_cache case */
|
||||||
if (fc->writeback_cache)
|
if (fc->writeback_cache)
|
||||||
filemap_write_and_wait(file->f_mapping);
|
write_inode_now(inode, 1);
|
||||||
|
|
||||||
if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state))
|
|
||||||
fuse_flush_mtime(file, true);
|
|
||||||
|
|
||||||
fuse_release_common(file, FUSE_RELEASE);
|
fuse_release_common(file, FUSE_RELEASE);
|
||||||
|
|
||||||
|
@ -439,7 +446,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
|
||||||
if (fc->no_flush)
|
if (fc->no_flush)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = filemap_write_and_wait(file->f_mapping);
|
err = write_inode_now(inode, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -480,13 +487,6 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
|
||||||
if (is_bad_inode(inode))
|
if (is_bad_inode(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
err = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -494,17 +494,17 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
|
||||||
* wait for all outstanding writes, before sending the FSYNC
|
* wait for all outstanding writes, before sending the FSYNC
|
||||||
* request.
|
* request.
|
||||||
*/
|
*/
|
||||||
err = write_inode_now(inode, 0);
|
err = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
fuse_sync_writes(inode);
|
fuse_sync_writes(inode);
|
||||||
|
err = sync_inode_metadata(inode, 1);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) {
|
if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
|
||||||
int err = fuse_flush_mtime(file, false);
|
goto out;
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
req = fuse_get_req_nopages(fc);
|
req = fuse_get_req_nopages(fc);
|
||||||
if (IS_ERR(req)) {
|
if (IS_ERR(req)) {
|
||||||
|
@ -1659,13 +1659,13 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
fuse_writepage_free(fc, req);
|
fuse_writepage_free(fc, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
|
static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc,
|
||||||
struct fuse_inode *fi)
|
struct fuse_inode *fi)
|
||||||
{
|
{
|
||||||
struct fuse_file *ff = NULL;
|
struct fuse_file *ff = NULL;
|
||||||
|
|
||||||
spin_lock(&fc->lock);
|
spin_lock(&fc->lock);
|
||||||
if (!WARN_ON(list_empty(&fi->write_files))) {
|
if (!list_empty(&fi->write_files)) {
|
||||||
ff = list_entry(fi->write_files.next, struct fuse_file,
|
ff = list_entry(fi->write_files.next, struct fuse_file,
|
||||||
write_entry);
|
write_entry);
|
||||||
fuse_file_get(ff);
|
fuse_file_get(ff);
|
||||||
|
@ -1675,6 +1675,29 @@ static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
|
||||||
return ff;
|
return ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
|
||||||
|
struct fuse_inode *fi)
|
||||||
|
{
|
||||||
|
struct fuse_file *ff = __fuse_write_file_get(fc, fi);
|
||||||
|
WARN_ON(!ff);
|
||||||
|
return ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
struct fuse_file *ff;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ff = __fuse_write_file_get(fc, fi);
|
||||||
|
err = fuse_flush_times(inode, ff);
|
||||||
|
if (ff)
|
||||||
|
fuse_file_put(ff, 0);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int fuse_writepage_locked(struct page *page)
|
static int fuse_writepage_locked(struct page *page)
|
||||||
{
|
{
|
||||||
struct address_space *mapping = page->mapping;
|
struct address_space *mapping = page->mapping;
|
||||||
|
@ -2972,6 +2995,9 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
|
bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
|
||||||
(mode & FALLOC_FL_PUNCH_HOLE);
|
(mode & FALLOC_FL_PUNCH_HOLE);
|
||||||
|
|
||||||
|
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (fc->no_fallocate)
|
if (fc->no_fallocate)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
@ -3017,12 +3043,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
|
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
|
||||||
bool changed = fuse_write_update_size(inode, offset + length);
|
bool changed = fuse_write_update_size(inode, offset + length);
|
||||||
|
|
||||||
if (changed && fc->writeback_cache) {
|
if (changed && fc->writeback_cache)
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
file_update_time(file);
|
||||||
|
|
||||||
inode->i_mtime = current_fs_time(inode->i_sb);
|
|
||||||
set_bit(FUSE_I_MTIME_DIRTY, &fi->state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||||
|
|
|
@ -119,8 +119,6 @@ enum {
|
||||||
FUSE_I_INIT_RDPLUS,
|
FUSE_I_INIT_RDPLUS,
|
||||||
/** An operation changing file size is in progress */
|
/** An operation changing file size is in progress */
|
||||||
FUSE_I_SIZE_UNSTABLE,
|
FUSE_I_SIZE_UNSTABLE,
|
||||||
/** i_mtime has been updated locally; a flush to userspace needed */
|
|
||||||
FUSE_I_MTIME_DIRTY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_conn;
|
struct fuse_conn;
|
||||||
|
@ -544,6 +542,9 @@ struct fuse_conn {
|
||||||
/** Is fallocate not implemented by fs? */
|
/** Is fallocate not implemented by fs? */
|
||||||
unsigned no_fallocate:1;
|
unsigned no_fallocate:1;
|
||||||
|
|
||||||
|
/** Is rename with flags implemented by fs? */
|
||||||
|
unsigned no_rename2:1;
|
||||||
|
|
||||||
/** Use enhanced/automatic page cache invalidation. */
|
/** Use enhanced/automatic page cache invalidation. */
|
||||||
unsigned auto_inval_data:1;
|
unsigned auto_inval_data:1;
|
||||||
|
|
||||||
|
@ -725,7 +726,7 @@ int fuse_dev_init(void);
|
||||||
void fuse_dev_cleanup(void);
|
void fuse_dev_cleanup(void);
|
||||||
|
|
||||||
int fuse_ctl_init(void);
|
int fuse_ctl_init(void);
|
||||||
void fuse_ctl_cleanup(void);
|
void __exit fuse_ctl_cleanup(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a request
|
* Allocate a request
|
||||||
|
@ -891,7 +892,8 @@ int fuse_dev_release(struct inode *inode, struct file *file);
|
||||||
|
|
||||||
bool fuse_write_update_size(struct inode *inode, loff_t pos);
|
bool fuse_write_update_size(struct inode *inode, loff_t pos);
|
||||||
|
|
||||||
int fuse_flush_mtime(struct file *file, bool nofail);
|
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
|
||||||
|
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
|
||||||
|
|
||||||
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
|
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
|
||||||
struct file *file);
|
struct file *file);
|
||||||
|
|
|
@ -175,9 +175,9 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||||
if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) {
|
if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) {
|
||||||
inode->i_mtime.tv_sec = attr->mtime;
|
inode->i_mtime.tv_sec = attr->mtime;
|
||||||
inode->i_mtime.tv_nsec = attr->mtimensec;
|
inode->i_mtime.tv_nsec = attr->mtimensec;
|
||||||
|
inode->i_ctime.tv_sec = attr->ctime;
|
||||||
|
inode->i_ctime.tv_nsec = attr->ctimensec;
|
||||||
}
|
}
|
||||||
inode->i_ctime.tv_sec = attr->ctime;
|
|
||||||
inode->i_ctime.tv_nsec = attr->ctimensec;
|
|
||||||
|
|
||||||
if (attr->blksize != 0)
|
if (attr->blksize != 0)
|
||||||
inode->i_blkbits = ilog2(attr->blksize);
|
inode->i_blkbits = ilog2(attr->blksize);
|
||||||
|
@ -256,6 +256,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
|
||||||
inode->i_size = attr->size;
|
inode->i_size = attr->size;
|
||||||
inode->i_mtime.tv_sec = attr->mtime;
|
inode->i_mtime.tv_sec = attr->mtime;
|
||||||
inode->i_mtime.tv_nsec = attr->mtimensec;
|
inode->i_mtime.tv_nsec = attr->mtimensec;
|
||||||
|
inode->i_ctime.tv_sec = attr->ctime;
|
||||||
|
inode->i_ctime.tv_nsec = attr->ctimensec;
|
||||||
if (S_ISREG(inode->i_mode)) {
|
if (S_ISREG(inode->i_mode)) {
|
||||||
fuse_init_common(inode);
|
fuse_init_common(inode);
|
||||||
fuse_init_file_inode(inode);
|
fuse_init_file_inode(inode);
|
||||||
|
@ -303,7 +305,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
|
||||||
|
|
||||||
if ((inode->i_state & I_NEW)) {
|
if ((inode->i_state & I_NEW)) {
|
||||||
inode->i_flags |= S_NOATIME;
|
inode->i_flags |= S_NOATIME;
|
||||||
if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
|
if (!fc->writeback_cache || !S_ISREG(attr->mode))
|
||||||
inode->i_flags |= S_NOCMTIME;
|
inode->i_flags |= S_NOCMTIME;
|
||||||
inode->i_generation = generation;
|
inode->i_generation = generation;
|
||||||
inode->i_data.backing_dev_info = &fc->bdi;
|
inode->i_data.backing_dev_info = &fc->bdi;
|
||||||
|
@ -788,6 +790,7 @@ static const struct super_operations fuse_super_operations = {
|
||||||
.alloc_inode = fuse_alloc_inode,
|
.alloc_inode = fuse_alloc_inode,
|
||||||
.destroy_inode = fuse_destroy_inode,
|
.destroy_inode = fuse_destroy_inode,
|
||||||
.evict_inode = fuse_evict_inode,
|
.evict_inode = fuse_evict_inode,
|
||||||
|
.write_inode = fuse_write_inode,
|
||||||
.drop_inode = generic_delete_inode,
|
.drop_inode = generic_delete_inode,
|
||||||
.remount_fs = fuse_remount_fs,
|
.remount_fs = fuse_remount_fs,
|
||||||
.put_super = fuse_put_super,
|
.put_super = fuse_put_super,
|
||||||
|
@ -890,6 +893,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
fc->async_dio = 1;
|
fc->async_dio = 1;
|
||||||
if (arg->flags & FUSE_WRITEBACK_CACHE)
|
if (arg->flags & FUSE_WRITEBACK_CACHE)
|
||||||
fc->writeback_cache = 1;
|
fc->writeback_cache = 1;
|
||||||
|
if (arg->time_gran && arg->time_gran <= 1000000000)
|
||||||
|
fc->sb->s_time_gran = arg->time_gran;
|
||||||
|
else
|
||||||
|
fc->sb->s_time_gran = 1000000000;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
|
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
|
||||||
fc->no_lock = 1;
|
fc->no_lock = 1;
|
||||||
|
@ -996,7 +1004,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
if (sb->s_flags & MS_MANDLOCK)
|
if (sb->s_flags & MS_MANDLOCK)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
sb->s_flags &= ~MS_NOSEC;
|
sb->s_flags &= ~(MS_NOSEC | MS_I_VERSION);
|
||||||
|
|
||||||
if (!parse_fuse_opt((char *) data, &d, is_bdev))
|
if (!parse_fuse_opt((char *) data, &d, is_bdev))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
@ -96,6 +96,11 @@
|
||||||
*
|
*
|
||||||
* 7.23
|
* 7.23
|
||||||
* - add FUSE_WRITEBACK_CACHE
|
* - add FUSE_WRITEBACK_CACHE
|
||||||
|
* - add time_gran to fuse_init_out
|
||||||
|
* - add reserved space to fuse_init_out
|
||||||
|
* - add FATTR_CTIME
|
||||||
|
* - add ctime and ctimensec to fuse_setattr_in
|
||||||
|
* - add FUSE_RENAME2 request
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -191,6 +196,7 @@ struct fuse_file_lock {
|
||||||
#define FATTR_ATIME_NOW (1 << 7)
|
#define FATTR_ATIME_NOW (1 << 7)
|
||||||
#define FATTR_MTIME_NOW (1 << 8)
|
#define FATTR_MTIME_NOW (1 << 8)
|
||||||
#define FATTR_LOCKOWNER (1 << 9)
|
#define FATTR_LOCKOWNER (1 << 9)
|
||||||
|
#define FATTR_CTIME (1 << 10)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags returned by the OPEN request
|
* Flags returned by the OPEN request
|
||||||
|
@ -348,6 +354,7 @@ enum fuse_opcode {
|
||||||
FUSE_BATCH_FORGET = 42,
|
FUSE_BATCH_FORGET = 42,
|
||||||
FUSE_FALLOCATE = 43,
|
FUSE_FALLOCATE = 43,
|
||||||
FUSE_READDIRPLUS = 44,
|
FUSE_READDIRPLUS = 44,
|
||||||
|
FUSE_RENAME2 = 45,
|
||||||
|
|
||||||
/* CUSE specific operations */
|
/* CUSE specific operations */
|
||||||
CUSE_INIT = 4096,
|
CUSE_INIT = 4096,
|
||||||
|
@ -426,6 +433,12 @@ struct fuse_rename_in {
|
||||||
uint64_t newdir;
|
uint64_t newdir;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fuse_rename2_in {
|
||||||
|
uint64_t newdir;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
struct fuse_link_in {
|
struct fuse_link_in {
|
||||||
uint64_t oldnodeid;
|
uint64_t oldnodeid;
|
||||||
};
|
};
|
||||||
|
@ -438,10 +451,10 @@ struct fuse_setattr_in {
|
||||||
uint64_t lock_owner;
|
uint64_t lock_owner;
|
||||||
uint64_t atime;
|
uint64_t atime;
|
||||||
uint64_t mtime;
|
uint64_t mtime;
|
||||||
uint64_t unused2;
|
uint64_t ctime;
|
||||||
uint32_t atimensec;
|
uint32_t atimensec;
|
||||||
uint32_t mtimensec;
|
uint32_t mtimensec;
|
||||||
uint32_t unused3;
|
uint32_t ctimensec;
|
||||||
uint32_t mode;
|
uint32_t mode;
|
||||||
uint32_t unused4;
|
uint32_t unused4;
|
||||||
uint32_t uid;
|
uint32_t uid;
|
||||||
|
@ -559,6 +572,9 @@ struct fuse_init_in {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FUSE_COMPAT_INIT_OUT_SIZE 8
|
||||||
|
#define FUSE_COMPAT_22_INIT_OUT_SIZE 24
|
||||||
|
|
||||||
struct fuse_init_out {
|
struct fuse_init_out {
|
||||||
uint32_t major;
|
uint32_t major;
|
||||||
uint32_t minor;
|
uint32_t minor;
|
||||||
|
@ -567,6 +583,8 @@ struct fuse_init_out {
|
||||||
uint16_t max_background;
|
uint16_t max_background;
|
||||||
uint16_t congestion_threshold;
|
uint16_t congestion_threshold;
|
||||||
uint32_t max_write;
|
uint32_t max_write;
|
||||||
|
uint32_t time_gran;
|
||||||
|
uint32_t unused[9];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CUSE_INIT_INFO_MAX 4096
|
#define CUSE_INIT_INFO_MAX 4096
|
||||||
|
|
Загрузка…
Ссылка в новой задаче