diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 9ee2a6bbfa3..8ea4ea13ec5 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1054,8 +1054,10 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, if (fi->i_time < get_jiffies_64()) err = fuse_do_getattr(inode); - if (!err) + if (!err) { generic_fillattr(inode, stat); + stat->mode = fi->orig_i_mode; + } return err; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e0555d68b4a..1764506fdd1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -63,6 +63,10 @@ struct fuse_inode { /** Time in jiffies until the file attributes are valid */ u64 i_time; + + /** The sticky bit in inode->i_mode may have been removed, so + preserve the original mode */ + mode_t orig_i_mode; }; /** FUSE specific file data */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 951e760d5c5..fd0735715c1 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -120,10 +120,11 @@ static void fuse_truncate(struct address_space *mapping, loff_t offset) void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) { struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); loff_t oldsize; inode->i_ino = attr->ino; - inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); + inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); inode->i_nlink = attr->nlink; inode->i_uid = attr->uid; inode->i_gid = attr->gid; @@ -135,6 +136,15 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) inode->i_ctime.tv_sec = attr->ctime; inode->i_ctime.tv_nsec = attr->ctimensec; + /* + * Don't set the sticky bit in i_mode, unless we want the VFS + * to check permissions. This prevents failures due to the + * check in may_delete(). + */ + fi->orig_i_mode = inode->i_mode; + if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) + inode->i_mode &= ~S_ISVTX; + spin_lock(&fc->lock); oldsize = inode->i_size; i_size_write(inode, attr->size);