fuse: add a flag FUSE_OPEN_KILL_SUIDGID for open() request
[linux-2.6-microblaze.git] / fs / fuse / dir.c
index ff7dbeb..5d43af1 100644 (file)
@@ -205,7 +205,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
        if (inode && is_bad_inode(inode))
                goto invalid;
        else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
-                (flags & LOOKUP_REVAL)) {
+                (flags & (LOOKUP_EXCL | LOOKUP_REVAL))) {
                struct fuse_entry_out outarg;
                FUSE_ARGS(args);
                struct fuse_forget_link *forget;
@@ -328,12 +328,11 @@ static struct vfsmount *fuse_dentry_automount(struct path *path)
        if (!fm)
                goto out_put_fsc;
 
-       refcount_set(&fm->count, 1);
        fsc->s_fs_info = fm;
        sb = sget_fc(fsc, NULL, set_anon_super_fc);
        if (IS_ERR(sb)) {
                err = PTR_ERR(sb);
-               fuse_mount_put(fm);
+               kfree(fm);
                goto out_put_fsc;
        }
        fm->fc = fuse_conn_get(fc);
@@ -542,6 +541,12 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        inarg.flags = flags;
        inarg.mode = mode;
        inarg.umask = current_umask();
+
+       if (fm->fc->handle_killpriv_v2 && (flags & O_TRUNC) &&
+           !(flags & O_EXCL) && !capable(CAP_FSETID)) {
+               inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
+       }
+
        args.opcode = FUSE_CREATE;
        args.nodeid = get_node_id(dir);
        args.in_numargs = 2;
@@ -1649,10 +1654,20 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
                inarg.valid |= FATTR_FH;
                inarg.fh = ff->fh;
        }
+
+       /* Kill suid/sgid for non-directory chown unconditionally */
+       if (fc->handle_killpriv_v2 && !S_ISDIR(inode->i_mode) &&
+           attr->ia_valid & (ATTR_UID | ATTR_GID))
+               inarg.valid |= FATTR_KILL_SUIDGID;
+
        if (attr->ia_valid & ATTR_SIZE) {
                /* For mandatory locking in truncate */
                inarg.valid |= FATTR_LOCKOWNER;
                inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
+
+               /* Kill suid/sgid for truncate only if no CAP_FSETID */
+               if (fc->handle_killpriv_v2 && !capable(CAP_FSETID))
+                       inarg.valid |= FATTR_KILL_SUIDGID;
        }
        fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
        err = fuse_simple_request(fm, &args);
@@ -1740,7 +1755,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
                 *
                 * This should be done on write(), truncate() and chown().
                 */
-               if (!fc->handle_killpriv) {
+               if (!fc->handle_killpriv && !fc->handle_killpriv_v2) {
                        /*
                         * ia_mode calculation may have used stale i_mode.
                         * Refresh and recalculate.