Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / fs / fuse / dir.c
index 0572bca..4b5f2c4 100644 (file)
@@ -1365,7 +1365,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
        return err;
 }
 
-static char *read_link(struct dentry *dentry)
+static const char *fuse_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1389,28 +1389,12 @@ static char *read_link(struct dentry *dentry)
                link = ERR_PTR(ret);
        } else {
                link[ret] = '\0';
+               *cookie = link;
        }
        fuse_invalidate_atime(inode);
        return link;
 }
 
-static void free_link(char *link)
-{
-       if (!IS_ERR(link))
-               free_page((unsigned long) link);
-}
-
-static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, read_link(dentry));
-       return NULL;
-}
-
-static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
-{
-       free_link(nd_get_link(nd));
-}
-
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
        return fuse_open_common(inode, file, true);
@@ -1713,14 +1697,46 @@ error:
 static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 {
        struct inode *inode = d_inode(entry);
+       struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL;
+       int ret;
 
        if (!fuse_allow_current_process(get_fuse_conn(inode)))
                return -EACCES;
 
-       if (attr->ia_valid & ATTR_FILE)
-               return fuse_do_setattr(inode, attr, attr->ia_file);
-       else
-               return fuse_do_setattr(inode, attr, NULL);
+       if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) {
+               int kill;
+
+               attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID |
+                                   ATTR_MODE);
+               /*
+                * ia_mode calculation may have used stale i_mode.  Refresh and
+                * recalculate.
+                */
+               ret = fuse_do_getattr(inode, NULL, file);
+               if (ret)
+                       return ret;
+
+               attr->ia_mode = inode->i_mode;
+               kill = should_remove_suid(entry);
+               if (kill & ATTR_KILL_SUID) {
+                       attr->ia_valid |= ATTR_MODE;
+                       attr->ia_mode &= ~S_ISUID;
+               }
+               if (kill & ATTR_KILL_SGID) {
+                       attr->ia_valid |= ATTR_MODE;
+                       attr->ia_mode &= ~S_ISGID;
+               }
+       }
+       if (!attr->ia_valid)
+               return 0;
+
+       ret = fuse_do_setattr(inode, attr, file);
+       if (!ret) {
+               /* Directory mode changed, may need to revalidate access */
+               if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE))
+                       fuse_invalidate_entry_cache(entry);
+       }
+       return ret;
 }
 
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
@@ -1813,6 +1829,23 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
        return ret;
 }
 
+static int fuse_verify_xattr_list(char *list, size_t size)
+{
+       size_t origsize = size;
+
+       while (size) {
+               size_t thislen = strnlen(list, size);
+
+               if (!thislen || thislen == size)
+                       return -EIO;
+
+               size -= thislen + 1;
+               list += thislen + 1;
+       }
+
+       return origsize;
+}
+
 static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
 {
        struct inode *inode = d_inode(entry);
@@ -1848,6 +1881,8 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
        ret = fuse_simple_request(fc, &args);
        if (!ret && !size)
                ret = outarg.size;
+       if (ret > 0 && size)
+               ret = fuse_verify_xattr_list(list, ret);
        if (ret == -ENOSYS) {
                fc->no_listxattr = 1;
                ret = -EOPNOTSUPP;
@@ -1926,7 +1961,7 @@ static const struct inode_operations fuse_common_inode_operations = {
 static const struct inode_operations fuse_symlink_inode_operations = {
        .setattr        = fuse_setattr,
        .follow_link    = fuse_follow_link,
-       .put_link       = fuse_put_link,
+       .put_link       = free_page_put_link,
        .readlink       = generic_readlink,
        .getattr        = fuse_getattr,
        .setxattr       = fuse_setxattr,