Merge remote-tracking branch 'lsk/v3.10/topic/arm64-misc' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / fs / fuse / dir.c
index f3f783dc4f7509096235c7f8f6ed468fe51ddded..e67b13de2ebc463b9be8c3bea57304e3af561454 100644 (file)
@@ -1175,6 +1175,8 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
                        return -EIO;
                if (reclen > nbytes)
                        break;
+               if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+                       return -EIO;
 
                over = filldir(dstbuf, dirent->name, dirent->namelen,
                               file->f_pos, dirent->ino, dirent->type);
@@ -1225,13 +1227,29 @@ static int fuse_direntplus_link(struct file *file,
                if (name.name[1] == '.' && name.len == 2)
                        return 0;
        }
+
+       if (invalid_nodeid(o->nodeid))
+               return -EIO;
+       if (!fuse_valid_type(o->attr.mode))
+               return -EIO;
+
        fc = get_fuse_conn(dir);
 
        name.hash = full_name_hash(name.name, name.len);
        dentry = d_lookup(parent, &name);
-       if (dentry && dentry->d_inode) {
+       if (dentry) {
                inode = dentry->d_inode;
-               if (get_node_id(inode) == o->nodeid) {
+               if (!inode) {
+                       d_drop(dentry);
+               } else if (get_node_id(inode) != o->nodeid ||
+                          ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
+                       err = d_invalidate(dentry);
+                       if (err)
+                               goto out;
+               } else if (is_bad_inode(inode)) {
+                       err = -EIO;
+                       goto out;
+               } else {
                        struct fuse_inode *fi;
                        fi = get_fuse_inode(inode);
                        spin_lock(&fc->lock);
@@ -1244,9 +1262,6 @@ static int fuse_direntplus_link(struct file *file,
                         */
                        goto found;
                }
-               err = d_invalidate(dentry);
-               if (err)
-                       goto out;
                dput(dentry);
                dentry = NULL;
        }
@@ -1261,10 +1276,19 @@ static int fuse_direntplus_link(struct file *file,
        if (!inode)
                goto out;
 
-       alias = d_materialise_unique(dentry, inode);
-       err = PTR_ERR(alias);
-       if (IS_ERR(alias))
-               goto out;
+       if (S_ISDIR(inode->i_mode)) {
+               mutex_lock(&fc->inst_mutex);
+               alias = fuse_d_add_directory(dentry, inode);
+               mutex_unlock(&fc->inst_mutex);
+               err = PTR_ERR(alias);
+               if (IS_ERR(alias)) {
+                       iput(inode);
+                       goto out;
+               }
+       } else {
+               alias = d_splice_alias(inode, dentry);
+       }
+
        if (alias) {
                dput(dentry);
                dentry = alias;
@@ -1301,6 +1325,8 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
                        return -EIO;
                if (reclen > nbytes)
                        break;
+               if (memchr(dirent->name, '/', dirent->namelen) != NULL)
+                       return -EIO;
 
                if (!over) {
                        /* We fill entries into dstbuf only as much as
@@ -1572,6 +1598,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
                    struct file *file)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_req *req;
        struct fuse_setattr_in inarg;
        struct fuse_attr_out outarg;
@@ -1599,8 +1626,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       if (is_truncate)
+       if (is_truncate) {
                fuse_set_nowrite(inode);
+               set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+       }
 
        memset(&inarg, 0, sizeof(inarg));
        memset(&outarg, 0, sizeof(outarg));
@@ -1662,12 +1691,14 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
                invalidate_inode_pages2(inode->i_mapping);
        }
 
+       clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
        return 0;
 
 error:
        if (is_truncate)
                fuse_release_nowrite(inode);
 
+       clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
        return err;
 }
 
@@ -1731,6 +1762,8 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
                fc->no_setxattr = 1;
                err = -EOPNOTSUPP;
        }
+       if (!err)
+               fuse_invalidate_attr(inode);
        return err;
 }
 
@@ -1860,6 +1893,8 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
                fc->no_removexattr = 1;
                err = -EOPNOTSUPP;
        }
+       if (!err)
+               fuse_invalidate_attr(inode);
        return err;
 }