]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/fuse/dir.c
Merge tag 'llvmlinux-for-v3.15' of git://git.linuxfoundation.org/llvmlinux/kernel
[karo-tx-linux.git] / fs / fuse / dir.c
index 1d1292c581c317ebaa16c54de9f9b85a75086f28..5b4e035b364cc604d68e72411337ef061a766fea 100644 (file)
@@ -839,6 +839,14 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
                          struct kstat *stat)
 {
        unsigned int blkbits;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+
+       /* see the comment in fuse_change_attributes() */
+       if (fc->writeback_cache && S_ISREG(inode->i_mode)) {
+               attr->size = i_size_read(inode);
+               attr->mtime = inode->i_mtime.tv_sec;
+               attr->mtimensec = inode->i_mtime.tv_nsec;
+       }
 
        stat->dev = inode->i_sb->s_dev;
        stat->ino = attr->ino;
@@ -1477,12 +1485,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd,
                                 FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR);
 }
 
-static bool update_mtime(unsigned ivalid)
+static bool update_mtime(unsigned ivalid, bool trust_local_mtime)
 {
        /* Always update if mtime is explicitly set  */
        if (ivalid & ATTR_MTIME_SET)
                return true;
 
+       /* Or if kernel i_mtime is the official one */
+       if (trust_local_mtime)
+               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;
@@ -1491,7 +1503,8 @@ static bool update_mtime(unsigned ivalid)
        return true;
 }
 
-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)
 {
        unsigned ivalid = iattr->ia_valid;
 
@@ -1510,11 +1523,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
                if (!(ivalid & ATTR_ATIME_SET))
                        arg->valid |= FATTR_ATIME_NOW;
        }
-       if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+       if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) {
                arg->valid |= FATTR_MTIME;
                arg->mtime = iattr->ia_mtime.tv_sec;
                arg->mtimensec = iattr->ia_mtime.tv_nsec;
-               if (!(ivalid & ATTR_MTIME_SET))
+               if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime)
                        arg->valid |= FATTR_MTIME_NOW;
        }
 }
@@ -1563,6 +1576,63 @@ void fuse_release_nowrite(struct inode *inode)
        spin_unlock(&fc->lock);
 }
 
+static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req,
+                             struct inode *inode,
+                             struct fuse_setattr_in *inarg_p,
+                             struct fuse_attr_out *outarg_p)
+{
+       req->in.h.opcode = FUSE_SETATTR;
+       req->in.h.nodeid = get_node_id(inode);
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(*inarg_p);
+       req->in.args[0].value = inarg_p;
+       req->out.numargs = 1;
+       if (fc->minor < 9)
+               req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
+       else
+               req->out.args[0].size = sizeof(*outarg_p);
+       req->out.args[0].value = outarg_p;
+}
+
+/*
+ * Flush inode->i_mtime to the server
+ */
+int fuse_flush_mtime(struct file *file, bool nofail)
+{
+       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_req *req = NULL;
+       struct fuse_setattr_in inarg;
+       struct fuse_attr_out outarg;
+       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);
+       }
+
+       memset(&inarg, 0, sizeof(inarg));
+       memset(&outarg, 0, sizeof(outarg));
+
+       inarg.valid |= FATTR_MTIME;
+       inarg.mtime = inode->i_mtime.tv_sec;
+       inarg.mtimensec = inode->i_mtime.tv_nsec;
+
+       fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
+       fuse_request_send(fc, req);
+       err = req->out.h.error;
+       fuse_put_request(fc, req);
+
+       if (!err)
+               clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
+
+       return err;
+}
+
 /*
  * Set attributes, and at the same time refresh them.
  *
@@ -1580,8 +1650,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
        struct fuse_setattr_in inarg;
        struct fuse_attr_out outarg;
        bool is_truncate = false;
+       bool is_wb = fc->writeback_cache;
        loff_t oldsize;
        int err;
+       bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode);
 
        if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
                attr->ia_valid |= ATTR_FORCE;
@@ -1610,7 +1682,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
 
        memset(&inarg, 0, sizeof(inarg));
        memset(&outarg, 0, sizeof(outarg));
-       iattr_to_fattr(attr, &inarg);
+       iattr_to_fattr(attr, &inarg, trust_local_mtime);
        if (file) {
                struct fuse_file *ff = file->private_data;
                inarg.valid |= FATTR_FH;
@@ -1621,17 +1693,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
                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;
-       req->in.args[0].size = sizeof(inarg);
-       req->in.args[0].value = &inarg;
-       req->out.numargs = 1;
-       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;
+       fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
        fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
@@ -1648,10 +1710,18 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
        }
 
        spin_lock(&fc->lock);
+       /* 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);
+       }
+
        fuse_change_attributes_common(inode, &outarg.attr,
                                      attr_timeout(&outarg));
        oldsize = inode->i_size;
-       i_size_write(inode, outarg.attr.size);
+       /* see the comment in fuse_change_attributes() */
+       if (!is_wb || is_truncate || !S_ISREG(inode->i_mode))
+               i_size_write(inode, outarg.attr.size);
 
        if (is_truncate) {
                /* NOTE: this may release/reacquire fc->lock */
@@ -1663,7 +1733,8 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
         * Only call invalidate_inode_pages2() after removing
         * FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock.
         */
-       if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
+       if ((is_truncate || !is_wb) &&
+           S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
                truncate_pagecache(inode, outarg.attr.size);
                invalidate_inode_pages2(inode->i_mapping);
        }
@@ -1875,6 +1946,17 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
        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));
+       }
+       return 0;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
        .lookup         = fuse_lookup,
        .mkdir          = fuse_mkdir,
@@ -1914,6 +1996,7 @@ static const struct inode_operations fuse_common_inode_operations = {
        .getxattr       = fuse_getxattr,
        .listxattr      = fuse_listxattr,
        .removexattr    = fuse_removexattr,
+       .update_time    = fuse_update_time,
 };
 
 static const struct inode_operations fuse_symlink_inode_operations = {