|
@@ -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,
|
|
|
|
- struct inode *newdir, struct dentry *newent)
|
|
|
|
|
|
+static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
|
|
|
+ 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_nofail_nopages(fc, file);
|
|
|
|
- } else {
|
|
|
|
- req = fuse_get_req_nopages(fc);
|
|
|
|
- if (IS_ERR(req))
|
|
|
|
- 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)) {
|
|
|
|
- inode->i_mtime = attr->ia_mtime;
|
|
|
|
- clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
|
|
|
|
|
|
+ if (trust_local_cmtime) {
|
|
|
|
+ if (attr->ia_valid & ATTR_MTIME)
|
|
|
|
+ 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-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));
|
|
|
|
|
|
+ fuse_update_ctime(inode);
|
|
}
|
|
}
|
|
- 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 = {
|