From patchwork Tue Jul 13 11:13:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373695 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 034F2C07E95 for ; Tue, 13 Jul 2021 11:15:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DEDA26127C for ; Tue, 13 Jul 2021 11:15:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235809AbhGMLSP (ORCPT ); Tue, 13 Jul 2021 07:18:15 -0400 Received: from mail.kernel.org ([198.145.29.99]:46748 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235390AbhGMLSP (ORCPT ); Tue, 13 Jul 2021 07:18:15 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 388B361178; Tue, 13 Jul 2021 11:15:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174925; bh=pM43hmxb1r8NzZe8Bu0JOwv38UcMe7SpmO84f9h22oQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rN39fDcQbuZTlSZX5QmPeRdOgcpSgedEP02wNb9YQT/dNvpHEBnAXNY8HVgIkUqU2 AVGqX6yQ7xVrEbj1mAIaBQE7ji/YfKhWD+4p/pK0zvtvhjL6wwlvbvz5FB+LRDoZRF QpkvX1uva5EQTS38RhbMzZJoGLVwPsQD3ZPI0aFbLLXeqSWYr611nw3gAx08hx47zV pG6EtIO7QVKK4XlK3qh8h/HsnmEHLNE+ltZqWkUviKGmfdyhGP3bw0lFURcPpNcB+z 17qTZSltmrbcrNTaTFABdrs8/CkNwqNs9NToQril6J6epyvcMSK1fyNXAmym09qJbH XYR0bSvn/d1MQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 01/24] namei: handle mappings in lookup_one_len() Date: Tue, 13 Jul 2021 13:13:21 +0200 Message-Id: <20210713111344.1149376-2-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=34833; h=from:subject; bh=HvpZqWnoX/cAxQzihtJiuF7rgFvIGkvNUFwJGjvsgCE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LQ1vCZW0vT1T8EFtRJCv4Ic6g++HUlc+SLGyf9r6Kst8 esWGjlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgInsNGdkeK1fzbbUn/v/S+u10vfnXD pzRGFi5YJYk9Vn5BMtd3j1sjMyTI/ecCuqdmeH513FA0vkZAOPprZolHzcafvpuLW+O48AOwA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Various filesystems use the lookup_one_len() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by enabling lookup_one_len() to take the idmap into account when calling inode_permission(). This change is a required to let btrfs (and other filesystems) support idmapped mounts. Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- arch/s390/hypfs/inode.c | 2 +- drivers/android/binderfs.c | 4 ++-- drivers/infiniband/hw/qib/qib_fs.c | 5 +++-- fs/afs/dir.c | 2 +- fs/afs/dir_silly.c | 2 +- fs/afs/dynroot.c | 4 ++-- fs/binfmt_misc.c | 2 +- fs/btrfs/ioctl.c | 4 ++-- fs/cachefiles/namei.c | 9 +++++---- fs/debugfs/inode.c | 6 ++++-- fs/exportfs/expfs.c | 3 ++- fs/namei.c | 15 +++++++++------ fs/nfs/unlink.c | 3 ++- fs/nfsd/nfs4recover.c | 7 ++++--- fs/nfsd/nfsproc.c | 3 ++- fs/nfsd/vfs.c | 19 ++++++++++--------- fs/overlayfs/copy_up.c | 10 ++++++---- fs/overlayfs/dir.c | 23 ++++++++++++----------- fs/overlayfs/export.c | 3 ++- fs/overlayfs/readdir.c | 12 +++++++----- fs/overlayfs/super.c | 8 +++++--- fs/overlayfs/util.c | 2 +- fs/reiserfs/xattr.c | 14 +++++++------- fs/tracefs/inode.c | 3 ++- include/linux/namei.h | 3 ++- ipc/mqueue.c | 5 +++-- kernel/bpf/inode.c | 2 +- security/apparmor/apparmorfs.c | 5 +++-- security/inode.c | 2 +- 29 files changed, 103 insertions(+), 79 deletions(-) diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 5c97f48cea91..e17d8c53a7a3 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -341,7 +341,7 @@ static struct dentry *hypfs_create_file(struct dentry *parent, const char *name, struct inode *inode; inode_lock(d_inode(parent)); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (IS_ERR(dentry)) { dentry = ERR_PTR(-ENOMEM); goto fail; diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e80ba93c62a9..3a55ffc6c4e5 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -178,7 +178,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, inode_lock(d_inode(root)); /* look it up */ - dentry = lookup_one_len(name, root, name_len); + dentry = lookup_one_len(&init_user_ns, name, root, name_len); if (IS_ERR(dentry)) { inode_unlock(d_inode(root)); ret = PTR_ERR(dentry); @@ -487,7 +487,7 @@ static struct dentry *binderfs_create_dentry(struct dentry *parent, { struct dentry *dentry; - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (IS_ERR(dentry)) return dentry; diff --git a/drivers/infiniband/hw/qib/qib_fs.c b/drivers/infiniband/hw/qib/qib_fs.c index a0c5f3bdc324..f85cfee80de5 100644 --- a/drivers/infiniband/hw/qib/qib_fs.c +++ b/drivers/infiniband/hw/qib/qib_fs.c @@ -91,7 +91,7 @@ static int create_file(const char *name, umode_t mode, int error; inode_lock(d_inode(parent)); - *dentry = lookup_one_len(name, parent, strlen(name)); + *dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (!IS_ERR(*dentry)) error = qibfs_mknod(d_inode(parent), *dentry, mode, fops, data); @@ -434,7 +434,8 @@ static int remove_device_files(struct super_block *sb, char unit[10]; snprintf(unit, sizeof(unit), "%u", dd->unit); - dir = lookup_one_len_unlocked(unit, sb->s_root, strlen(unit)); + dir = lookup_one_len_unlocked(&init_user_ns, unit, sb->s_root, + strlen(unit)); if (IS_ERR(dir)) { pr_err("Lookup of %s failed\n", unit); diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 78719f2f567e..ca3e4980ad8b 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -986,7 +986,7 @@ static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry, } strcpy(p, name); - ret = lookup_one_len(buf, dentry->d_parent, len); + ret = lookup_one_len(&init_user_ns, buf, dentry->d_parent, len); if (IS_ERR(ret) || d_is_positive(ret)) goto out_s; dput(ret); diff --git a/fs/afs/dir_silly.c b/fs/afs/dir_silly.c index dae9a57d7ec0..341bf214cbdf 100644 --- a/fs/afs/dir_silly.c +++ b/fs/afs/dir_silly.c @@ -122,7 +122,7 @@ int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode, * understood by the salvager and must not be changed. */ slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter); - sdentry = lookup_one_len(silly, dentry->d_parent, slen); + sdentry = lookup_one_len(&init_user_ns, silly, dentry->d_parent, slen); /* N.B. Better to return EBUSY here ... it could be dangerous * to delete the file while it's in use. diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index db832cc931c8..6b89d68c8e44 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -202,7 +202,7 @@ static struct dentry *afs_lookup_atcell(struct dentry *dentry) if (!cell) goto out_n; - ret = lookup_one_len(name, dentry->d_parent, len); + ret = lookup_one_len(&init_user_ns, name, dentry->d_parent, len); /* We don't want to d_add() the @cell dentry here as we don't want to * the cached dentry to hide changes to the local cell name. @@ -285,7 +285,7 @@ int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell) /* Let the ->lookup op do the creation */ root = sb->s_root; inode_lock(root->d_inode); - subdir = lookup_one_len(cell->name, root, cell->name_len); + subdir = lookup_one_len(&init_user_ns, cell->name, root, cell->name_len); if (IS_ERR(subdir)) { ret = PTR_ERR(subdir); goto unlock; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index e1eae7ea823a..3c8bf8f551ee 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -668,7 +668,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer, } inode_lock(d_inode(root)); - dentry = lookup_one_len(e->name, root, strlen(e->name)); + dentry = lookup_one_len(&init_user_ns, e->name, root, strlen(e->name)); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0ba98e08a029..8ec67e52fde3 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -893,7 +893,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(name, parent->dentry, namelen); + dentry = lookup_one_len(&init_user_ns, name, parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; @@ -3016,7 +3016,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); if (err == -EINTR) goto free_subvol_name; - dentry = lookup_one_len(subvol_name, parent, subvol_namelen); + dentry = lookup_one_len(&init_user_ns, subvol_name, parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 7bf0732ae25c..40497c6b6376 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -367,7 +367,8 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, return -EIO; } - grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); + grave = lookup_one_len(&init_user_ns, nbuffer, + cache->graveyard, strlen(nbuffer)); if (IS_ERR(grave)) { unlock_rename(cache->graveyard, dir); @@ -536,7 +537,7 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent, inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); start = jiffies; - next = lookup_one_len(name, dir, nlen); + next = lookup_one_len(&init_user_ns, name, dir, nlen); cachefiles_hist(cachefiles_lookup_histogram, start); if (IS_ERR(next)) { trace_cachefiles_lookup(object, next, NULL); @@ -776,7 +777,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, retry: start = jiffies; - subdir = lookup_one_len(dirname, dir, strlen(dirname)); + subdir = lookup_one_len(&init_user_ns, dirname, dir, strlen(dirname)); cachefiles_hist(cachefiles_lookup_histogram, start); if (IS_ERR(subdir)) { if (PTR_ERR(subdir) == -ENOMEM) @@ -886,7 +887,7 @@ static struct dentry *cachefiles_check_active(struct cachefiles_cache *cache, inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); start = jiffies; - victim = lookup_one_len(filename, dir, strlen(filename)); + victim = lookup_one_len(&init_user_ns, filename, dir, strlen(filename)); cachefiles_hist(cachefiles_lookup_histogram, start); if (IS_ERR(victim)) goto lookup_error; diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 8129a430d789..92db343f35f4 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -349,7 +349,8 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) if (unlikely(IS_DEADDIR(d_inode(parent)))) dentry = ERR_PTR(-ENOENT); else - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, + parent, strlen(name)); if (!IS_ERR(dentry) && d_really_is_positive(dentry)) { if (d_is_dir(dentry)) pr_err("Directory '%s' with parent '%s' already present!\n", @@ -775,7 +776,8 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, if (d_really_is_negative(old_dentry) || old_dentry == trap || d_mountpoint(old_dentry)) goto exit; - dentry = lookup_one_len(new_name, new_dir, strlen(new_name)); + dentry = lookup_one_len(&init_user_ns, new_name, + new_dir, strlen(new_name)); /* Lookup failed, cyclic rename or target exists? */ if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry)) goto exit; diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 0106eba46d5a..9ba408594094 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -525,7 +525,8 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, } inode_lock(target_dir->d_inode); - nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); + nresult = lookup_one_len(&init_user_ns, nbuf, + target_dir, strlen(nbuf)); if (!IS_ERR(nresult)) { if (unlikely(nresult->d_inode != result->d_inode)) { dput(nresult); diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..5a3e8188585e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2575,7 +2575,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, +static int lookup_one_len_common(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, int len, struct qstr *this) { this->name = name; @@ -2604,7 +2605,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC); + return inode_permission(mnt_userns, base->d_inode, MAY_EXEC); } /** @@ -2628,7 +2629,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2638,6 +2639,7 @@ EXPORT_SYMBOL(try_lookup_one_len); /** * lookup_one_len - filesystem helper to lookup single pathname component + * @mnt_userns: user namespace of the mount the lookup is performed from * @name: pathname component to lookup * @base: base directory to lookup from * @len: maximum length @len should be interpreted to @@ -2647,7 +2649,8 @@ EXPORT_SYMBOL(try_lookup_one_len); * * The caller must hold base->i_mutex. */ -struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *lookup_one_len(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, int len) { struct dentry *dentry; struct qstr this; @@ -2655,7 +2658,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(mnt_userns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2683,7 +2686,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 5fa11e1aca4c..b519f97a2b19 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -463,7 +463,8 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) dfprintk(VFS, "NFS: trying to rename %pd to %s\n", dentry, silly); - sdentry = lookup_one_len(silly, dentry->d_parent, slen); + sdentry = lookup_one_len(&init_user_ns, silly, + dentry->d_parent, slen); /* * N.B. Better to return EBUSY here ... it could be * dangerous to delete the file while it's in use. diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 6fedc49726bf..0ac319feea55 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -218,7 +218,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) /* lock the parent */ inode_lock(d_inode(dir)); - dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1); + dentry = lookup_one_len(&init_user_ns, dname, dir, HEXDIR_LEN-1); if (IS_ERR(dentry)) { status = PTR_ERR(dentry); goto out_unlock; @@ -313,7 +313,8 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) list_for_each_entry_safe(entry, tmp, &ctx.names, list) { if (!status) { struct dentry *dentry; - dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); + dentry = lookup_one_len(&init_user_ns, entry->name, + dir, HEXDIR_LEN-1); if (IS_ERR(dentry)) { status = PTR_ERR(dentry); break; @@ -345,7 +346,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn) dir = nn->rec_file->f_path.dentry; inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - dentry = lookup_one_len(name, dir, namlen); + dentry = lookup_one_len(&init_user_ns, name, dir, namlen); if (IS_ERR(dentry)) { status = PTR_ERR(dentry); goto out_unlock; diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 60d7c59e7935..4a2473ae81cb 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -292,7 +292,8 @@ nfsd_proc_create(struct svc_rqst *rqstp) } fh_lock_nested(dirfhp, I_MUTEX_PARENT); - dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); + dchild = lookup_one_len(&init_user_ns, argp->name, + dirfhp->fh_dentry, argp->len); if (IS_ERR(dchild)) { resp->status = nfserrno(PTR_ERR(dchild)); goto out_unlock; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a224a5e23cc1..962d718faf38 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -205,7 +205,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, * need to take the child's i_mutex: */ fh_lock_nested(fhp, I_MUTEX_PARENT); - dentry = lookup_one_len(name, dparent, len); + dentry = lookup_one_len(&init_user_ns, name, dparent, len); host_err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; @@ -1277,7 +1277,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, host_err = vfs_mkdir(&init_user_ns, dirp, dchild, iap->ia_mode); if (!host_err && unlikely(d_unhashed(dchild))) { struct dentry *d; - d = lookup_one_len(dchild->d_name.name, + d = lookup_one_len(&init_user_ns, + dchild->d_name.name, dchild->d_parent, dchild->d_name.len); if (IS_ERR(d)) { @@ -1367,7 +1368,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, return nfserrno(host_err); fh_lock_nested(fhp, I_MUTEX_PARENT); - dchild = lookup_one_len(fname, dentry, flen); + dchild = lookup_one_len(&init_user_ns, fname, dentry, flen); host_err = PTR_ERR(dchild); if (IS_ERR(dchild)) return nfserrno(host_err); @@ -1424,7 +1425,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, /* * Compose the response file handle. */ - dchild = lookup_one_len(fname, dentry, flen); + dchild = lookup_one_len(&init_user_ns, fname, dentry, flen); host_err = PTR_ERR(dchild); if (IS_ERR(dchild)) goto out_nfserr; @@ -1620,7 +1621,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, fh_lock(fhp); dentry = fhp->fh_dentry; - dnew = lookup_one_len(fname, dentry, flen); + dnew = lookup_one_len(&init_user_ns, fname, dentry, flen); host_err = PTR_ERR(dnew); if (IS_ERR(dnew)) goto out_nfserr; @@ -1683,7 +1684,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ddir = ffhp->fh_dentry; dirp = d_inode(ddir); - dnew = lookup_one_len(name, ddir, len); + dnew = lookup_one_len(&init_user_ns, name, ddir, len); host_err = PTR_ERR(dnew); if (IS_ERR(dnew)) goto out_nfserr; @@ -1783,7 +1784,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, fill_pre_wcc(ffhp); fill_pre_wcc(tfhp); - odentry = lookup_one_len(fname, fdentry, flen); + odentry = lookup_one_len(&init_user_ns, fname, fdentry, flen); host_err = PTR_ERR(odentry); if (IS_ERR(odentry)) goto out_nfserr; @@ -1795,7 +1796,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, if (odentry == trap) goto out_dput_old; - ndentry = lookup_one_len(tname, tdentry, tlen); + ndentry = lookup_one_len(&init_user_ns, tname, tdentry, tlen); host_err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) goto out_dput_old; @@ -1893,7 +1894,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, dentry = fhp->fh_dentry; dirp = d_inode(dentry); - rdentry = lookup_one_len(fname, dentry, flen); + rdentry = lookup_one_len(&init_user_ns, fname, dentry, flen); host_err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) goto out_drop_write; diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 2846b943e80c..ece61c70d50f 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -420,7 +420,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin, if (err) goto out; - index = lookup_one_len(name.name, indexdir, name.len); + index = lookup_one_len(&init_user_ns, name.name, indexdir, name.len); if (IS_ERR(index)) { err = PTR_ERR(index); } else { @@ -468,7 +468,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) return err; inode_lock_nested(udir, I_MUTEX_PARENT); - upper = lookup_one_len(c->dentry->d_name.name, upperdir, + upper = lookup_one_len(&init_user_ns, c->dentry->d_name.name, upperdir, c->dentry->d_name.len); err = PTR_ERR(upper); if (!IS_ERR(upper)) { @@ -620,7 +620,8 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) goto cleanup; } - upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len); + upper = lookup_one_len(&init_user_ns, c->destname.name, + c->destdir, c->destname.len); err = PTR_ERR(upper); if (IS_ERR(upper)) goto cleanup; @@ -671,7 +672,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) inode_lock_nested(udir, I_MUTEX_PARENT); - upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len); + upper = lookup_one_len(&init_user_ns, c->destname.name, + c->destdir, c->destname.len); err = PTR_ERR(upper); if (!IS_ERR(upper)) { err = ovl_do_link(temp, udir, upper); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 93efe7048a77..df8fc89416db 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -51,7 +51,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir) /* counter is allowed to wrap, since temp dentries are ephemeral */ snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id)); - temp = lookup_one_len(name, workdir, strlen(name)); + temp = lookup_one_len(&init_user_ns, name, workdir, strlen(name)); if (!IS_ERR(temp) && temp->d_inode) { pr_err("workdir/%s already exists\n", name); dput(temp); @@ -155,8 +155,8 @@ static int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, * to it unhashed and negative. If that happens, try to * lookup a new hashed and positive dentry. */ - d = lookup_one_len(dentry->d_name.name, dentry->d_parent, - dentry->d_name.len); + d = lookup_one_len(&init_user_ns, dentry->d_name.name, + dentry->d_parent, dentry->d_name.len); if (IS_ERR(d)) { pr_warn("failed lookup after mkdir (%pd2, err=%i).\n", dentry, err); @@ -330,7 +330,8 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = ovl_create_real(udir, - lookup_one_len(dentry->d_name.name, + lookup_one_len(&init_user_ns, + dentry->d_name.name, upperdir, dentry->d_name.len), attr); @@ -482,7 +483,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, if (err) goto out; - upper = lookup_one_len(dentry->d_name.name, upperdir, + upper = lookup_one_len(&init_user_ns, dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) @@ -763,7 +764,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, if (err) goto out_dput; - upper = lookup_one_len(dentry->d_name.name, upperdir, + upper = lookup_one_len(&init_user_ns, dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) @@ -810,7 +811,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir, } inode_lock_nested(dir, I_MUTEX_PARENT); - upper = lookup_one_len(dentry->d_name.name, upperdir, + upper = lookup_one_len(&init_user_ns, dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) @@ -1184,8 +1185,8 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, trap = lock_rename(new_upperdir, old_upperdir); - olddentry = lookup_one_len(old->d_name.name, old_upperdir, - old->d_name.len); + olddentry = lookup_one_len(&init_user_ns, old->d_name.name, + old_upperdir, old->d_name.len); err = PTR_ERR(olddentry); if (IS_ERR(olddentry)) goto out_unlock; @@ -1194,8 +1195,8 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, if (!ovl_matches_upper(old, olddentry)) goto out_dput_old; - newdentry = lookup_one_len(new->d_name.name, new_upperdir, - new->d_name.len); + newdentry = lookup_one_len(&init_user_ns, new->d_name.name, + new_upperdir, new->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_dput_old; diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 41ebf52f1bbc..5469b1f36039 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -391,7 +391,8 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected, * pointer because we hold no lock on the real dentry. */ take_dentry_name_snapshot(&name, real); - this = lookup_one_len(name.name.name, connected, name.name.len); + this = lookup_one_len(&init_user_ns, name.name.name, + connected, name.name.len); err = PTR_ERR(this); if (IS_ERR(this)) { goto fail; diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index e8ad2c2c77dd..3de32997ea28 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -278,7 +278,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) while (rdd->first_maybe_whiteout) { p = rdd->first_maybe_whiteout; rdd->first_maybe_whiteout = p->next_maybe_whiteout; - dentry = lookup_one_len(p->name, dir, p->len); + dentry = lookup_one_len(&init_user_ns, p->name, + dir, p->len); if (!IS_ERR(dentry)) { p->is_whiteout = ovl_is_whiteout(dentry); dput(dentry); @@ -479,7 +480,7 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) goto get; } } - this = lookup_one_len(p->name, dir, p->len); + this = lookup_one_len(&init_user_ns, p->name, dir, p->len); if (IS_ERR_OR_NULL(this) || !this->d_inode) { if (IS_ERR(this)) { err = PTR_ERR(this); @@ -1007,7 +1008,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) if (WARN_ON(!p->is_whiteout || !p->is_upper)) continue; - dentry = lookup_one_len(p->name, upper, p->len); + dentry = lookup_one_len(&init_user_ns, p->name, upper, p->len); if (IS_ERR(dentry)) { pr_err("lookup '%s/%.*s' failed (%i)\n", upper->d_name.name, p->len, p->name, @@ -1106,7 +1107,8 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level) err = -EINVAL; break; } - dentry = lookup_one_len(p->name, path->dentry, p->len); + dentry = lookup_one_len(&init_user_ns, p->name, + path->dentry, p->len); if (IS_ERR(dentry)) continue; if (dentry->d_inode) @@ -1174,7 +1176,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) if (p->len == 2 && p->name[1] == '.') continue; } - index = lookup_one_len(p->name, indexdir, p->len); + index = lookup_one_len(&init_user_ns, p->name, indexdir, p->len); if (IS_ERR(index)) { err = PTR_ERR(index); index = NULL; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index b01d4147520d..58c592f52b5b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -761,7 +761,8 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, inode_lock_nested(dir, I_MUTEX_PARENT); retry: - work = lookup_one_len(name, ofs->workbasedir, strlen(name)); + work = lookup_one_len(&init_user_ns, name, + ofs->workbasedir, strlen(name)); if (!IS_ERR(work)) { struct iattr attr = { @@ -1284,7 +1285,8 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) goto cleanup_temp; } - whiteout = lookup_one_len(name.name.name, workdir, name.name.len); + whiteout = lookup_one_len(&init_user_ns, name.name.name, + workdir, name.name.len); err = PTR_ERR(whiteout); if (IS_ERR(whiteout)) goto cleanup_temp; @@ -1315,7 +1317,7 @@ static struct dentry *ovl_lookup_or_create(struct dentry *parent, struct dentry *child; inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); - child = lookup_one_len(name, parent, len); + child = lookup_one_len(&init_user_ns, name, parent, len); if (!IS_ERR(child) && !child->d_inode) child = ovl_create_real(parent->d_inode, child, OVL_CATTR(mode)); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index b9d03627f364..1138c10814c5 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -740,7 +740,7 @@ static void ovl_cleanup_index(struct dentry *dentry) } inode_lock_nested(dir, I_MUTEX_PARENT); - index = lookup_one_len(name.name, indexdir, name.len); + index = lookup_one_len(&init_user_ns, name.name, indexdir, name.len); err = PTR_ERR(index); if (IS_ERR(index)) { index = NULL; diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index bd073836e141..d70f4ffe5d6b 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -159,7 +159,7 @@ static struct dentry *open_xa_dir(const struct inode *inode, int flags) inode_lock_nested(d_inode(xaroot), I_MUTEX_XATTR); - xadir = lookup_one_len(namebuf, xaroot, strlen(namebuf)); + xadir = lookup_one_len(&init_user_ns, namebuf, xaroot, strlen(namebuf)); if (!IS_ERR(xadir) && d_really_is_negative(xadir)) { int err = -ENODATA; @@ -206,7 +206,7 @@ fill_with_dentries(struct dir_context *ctx, const char *name, int namelen, (namelen == 2 && name[1] == '.'))) return 0; - dentry = lookup_one_len(name, dbuf->xadir, namelen); + dentry = lookup_one_len(&init_user_ns, name, dbuf->xadir, namelen); if (IS_ERR(dentry)) { dbuf->err = PTR_ERR(dentry); return PTR_ERR(dentry); @@ -397,7 +397,7 @@ static struct dentry *xattr_lookup(struct inode *inode, const char *name, return ERR_CAST(xadir); inode_lock_nested(d_inode(xadir), I_MUTEX_XATTR); - xafile = lookup_one_len(name, xadir, strlen(name)); + xafile = lookup_one_len(&init_user_ns, name, xadir, strlen(name)); if (IS_ERR(xafile)) { err = PTR_ERR(xafile); goto out; @@ -491,7 +491,7 @@ static int lookup_and_delete_xattr(struct inode *inode, const char *name) return PTR_ERR(xadir); inode_lock_nested(d_inode(xadir), I_MUTEX_XATTR); - dentry = lookup_one_len(name, xadir, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, xadir, strlen(name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_dput; @@ -977,7 +977,7 @@ int reiserfs_lookup_privroot(struct super_block *s) /* If we don't have the privroot located yet - go find it */ inode_lock(d_inode(s->s_root)); - dentry = lookup_one_len(PRIVROOT_NAME, s->s_root, + dentry = lookup_one_len(&init_user_ns, PRIVROOT_NAME, s->s_root, strlen(PRIVROOT_NAME)); if (!IS_ERR(dentry)) { REISERFS_SB(s)->priv_root = dentry; @@ -1018,8 +1018,8 @@ int reiserfs_xattr_init(struct super_block *s, int mount_flags) if (!REISERFS_SB(s)->xattr_root) { struct dentry *dentry; - dentry = lookup_one_len(XAROOT_NAME, privroot, - strlen(XAROOT_NAME)); + dentry = lookup_one_len(&init_user_ns, XAROOT_NAME, + privroot, strlen(XAROOT_NAME)); if (!IS_ERR(dentry)) REISERFS_SB(s)->xattr_root = dentry; else diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 1261e8b41edb..a704d84c9d22 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -335,7 +335,8 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) if (unlikely(IS_DEADDIR(parent->d_inode))) dentry = ERR_PTR(-ENOENT); else - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, + parent, strlen(name)); if (!IS_ERR(dentry) && dentry->d_inode) { dput(dentry); dentry = ERR_PTR(-EEXIST); diff --git a/include/linux/namei.h b/include/linux/namei.h index be9a2b349ca7..7f8b58b43075 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -65,7 +65,8 @@ extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); -extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len(struct user_namespace *mnt_userns, + const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 5becca9be867..4c84805161e8 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -899,7 +899,8 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, ro = mnt_want_write(mnt); /* we'll drop it in any case */ inode_lock(d_inode(root)); - path.dentry = lookup_one_len(name->name, root, strlen(name->name)); + path.dentry = lookup_one_len(&init_user_ns, name->name, + root, strlen(name->name)); if (IS_ERR(path.dentry)) { error = PTR_ERR(path.dentry); goto out_putfd; @@ -955,7 +956,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) if (err) goto out_name; inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT); - dentry = lookup_one_len(name->name, mnt->mnt_root, + dentry = lookup_one_len(&init_user_ns, name->name, mnt->mnt_root, strlen(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 80da1db47c68..5e50f9fc7dd1 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -423,7 +423,7 @@ static int bpf_iter_link_pin_kernel(struct dentry *parent, int ret; inode_lock(parent->d_inode); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (IS_ERR(dentry)) { inode_unlock(parent->d_inode); return PTR_ERR(dentry); diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2ee3b3d29f10..02c61284aab7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -280,7 +280,7 @@ static struct dentry *aafs_create(const char *name, umode_t mode, dir = d_inode(parent); inode_lock(dir); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); goto fail_lock; @@ -2504,7 +2504,8 @@ static int aa_mk_null_file(struct dentry *parent) return error; inode_lock(d_inode(parent)); - dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME)); + dentry = lookup_one_len(&init_user_ns, NULL_FILE_NAME, + parent, strlen(NULL_FILE_NAME)); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); goto out; diff --git a/security/inode.c b/security/inode.c index 6c326939750d..6b89567be9a6 100644 --- a/security/inode.c +++ b/security/inode.c @@ -128,7 +128,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, dir = d_inode(parent); inode_lock(dir); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len(&init_user_ns, name, parent, strlen(name)); if (IS_ERR(dentry)) goto out; From patchwork Tue Jul 13 11:13:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373697 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0DFB6C11F66 for ; Tue, 13 Jul 2021 11:15:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E68BB6128B for ; Tue, 13 Jul 2021 11:15:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235859AbhGMLSU (ORCPT ); Tue, 13 Jul 2021 07:18:20 -0400 Received: from mail.kernel.org ([198.145.29.99]:46782 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235390AbhGMLSU (ORCPT ); Tue, 13 Jul 2021 07:18:20 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 0B4AB61284; Tue, 13 Jul 2021 11:15:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174930; bh=jfbIbTyMezX9eUfQmSzLH7TeEwmPEjP7Oxepa+6yAcg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XrkR9p8KNVc6cu7AY2H4b//CGNZIwin1Kye2UNeV2aZFi/aFNba2k6AghwnGIllxx aK/kaIyzs0EMii7zHX2ZLFyW/KrWv2405FQi9ogNMF66QNmMjAEnWQA6eAJzP6Ec5h 81zN6hC8Xc651KvmARFaEhQ7Tab5Nj6GSBd+U/KXYVEf6ZhffyAgRS+AWeUQ0hgDwP TriNXARbt/XowijAyA65CPjxyxytkXdRAzlPOWUdy9pCVPUfXPVypPBPoc8quUtTkO AvpTAqHttmjtJ7JfgjVwGplQoP0POC95y9t6ANCBwQlkYL/mTQY0rXiTwWMJK0G78/ DL/bZDywi6ztw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 02/24] namei: handle mappings in lookup_one_len_unlocked() Date: Tue, 13 Jul 2021 13:13:22 +0200 Message-Id: <20210713111344.1149376-3-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4542; h=from:subject; bh=ecm/BrL4GCunvk1nKO2IhmCgiKCJ9C8BgEZE8gY2tgc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LQ2f0qVwU03t0zPhmfuzL+3amf90zdwFe3+sDryszRy/ bveVQx2lLAxiXAyyYoosDu0m4XLLeSo2G2VqwMxhZQIZwsDFKQATcfRn+F++yH9P15ryaIawuxINHX f9Hm5elNW6JVLrlFuMldyHiw0M/0wvOf71fT1R0CrN/HvWQZ9wZ5eLfNNbvc95BlnMaHXawAUA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Various filesystems use the lookup_one_len_unlocked() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by enabling lookup_one_len_unlocked() to take the idmap into account when calling inode_permission(). Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/ecryptfs/inode.c | 3 ++- fs/exportfs/expfs.c | 3 ++- fs/namei.c | 9 +++++---- fs/overlayfs/namei.c | 3 ++- include/linux/namei.h | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 16d50dface59..755975a214a9 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -405,7 +405,8 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, name = encrypted_and_encoded_name; } - lower_dentry = lookup_one_len_unlocked(name, lower_dir_dentry, len); + lower_dentry = lookup_one_len_unlocked(&init_user_ns, name, + lower_dir_dentry, len); if (IS_ERR(lower_dentry)) { ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " "[%ld] on lower_dentry = [%s]\n", __func__, diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 9ba408594094..0e35efba2bc5 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -145,7 +145,8 @@ static struct dentry *reconnect_one(struct vfsmount *mnt, if (err) goto out_err; dprintk("%s: found name: %s\n", __func__, nbuf); - tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); + tmp = lookup_one_len_unlocked(&init_user_ns, nbuf, + parent, strlen(nbuf)); if (IS_ERR(tmp)) { dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); err = PTR_ERR(tmp); diff --git a/fs/namei.c b/fs/namei.c index 5a3e8188585e..53561311b492 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2679,14 +2679,15 @@ EXPORT_SYMBOL(lookup_one_len); * Unlike lookup_one_len, it should be called without the parent * i_mutex held, and will take the i_mutex itself if necessary. */ -struct dentry *lookup_one_len_unlocked(const char *name, - struct dentry *base, int len) +struct dentry *lookup_one_len_unlocked(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, + int len) { struct qstr this; int err; struct dentry *ret; - err = lookup_one_len_common(&init_user_ns, name, base, len, &this); + err = lookup_one_len_common(mnt_userns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2708,7 +2709,7 @@ EXPORT_SYMBOL(lookup_one_len_unlocked); struct dentry *lookup_positive_unlocked(const char *name, struct dentry *base, int len) { - struct dentry *ret = lookup_one_len_unlocked(name, base, len); + struct dentry *ret = lookup_one_len_unlocked(&init_user_ns, name, base, len); if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) { dput(ret); ret = ERR_PTR(-ENOENT); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 210cd6f66e28..291985b79a6d 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -202,7 +202,8 @@ static struct dentry *ovl_lookup_positive_unlocked(const char *name, struct dentry *base, int len, bool drop_negative) { - struct dentry *ret = lookup_one_len_unlocked(name, base, len); + struct dentry *ret = lookup_one_len_unlocked(&init_user_ns, name, + base, len); if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) { if (drop_negative && ret->d_lockref.count == 1) { diff --git a/include/linux/namei.h b/include/linux/namei.h index 7f8b58b43075..b4073e36450a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -67,7 +67,8 @@ extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(struct user_namespace *mnt_userns, const char *, struct dentry *, int); -extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len_unlocked(struct user_namespace *, + const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); extern int follow_down_one(struct path *); From patchwork Tue Jul 13 11:13:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373699 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F315AC07E95 for ; Tue, 13 Jul 2021 11:15:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D77E36128B for ; Tue, 13 Jul 2021 11:15:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235864AbhGMLSZ (ORCPT ); Tue, 13 Jul 2021 07:18:25 -0400 Received: from mail.kernel.org ([198.145.29.99]:46816 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235390AbhGMLSZ (ORCPT ); Tue, 13 Jul 2021 07:18:25 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 135B36023F; Tue, 13 Jul 2021 11:15:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174935; bh=9qvYGFFvCFJdEJtArvHjO2+pUJ+InQnuNkHlgbKL7sY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=g4EshPL4vnyNryZlpd83w3q6SqWS1Ar2uYDYsruyvLxSCU9a2EvLIxgPoobSAB0mj AvtdwDdXu4v8OQxuyjp9GdHAkdeEFGojkWE9POUAwP4RwUW0QNhyCps9BGQXHG6otS 5TLiFJpkBlAngDxY2DlwiVGYZ1DcCotAjJ5twJ7vvEKY96brMXNQ2kiFPRF/zrVdPE GWgVN5vV5n1UqyUxMPilJWbrOmabtq7kuafw97xvh1f93Zdlw3zYAIVgx/a7lW2t4d fuVrEKN2GyjYc5e+4yWeBPHtT8PzjK1p47Ule2Ynn3uoXLhFQ/Q1eNcKyNs97NZzkx 2ekR1OGVrnISw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 03/24] namei: handle mappings in lookup_positive_unlocked() Date: Tue, 13 Jul 2021 13:13:23 +0200 Message-Id: <20210713111344.1149376-4-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6848; h=from:subject; bh=/iZaQudmGJtAMTQgRcbLJUCNcpqUKhwGKbfR410yXTc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY34aWi0YjKXn94+x6dKF97NeX+pZu46RXWbniZR36dB kTvfdpSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAExk+2uG/5miZVy5TNm7pC2uWnvc+n jydU7VlfP+j6VOcvKvmWq09jbD/9gq9vV8QjzPJI9HOX46t/luZ7Yr02sLi4An0Tzz1iy/yA8A X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Various filesystems use the lookup_positive_unlocked() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by enabling lookup_positive_unlocked() to take the idmap into account when calling inode_permission(). This change is a required to let btrfs (and other filesystems) support idmapped mounts. Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/cifs/cifsfs.c | 3 ++- fs/debugfs/inode.c | 3 ++- fs/kernfs/mount.c | 4 ++-- fs/namei.c | 7 ++++--- fs/nfsd/nfs3xdr.c | 3 ++- fs/nfsd/nfs4xdr.c | 3 ++- fs/overlayfs/namei.c | 10 ++++++---- fs/quota/dquot.c | 3 ++- include/linux/namei.h | 3 ++- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 64b71c4e2a9d..b61643427e46 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -804,7 +804,8 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) while (*s && *s != sep) s++; - child = lookup_positive_unlocked(p, dentry, s - p); + child = lookup_positive_unlocked(&init_user_ns, p, + dentry, s - p); dput(dentry); dentry = child; } while (!IS_ERR(dentry)); diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 92db343f35f4..eda31d0966a5 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -307,7 +307,8 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent) if (!parent) parent = debugfs_mount->mnt_root; - dentry = lookup_positive_unlocked(name, parent, strlen(name)); + dentry = lookup_positive_unlocked(&init_user_ns, name, + parent, strlen(name)); if (IS_ERR(dentry)) return NULL; return dentry; diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 9dc7e7a64e10..20b8617e619b 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -223,8 +223,8 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn, dput(dentry); return ERR_PTR(-EINVAL); } - dtmp = lookup_positive_unlocked(kntmp->name, dentry, - strlen(kntmp->name)); + dtmp = lookup_positive_unlocked(&init_user_ns, kntmp->name, + dentry, strlen(kntmp->name)); dput(dentry); if (IS_ERR(dtmp)) return dtmp; diff --git a/fs/namei.c b/fs/namei.c index 53561311b492..68489c23bc44 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2706,10 +2706,11 @@ EXPORT_SYMBOL(lookup_one_len_unlocked); * need to be very careful; pinned positives have ->d_inode stable, so * this one avoids such problems. */ -struct dentry *lookup_positive_unlocked(const char *name, - struct dentry *base, int len) +struct dentry *lookup_positive_unlocked(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, + int len) { - struct dentry *ret = lookup_one_len_unlocked(&init_user_ns, name, base, len); + struct dentry *ret = lookup_one_len_unlocked(mnt_userns, name, base, len); if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) { dput(ret); ret = ERR_PTR(-ENOENT); diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 0a5ebc52e6a9..0fad12be94cd 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -1109,7 +1109,8 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, } else dchild = dget(dparent); } else - dchild = lookup_positive_unlocked(name, dparent, namlen); + dchild = lookup_positive_unlocked(&init_user_ns, name, + dparent, namlen); if (IS_ERR(dchild)) return rv; if (d_mountpoint(dchild)) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 7abeccb975b2..a0425d120e6a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3413,7 +3413,8 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd, __be32 nfserr; int ignore_crossmnt = 0; - dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen); + dentry = lookup_positive_unlocked(&init_user_ns, name, + cd->rd_fhp->fh_dentry, namlen); if (IS_ERR(dentry)) return nfserrno(PTR_ERR(dentry)); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 291985b79a6d..52721af35dcf 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -673,7 +673,8 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh) if (err) return ERR_PTR(err); - index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len); + index = lookup_positive_unlocked(&init_user_ns, name.name, + ofs->indexdir, name.len); kfree(name.name); if (IS_ERR(index)) { if (PTR_ERR(index) == -ENOENT) @@ -705,7 +706,8 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper, if (err) return ERR_PTR(err); - index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len); + index = lookup_positive_unlocked(&init_user_ns, name.name, + ofs->indexdir, name.len); if (IS_ERR(index)) { err = PTR_ERR(index); if (err == -ENOENT) { @@ -1164,8 +1166,8 @@ bool ovl_lower_positive(struct dentry *dentry) struct dentry *this; struct dentry *lowerdir = poe->lowerstack[i].dentry; - this = lookup_positive_unlocked(name->name, lowerdir, - name->len); + this = lookup_positive_unlocked(&init_user_ns, name->name, + lowerdir, name->len); if (IS_ERR(this)) { switch (PTR_ERR(this)) { case -ENOENT: diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 22d904bde6ab..8cdd6df7597a 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2487,7 +2487,8 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name, struct dentry *dentry; int error; - dentry = lookup_positive_unlocked(qf_name, sb->s_root, strlen(qf_name)); + dentry = lookup_positive_unlocked(&init_user_ns, qf_name, + sb->s_root, strlen(qf_name)); if (IS_ERR(dentry)) return PTR_ERR(dentry); diff --git a/include/linux/namei.h b/include/linux/namei.h index b4073e36450a..c177edfb2364 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -69,7 +69,8 @@ extern struct dentry *lookup_one_len(struct user_namespace *mnt_userns, const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(struct user_namespace *, const char *, struct dentry *, int); -extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_positive_unlocked(struct user_namespace *, + const char *, struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); From patchwork Tue Jul 13 11:13:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373701 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17F41C07E96 for ; Tue, 13 Jul 2021 11:15:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0188D6128D for ; Tue, 13 Jul 2021 11:15:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235879AbhGMLSb (ORCPT ); Tue, 13 Jul 2021 07:18:31 -0400 Received: from mail.kernel.org ([198.145.29.99]:46860 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235709AbhGMLSa (ORCPT ); Tue, 13 Jul 2021 07:18:30 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2F5A66127C; Tue, 13 Jul 2021 11:15:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174941; bh=hidsKq1SmHOo5r8gQbPSJ5AaAhsbxGQWW2F55p9V0tM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oMvS0QIJn9lXrW5yjBfiOT9mbKHlYK+0JvK9PBdA5xKQa82t2g3nUpCAhmHrs/weL 4QZFb/o5dANPNej2JF3a5kbKMqvHKXHYOaWO16BgPGwko1/o90N+LkYdAhlOVDs0K0 sQ+YNfi8bXRUTFWUg56bqlO7Y+bZcUF0xc/EmONwYQbaHR0z7Q4a9tKmaqVW689Px6 P1uaS2acoljNgtb3gce+mj6xL/nfJ6cOu6vajDYo6w/M4/vxfnEkXMzlANa/Z2YR5+ 0cwW7oQVHcp7fgEvMsWRylpTgRuhHrC5CLh1QlCOsQN9+JCaYVLQyxorz3cckoRZK/ pJwRxAjCb53MA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 04/24] namei: handle mappings in try_lookup_one_len() Date: Tue, 13 Jul 2021 13:13:24 +0200 Message-Id: <20210713111344.1149376-5-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3230; h=from:subject; bh=GvOAx5y8ei8ICCoYDPc3ZZ1DfOkJ7Me2qIkoQsneRhA=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2Y/TWUObNq+/+AWWzT0hc9261W15r39VLF2eJmHcYa vpb7HaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABNJ8WT4K+4adP2Pm5RpSIXr9LhTk1 fOryrIW/OKyVCZ4fPq55WhGxkZ5v5hsNv67eaOP7mpRin6scbZrt8uhe4t7z4U97j6aZoUCwA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Various filesystems use the try_lookup_one_len() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by enabling try_lookup_one_len() to take the idmap into account when calling inode_permission(). This change is a required to let btrfs (and other filesystems) support idmapped mounts. Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/afs/dynroot.c | 2 +- fs/namei.c | 7 +++++-- include/linux/namei.h | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 6b89d68c8e44..9e96840e6952 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -315,7 +315,7 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell) inode_lock(root->d_inode); /* Don't want to trigger a lookup call, which will re-add the cell */ - subdir = try_lookup_one_len(cell->name, root, cell->name_len); + subdir = try_lookup_one_len(&init_user_ns, cell->name, root, cell->name_len); if (IS_ERR_OR_NULL(subdir)) { _debug("lookup %ld", PTR_ERR(subdir)); goto no_dentry; diff --git a/fs/namei.c b/fs/namei.c index 68489c23bc44..0866b88628b9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2610,6 +2610,7 @@ static int lookup_one_len_common(struct user_namespace *mnt_userns, /** * try_lookup_one_len - filesystem helper to lookup single pathname component + * @mnt_userns: user namespace of the mount the lookup is performed from * @name: pathname component to lookup * @base: base directory to lookup from * @len: maximum length @len should be interpreted to @@ -2622,14 +2623,16 @@ static int lookup_one_len_common(struct user_namespace *mnt_userns, * * The caller must hold base->i_mutex. */ -struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *try_lookup_one_len(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, + int len) { struct qstr this; int err; WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(&init_user_ns, name, base, len, &this); + err = lookup_one_len_common(mnt_userns, name, base, len, &this); if (err) return ERR_PTR(err); diff --git a/include/linux/namei.h b/include/linux/namei.h index c177edfb2364..206ea4702d62 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -64,7 +64,8 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *, extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); -extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *try_lookup_one_len(struct user_namespace *, const char *, + struct dentry *, int); extern struct dentry *lookup_one_len(struct user_namespace *mnt_userns, const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(struct user_namespace *, From patchwork Tue Jul 13 11:13:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373703 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B4A7C11F66 for ; Tue, 13 Jul 2021 11:15:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 84BE06128C for ; Tue, 13 Jul 2021 11:15:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235891AbhGMLSe (ORCPT ); Tue, 13 Jul 2021 07:18:34 -0400 Received: from mail.kernel.org ([198.145.29.99]:46892 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235709AbhGMLSe (ORCPT ); Tue, 13 Jul 2021 07:18:34 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 920C16128B; Tue, 13 Jul 2021 11:15:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174944; bh=wIYK+zTwQ7yPDwj/nToSjirA2iz4TxAWTPQjoOOdykY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e0xmy1AZt5dLXotHVZBlSnrrHV27znA7tThhN4QbeuZB2m4FFpKz3kSPOUgEju7Am iyW2O1i1wH1f8BhSfw5flL2lZQEoGVVEXuW71xCQ/vY6UZIhNKpfnL9BBbg8NGWM2n pL7bnnGcIHgOLB/38q7utTBFe8IUbwDHC06LdrxvE6yE2bXzYxPRB6oekPEXiwTEp8 toBY1AZs8gY6AnQzKmLS6sr11aDjENEmfpb56ib8awRZZNe+IQjT02/BbLrnt0mMlS ckBhqzrDJIZFaVGm7Ptxh+zxAJn69zWc90jFFmFhYDu3kVkbFRI3zgV/HlS9WJ9Fsn LhGH+ese+APhA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 05/24] btrfs/inode: handle idmaps in btrfs_new_inode() Date: Tue, 13 Jul 2021 13:13:25 +0200 Message-Id: <20210713111344.1149376-6-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4609; h=from:subject; bh=lqrQ2ADi/0WAeQ5SMUfIJPUaNfSV/gjOtKDcd4xJdwQ=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3wCHV0m9rQuWNX3Ex+hwWz7rBLL7F6ZN2+sfi67ao9 OeLNHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABPpCWL4Z/HxWTunycqTUStuVHGuvf yOa12Ju8Xvz6XrDTZ563jM/cPwh3f7q7zjc66KPbx32fzg33sXWnxX33+1Ikl5r5uxpMDRXHYA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Extend btrfs_new_inode() to take the idmapped mount into account when initializing a new inode. This is just a matter of passing down the mount's userns. The rest is taken care of in inode_init_owner(). This is a preliminary patch to make the individual btrfs inode operations idmapped mount aware. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e6eb20987351..d8aef4cf0972 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6281,6 +6281,7 @@ static void btrfs_inherit_iflags(struct inode *inode, struct inode *dir) static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct user_namespace *mnt_userns, struct inode *dir, const char *name, int name_len, u64 ref_objectid, u64 objectid, @@ -6390,7 +6391,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, if (ret != 0) goto fail_unlock; - inode_init_owner(&init_user_ns, inode, dir, mode); + inode_init_owner(mnt_userns, inode, dir, mode); inode_set_bytes(inode, 0); inode->i_mtime = current_time(inode); @@ -6575,9 +6576,9 @@ static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, - mode, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -6639,9 +6640,9 @@ static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, - mode, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -6784,8 +6785,9 @@ static int btrfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_fail; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, S_IFDIR | mode, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); @@ -8860,7 +8862,8 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, if (err < 0) return err; - inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, ino, ino, + inode = btrfs_new_inode(trans, new_root, &init_user_ns, NULL, "..", 2, + ino, ino, S_IFDIR | (~current_umask() & S_IRWXUGO), &index); if (IS_ERR(inode)) @@ -9353,7 +9356,7 @@ static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, if (ret) return ret; - inode = btrfs_new_inode(trans, root, dir, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), @@ -9852,9 +9855,10 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name, - dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), - objectid, S_IFLNK|S_IRWXUGO, &index); + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + dentry->d_name.name, dentry->d_name.len, + btrfs_ino(BTRFS_I(dir)), objectid, + S_IFLNK | S_IRWXUGO, &index); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -10203,7 +10207,7 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, if (ret) goto out; - inode = btrfs_new_inode(trans, root, dir, NULL, 0, + inode = btrfs_new_inode(trans, root, &init_user_ns, dir, NULL, 0, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { ret = PTR_ERR(inode); From patchwork Tue Jul 13 11:13:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373705 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F30A8C11F68 for ; Tue, 13 Jul 2021 11:15:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DA73661361 for ; Tue, 13 Jul 2021 11:15:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235922AbhGMLSm (ORCPT ); Tue, 13 Jul 2021 07:18:42 -0400 Received: from mail.kernel.org ([198.145.29.99]:46928 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235908AbhGMLSl (ORCPT ); Tue, 13 Jul 2021 07:18:41 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5D27561178; Tue, 13 Jul 2021 11:15:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174951; bh=OFkDKvNxw31SKltT2MtCz+x/XsUFs5mFaA5DgkAaxMM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L4d2d7XcU0jspdVQWJpb+mQgFlXYYcbYFECGS+E/uRjgjKXG0F4Bf06ky8P6G5IlZ fMn5SIiIhOUP/+agitE8PtMYo6QacwDVV2dpGFdrxy2WKLVtVBm/EgX79B9ZM9O36k hk0wA82QMPoWSFmBkEzAtnWYOZGVbaY+4I4w1NNku1aB9dXLyMnLX9e/gWCTP6hADz iSbtJ8pEGBYHJwhXNw86iPO7bcuPWRu3aPTAuYVUNp0J0y5VCln2hKs9UXOarlrXg/ otY3fG9pStrOO1irA9MtvEfVtiBbE7D3xRsOvHdRsrFvA3uX+w/xuIEVYecOt2IB7e UH8rcpd81d30g== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 06/24] btrfs/inode: allow idmapped rename iop Date: Tue, 13 Jul 2021 13:13:26 +0200 Message-Id: <20210713111344.1149376-7-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2584; h=from:subject; bh=+u0X0J58uOOWZch/PHzeHjkn22DUeLJo2mBzZTdaecE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY04rNPof+nHLxudQkHXV5+//3p0Puyy/aJpzVPuMqw4 vJHrfUcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBEZtxi+J+YPE2vLDfAU3BWpVTn+j vnpx8Vr3j5R8Qh/imPXugR5lOMDIujZ/1fsV9xevYMO+bvbOYKHwWrlqYWrp9UO3mL1kLLeTwA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_rename() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d8aef4cf0972..c0c386cf8a2e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9344,6 +9344,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, struct btrfs_root *root, + struct user_namespace *mnt_userns, struct inode *dir, struct dentry *dentry) { @@ -9356,7 +9357,7 @@ static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, if (ret) return ret; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), @@ -9393,9 +9394,10 @@ static int btrfs_whiteout_for_rename(struct btrfs_trans_handle *trans, return ret; } -static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) +static int btrfs_rename(struct user_namespace *mnt_userns, + struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct btrfs_fs_info *fs_info = btrfs_sb(old_dir->i_sb); struct btrfs_trans_handle *trans; @@ -9568,8 +9570,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, } if (flags & RENAME_WHITEOUT) { - ret = btrfs_whiteout_for_rename(trans, root, old_dir, - old_dentry); + ret = btrfs_whiteout_for_rename(trans, root, mnt_userns, + old_dir, old_dentry); if (ret) { btrfs_abort_transaction(trans, ret); @@ -9619,7 +9621,8 @@ static int btrfs_rename2(struct user_namespace *mnt_userns, struct inode *old_di return btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); - return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); + return btrfs_rename(mnt_userns, old_dir, old_dentry, new_dir, + new_dentry, flags); } struct btrfs_delalloc_work { From patchwork Tue Jul 13 11:13:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373707 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 47ECAC11F66 for ; Tue, 13 Jul 2021 11:15:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 322A86128C for ; Tue, 13 Jul 2021 11:15:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235925AbhGMLSq (ORCPT ); Tue, 13 Jul 2021 07:18:46 -0400 Received: from mail.kernel.org ([198.145.29.99]:47026 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235881AbhGMLSq (ORCPT ); Tue, 13 Jul 2021 07:18:46 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7185261284; Tue, 13 Jul 2021 11:15:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174956; bh=EuWHHVhQpz/8kN4i1Eoh23f1Icsz4lC46RL9K9vSwPo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cyYvnwcGHsOuHJAjX4boHGZ457QR3HnGJ75+ZsEQrnZMXP/id9JJ9zIzipHT3Ni6E EWjZ7qBbAH+aUxdeK09nfSrNrODyBzFrKfvRM4eRYdAFTPyNjGqoIdYinc9Fx3P1FT yLbxGB+vwTlQvtYWaTyi6icUofDfqDU0bgkMH9yOzuGHuNd9rRIJ5ZlLit2xQ4o8QW Nzpz+ef/gbREbzQlizzKYAznSHesnKYvDtFVakeekJLx0xPUssOyxAjCQbkNQV/aDU XAm/nmHCrz7M0e6kzaBxXZ6dzxm4cA38fbO0YLTZJu+W4OqlpuxbDZld1pwmdCuSBF IPR3mLuMgJv3w== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 07/24] btrfs/inode: allow idmapped getattr iop Date: Tue, 13 Jul 2021 13:13:27 +0200 Message-Id: <20210713111344.1149376-8-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=920; h=from:subject; bh=AHCYizuH9bI3Zjt3oYc7YEGdTu64DtmP6yYi9l33a40=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY30fnvzfF6+5s5/8a9nOIrv+Mr0YENm9CLLjSlc6eX/ f13o6yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZjI5O8M/8xTAlbuzBNmUmi4l7D+jM A2poTrS6/nfl94xl5+wo3iizyMDEtv7b2+Nsc9XSo0uOnLxAC/J1WHP+eW6t01S68/xHDOkxEA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_getattr() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c0c386cf8a2e..4d8cfc10ffd0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9106,7 +9106,7 @@ static int btrfs_getattr(struct user_namespace *mnt_userns, STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); - generic_fillattr(&init_user_ns, inode, stat); + generic_fillattr(mnt_userns, inode, stat); stat->dev = BTRFS_I(inode)->root->anon_dev; spin_lock(&BTRFS_I(inode)->lock); From patchwork Tue Jul 13 11:13:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373709 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B935AC11F66 for ; Tue, 13 Jul 2021 11:16:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A6B9F6127C for ; Tue, 13 Jul 2021 11:16:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235931AbhGMLSw (ORCPT ); Tue, 13 Jul 2021 07:18:52 -0400 Received: from mail.kernel.org ([198.145.29.99]:47056 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235835AbhGMLSv (ORCPT ); Tue, 13 Jul 2021 07:18:51 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 797DC61164; Tue, 13 Jul 2021 11:15:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174962; bh=VUdQVTm26WiGnoVun2g1DOkKZS40zDCEmxEtD2MOx0A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IM/+DxpMuUJvfwdTFUYeuUsHDqO+6ZaA7DPjeB7f+AG7/0qbEQZ7897Ptyj26OO8X lPCdgVkJNJNchut3KXYp9618PLVBwOY83ae1PURNKocFufUGCJ5r5+kJq1bWeJmqi/ InhTC8i/gLn4IAegsXMUVEqPBCFzLYMRo5XnJxN/YcK2dEcNtRi6HwhwT92p3RS0Wk S4KY8HQmZpdvgdZ4rR1zN4MA4AiqxrF7kF3gO/WIOF8k7dzvn8BCEAi5oRUKnhSVaF pdw5aWoFjI9cnWdyxjDMZXxlrLqhejc03hiCuX4Z/alo+tdhjhmkXOeAVMsvextr2k G1pXEPlWriNkw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 08/24] btrfs/inode: allow idmapped mknod iop Date: Tue, 13 Jul 2021 13:13:28 +0200 Message-Id: <20210713111344.1149376-9-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=969; h=from:subject; bh=+EwkFPpYeKnXwij6N6mXNqdIdWglyubNpLK7STf44c8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3co379pvGyS5sufbMubuiZe/+l96c6xtp1AUImk7w/ e2U6dpSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEwk4DnDP2tmLeG7x2cnC782jK9Z8H XBlmk3LD9p2E3oeS4bxDVf9BcjQ7t4uAe7Tuyc2Umf7v7QcpYRXWZ5pj/fKojt9g593tuF/AA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_mknod() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4d8cfc10ffd0..979869bc76bd 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6576,7 +6576,7 @@ static int btrfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { From patchwork Tue Jul 13 11:13:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373711 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B746CC07E95 for ; Tue, 13 Jul 2021 11:16:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A34656128B for ; Tue, 13 Jul 2021 11:16:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235771AbhGMLTM (ORCPT ); Tue, 13 Jul 2021 07:19:12 -0400 Received: from mail.kernel.org ([198.145.29.99]:47120 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235875AbhGMLTL (ORCPT ); Tue, 13 Jul 2021 07:19:11 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id AFB9E61178; Tue, 13 Jul 2021 11:16:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174982; bh=MLIhPumDu6HfavaPPyljeBF/5Sypk1xI//R6cojm6YY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RTaBD0Gus39X1Q6497agTM/SjTeUNtaV+vt4FWHhePvnnlCZ69U9YUunduniM7pnA h8XWZiQWN7m2Gy5TIykMbKtdbxrg9puPf6Qijyh/a8XI6T6Mzdwfji2svWKW12BNUz DhyX+pnd72v93LZuX18tjl/iD+sYeQvpkJ55pAtKc0WmfHp+PP+WK13RZVR8Dbyfq2 6i3oNoCnSqsyogaErGULlpydFIuA3Atd3WP0tCJB+oj7OgGc6PGBrvFooM0r4AwC5T KfljN8j953ybeZ0tBE/u+R5cQKFObM19sqjUkvlVVGexJpERqEVW2BHesTx00a+24G Rk0YNxI09uSng== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 09/24] btrfs/inode: allow idmapped create iop Date: Tue, 13 Jul 2021 13:13:29 +0200 Message-Id: <20210713111344.1149376-10-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=971; h=from:subject; bh=o4DzlH9QFw3NAVOXB5qDthHpAOFD0Rf35IibVfJFgbM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2M/hazJX/NG23V2Q2G64+5VIT06kwPZ34kZitl7Cic HHqho5SFQYyLQVZMkcWh3SRcbjlPxWajTA2YOaxMIEMYuDgFYCKZ0gz/dDaz+P5tu8ajd7/+89/9nf NdGyrTFN0qFe73+/wr2NjtxvBP9cWht+citCRbu38eTouWefMpaorCas3qFxl7VHJ6RHw4AQ== X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_create() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 979869bc76bd..e21003e8a408 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6640,7 +6640,7 @@ static int btrfs_create(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { From patchwork Tue Jul 13 11:13:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373713 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 059F1C11F67 for ; Tue, 13 Jul 2021 11:16:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E0E9F6128B for ; Tue, 13 Jul 2021 11:16:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235914AbhGMLTQ (ORCPT ); Tue, 13 Jul 2021 07:19:16 -0400 Received: from mail.kernel.org ([198.145.29.99]:47154 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235875AbhGMLTQ (ORCPT ); Tue, 13 Jul 2021 07:19:16 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 377C66023F; Tue, 13 Jul 2021 11:16:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174986; bh=DOtylg3BZ15KVtpI04TjlNkmDDRmeNx1Lm9LkW5LhlE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VzQXfFhZRiGhyJhD/Fsh4gJDNXMf1cKR2e+0ksdHv+6NXAtQ6wM0hOTXKfQK0GLoC nfoyDSqFqtd2z4I62Nn7XMKwcZKVj33ds4XixSn9is1f9P1LszkXQXolrBeBXt/VZw tW8Sy+nLF5dKa9G1er6JwrYNldKou9RpYpHL2bD9hAKpJ/FXtKUqlahrEOuKuZzKZf ILJlUNBUkbKRJ7es2RLpoSkpphhEBNrvoXT4lM4zqx2Y7Ll8sDaUllRL9lEi7ai6oh xGQEpa3Ott7lWe1xQZ5/uyUCbIFC8uLknjCCS4ncKCTJOXlHXvY2zgjuqRmCW4Quhg SIAOFQmKGlt7g== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 10/24] btrfs/inode: allow idmapped mkdir iop Date: Tue, 13 Jul 2021 13:13:30 +0200 Message-Id: <20210713111344.1149376-11-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=958; h=from:subject; bh=PGRv/5X8UUVctjkmNx2b/IITQrP/e2NROk58J20eAnY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3a/3br77fvfzQKfJg0q/ioemiw1fPAXZzLH/I+3Omi 9pbvakcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBEArgZGeYa1Hsfs8s9oSH5aUq6wu mtnk0Xl709WituaKklcy5rJRcjw1/LxFnXYmfzn0q5ViP4c99cnddFm+WbVY6syd2THs35nRkA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_mkdir() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e21003e8a408..5038ab28f688 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6785,7 +6785,7 @@ static int btrfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_fail; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, S_IFDIR | mode, &index); From patchwork Tue Jul 13 11:13:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373715 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5116AC11F66 for ; Tue, 13 Jul 2021 11:16:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 364146128D for ; Tue, 13 Jul 2021 11:16:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235875AbhGMLTU (ORCPT ); Tue, 13 Jul 2021 07:19:20 -0400 Received: from mail.kernel.org ([198.145.29.99]:47188 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235933AbhGMLTT (ORCPT ); Tue, 13 Jul 2021 07:19:19 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1118A6127C; Tue, 13 Jul 2021 11:16:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174989; bh=aGlfbs5KocbR7/3OZMjyoQ/sfpsdzO8gCgcw9kXMDt0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f5reKjnl2lHfq1rdjzBWckRxhcmanu9AQATMGDOzUDUpvZWMHiSnq8eb1lZuobKHJ PmyDhF6o4ti81Xm5kINt/lrZdBGMEbx9G30Wdyeo39VgzCtCddOJVVq7MQXs9YBsj8 ygXkPV90WdrkbiHfEFGMj0mmJS8AgrQeqCg116SrHV37t3ljo2EfHBeTjhDVlDNf5Y Xjy7T3y2oXc+YP+blZN6HLZBbYyR0V4+2pForRz+LHwhPso5Tju1aGy1iFPN5yyEWl GEDpr6mdvjzw3aUWYIczjlzFUiUogE4uPKsl4YmneCfzGVe59ihhqic/2P7wu/SWNk 7Smu57xjf0B5w== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 11/24] btrfs/inode: allow idmapped symlink iop Date: Tue, 13 Jul 2021 13:13:31 +0200 Message-Id: <20210713111344.1149376-12-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=972; h=from:subject; bh=mHAf1CJI7TSQSYXIAqNuV3KwWC8VYy3PPvHsFQU03jM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2Ksnmv/LEwp2jdx59yHu9yjZSWOXO4Rnleyf2XznyT RfJLRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwETy5jIy3JzTmxZXJT4z0tfd7WtB/Z IlPh9VnTOPXTjXeiRIYo7PTYb/Dte7F837UjuDa0rsd7WlZf85+TsLHyZqGpudqPAJmxXGDwA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_symlink() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5038ab28f688..9b87ac971875 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9858,7 +9858,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (err) goto out_unlock; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, dentry->d_name.name, dentry->d_name.len, btrfs_ino(BTRFS_I(dir)), objectid, S_IFLNK | S_IRWXUGO, &index); From patchwork Tue Jul 13 11:13:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373717 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97787C11F66 for ; Tue, 13 Jul 2021 11:16:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 84F6E61288 for ; Tue, 13 Jul 2021 11:16:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235949AbhGMLTX (ORCPT ); Tue, 13 Jul 2021 07:19:23 -0400 Received: from mail.kernel.org ([198.145.29.99]:47224 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235933AbhGMLTW (ORCPT ); Tue, 13 Jul 2021 07:19:22 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5E31A6128B; Tue, 13 Jul 2021 11:16:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174993; bh=kP+Gh543ESQvDnR3D/1zAbz+bWmsKpr4tN1JDFVBRA8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m/6OtF+WpZ0oweMfRpsVUS2Y+Wqux6sP8oY7V+6A3/nbRSd3Z8sR5eY6ViAKNo1SB jZwysWlmgr1YYxzClZKOKg6B9JKuQJwFXlNut2Sc33dllO8S5LoY+tFHP/RtLmbOFF z47UGsrRwFkWT+3/5GyAWXx71LkQeBAbvo+3P3BkSWUTlKc2dLIYLKRDDL6HHv697v K8IGB6XILpPBB1y8PK65f5N6W9cLaG+Jtl0236Bu587qPj9uTO7kmekky/5CUFpNhF PLxy9iqnGyqER+AG1RL/oMO7bd2233YsYNyTnLWkzFPvz1rAOzCPsN5Wf7gYp0IOR1 D4vyR/hE4Kjaw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 12/24] btrfs/inode: allow idmapped tmpfile iop Date: Tue, 13 Jul 2021 13:13:32 +0200 Message-Id: <20210713111344.1149376-13-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=966; h=from:subject; bh=Fz3mRqHZ29Xsn+F984jNBMmMPDvrqIDjK1mR0iD0rzM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3qMStQKlp9cfHns0X2xZ8clvUb5Z5he3fQ9fjLyyYp LWv2d5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAExEjYuRYZmXkfREgaOFFdkzJz2P7d hv4ma467N/zhGZV96Pe1MnTGBkuDHB7PBL8Q9WSxhPP3jVPFHM9Why63vbBT7Ka5YltczPZQcA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_tmpfile() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 9b87ac971875..a51545b351c8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10210,7 +10210,7 @@ static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, if (ret) goto out; - inode = btrfs_new_inode(trans, root, &init_user_ns, dir, NULL, 0, + inode = btrfs_new_inode(trans, root, mnt_userns, dir, NULL, 0, btrfs_ino(BTRFS_I(dir)), objectid, mode, &index); if (IS_ERR(inode)) { ret = PTR_ERR(inode); From patchwork Tue Jul 13 11:13:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373719 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F7ABC07E96 for ; Tue, 13 Jul 2021 11:16:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 69D5C6128D for ; Tue, 13 Jul 2021 11:16:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235956AbhGMLT1 (ORCPT ); Tue, 13 Jul 2021 07:19:27 -0400 Received: from mail.kernel.org ([198.145.29.99]:47258 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235722AbhGMLT0 (ORCPT ); Tue, 13 Jul 2021 07:19:26 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E53DA6127C; Tue, 13 Jul 2021 11:16:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174996; bh=SB6h6B2E7YmJiJAEvUn2tP87f7ZDJwsJaTNT4YRLlTk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m4XLbSK2HgzXHZJfKwGRWamX1o3CqaHlBK9+/FTzCCXC2KZ8cBuBOTZXiGD8e/4ZM yP8IIaF8aSssdlH08FNOq5NYW8BDee4VCWkTI0VPBHkkxv/cLSIqB2nZc/HfQ1yp1a E3wvgS0yzL0hb9NWJnbZmmLP9L0ZHdcOS8fRjFuzBSB9Ie9KjwJXvhrSq0KHe+FvrX tKDM4EHo9/VyElPx1nGfGzu6vaNm4UlArdKT9j/HwtZflhJTj70Ry6fBIcodX0QBVR 8VgOq+3X//lqP1pF9XXyx5hzTXyR7Fbef7Xr+VLsN24lwG/h2cU45jE6btM9FUJDfk sM5BYMkywkviQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 13/24] btrfs/inode: allow idmapped setattr iop Date: Tue, 13 Jul 2021 13:13:33 +0200 Message-Id: <20210713111344.1149376-14-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1371; h=from:subject; bh=dpUPs8T/xVYhX0/N2bAdockNjc1gy/w4bB/6qMaovT4=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY1KaEx+veY1g3TY1pSrGwIdeiKW/3N7eiRJZk0j52bf 1csWd5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyE6z4jwxQNllfPdv+pnSi89OXdrT /lnDL8HJuuzDm6bJtyntqqUjFGhgW+PAeOCC2fvvTrzzcBO2rM5WK/yygllz2Onnv8iP71I3wA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_setattr() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a51545b351c8..8a80ef810703 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5253,7 +5253,7 @@ static int btrfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentr if (btrfs_root_readonly(root)) return -EROFS; - err = setattr_prepare(&init_user_ns, dentry, attr); + err = setattr_prepare(mnt_userns, dentry, attr); if (err) return err; @@ -5264,12 +5264,12 @@ static int btrfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentr } if (attr->ia_valid) { - setattr_copy(&init_user_ns, inode, attr); + setattr_copy(mnt_userns, inode, attr); inode_inc_iversion(inode); err = btrfs_dirty_inode(inode); if (!err && attr->ia_valid & ATTR_MODE) - err = posix_acl_chmod(&init_user_ns, inode, + err = posix_acl_chmod(mnt_userns, inode, inode->i_mode); } From patchwork Tue Jul 13 11:13:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373721 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C78BBC11F66 for ; Tue, 13 Jul 2021 11:16:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B50106127C for ; Tue, 13 Jul 2021 11:16:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235963AbhGMLTa (ORCPT ); Tue, 13 Jul 2021 07:19:30 -0400 Received: from mail.kernel.org ([198.145.29.99]:47298 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235722AbhGMLT3 (ORCPT ); Tue, 13 Jul 2021 07:19:29 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2E95461164; Tue, 13 Jul 2021 11:16:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626174999; bh=Ypx8lR5RjOf76of34VRfY1iBSYOZtZAOJSsDLHWgBTo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eX6U5Qb0PphrvtG1cLRfUbprM3JggVAI93mo39RtxtVng7P5F5cGhqbJ9n/gbfG0O 6Q8xS3Y5yTcgEbBbNjsHuQ8pNA1ysdhEmolwlTdokkcKeUajG5hnWfZLZS4hbLMc99 2eXOjdFocNKbJroZjM10nL8gzcITMna6S3ZzvrK1dge2E0WvD//5oj2qSDkvpCIJHt W0YhqxaBU0kIadKUQgPARsmipKDmrgByZvs999/D7nu4OxgRo0+Kb4mamWPL7wKSiA OqOUTzKxTN0+/n5W9zEYeb9wNPMmEpgZHt7LV2QrXqd/QVYyfFkkNFLVpxw+5VKDTP NU1+Mx3cBF1Lw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 14/24] btrfs/inode: allow idmapped permission iop Date: Tue, 13 Jul 2021 13:13:34 +0200 Message-Id: <20210713111344.1149376-15-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=965; h=from:subject; bh=HOMgk9xSp2+unZe8XN3SFO5+XS+rZFI85aLkGhjJ16E=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY1+O2/1Rom3ot6pzxXnWwStem+tUVZy55JwgozKfr8z 1/7pdpSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzkyxJGhvmfJ6ixLDz9+v35Pxr7nn ffn9De4TPnkOVptpnqLGd2JrEw/C9Manmffbfx0N9ZuRmsObLchQplS785m/Vu37BrUue0JG4A X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Enable btrfs_permission() to handle idmapped mounts. This is just a matter of passing down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8a80ef810703..3b537d90172e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10185,7 +10185,7 @@ static int btrfs_permission(struct user_namespace *mnt_userns, if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) return -EACCES; } - return generic_permission(&init_user_ns, inode, mask); + return generic_permission(mnt_userns, inode, mask); } static int btrfs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, From patchwork Tue Jul 13 11:13:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373723 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B5D97C11F6A for ; Tue, 13 Jul 2021 11:16:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9D7A66128E for ; Tue, 13 Jul 2021 11:16:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235993AbhGMLTd (ORCPT ); Tue, 13 Jul 2021 07:19:33 -0400 Received: from mail.kernel.org ([198.145.29.99]:47332 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235967AbhGMLTc (ORCPT ); Tue, 13 Jul 2021 07:19:32 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 29F446023F; Tue, 13 Jul 2021 11:16:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175002; bh=LqQWbaMFnk7v2oD022KqzI2BGEA0CLtc3MBEjuZtkBQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SUVIvC3+su3VXz/kmolI0/47h+kWXlYlEi/xvZOsvYSfw2ugTzuYe62XehEQMgEhv oJJ6n1W9wct0WPJg8uAh2cHT4FHMB8Jb3gwgK5KGtfCOgB0Hyc9oI9bI+LIUeoMbGQ MuOZcmpCFDZ048inNmNGsEnM1737exQg8jdZuZE1M1D2EaNAa2DAsd7UfAMSlJYHkq 5ds37y5RMhhLgcW42VTb+G1KHZapv5QIUsSRROX2jiEGb6mO2sDJo3zerbGy2LOx+E tGDYA2bfcLtoUvE+PHgkuYANzeRrnnz/mxi84EbmooZ7OJKsyRnuPD0lR9LbN5aXaP pJxuaqi5ZT4tg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 15/24] btrfs/ioctl: check whether fs{g,u}id are mapped during subvolume creation Date: Tue, 13 Jul 2021 13:13:35 +0200 Message-Id: <20210713111344.1149376-16-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1613; h=from:subject; bh=sv03z0zOZb92w1232TW8Rxao9nQV9t8jRDp53bmIzPU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2W7Oie/kRXp1hx9n325hUWbm7Gl1cXeTJlRakcuq/F F/yyo5SFQYyLQVZMkcWh3SRcbjlPxWajTA2YOaxMIEMYuDgFYCJP+Rn+J+wt/jhzw5yePwckM/lei8 +vXsjlynGxK3Tf8U3W3Ak3MhkZloWtX/Hpg8cGh2377E3MvYSEWP5OPamSU3Gvcu+z2y/+cgAA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner When a new subvolume is created btrfs currently doesn't check whether the fs{g,u}id of the caller actually have a mapping in the user namespace attached to the filesystem. The vfs always checks this to make sure that the caller's fs{g,u}id can be represented on-disk. This is most relevant for filesystems that can be mounted inside user namespaces but it is in general a good hardening measure to prevent unrepresentable {g,u}ids from being written to disk. Since we want to support idmapped mounts for btrfs ioctls to create subvolumes in follow-up patches this becomes important since we want to make sure the fs{g,u}id of the caller as mapped according to the idmapped mount can be represented on-disk. Simply add the missing fsuidgid_has_mapping() line from the vfs may_create() version to btrfs_may_create(). Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 8ec67e52fde3..f332de258058 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -870,6 +870,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; + if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + return -EOVERFLOW; return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); } From patchwork Tue Jul 13 11:13:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373725 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C2AD8C07E96 for ; Tue, 13 Jul 2021 11:16:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A585061288 for ; Tue, 13 Jul 2021 11:16:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236002AbhGMLTg (ORCPT ); Tue, 13 Jul 2021 07:19:36 -0400 Received: from mail.kernel.org ([198.145.29.99]:47438 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235893AbhGMLTg (ORCPT ); Tue, 13 Jul 2021 07:19:36 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 23C4B61164; Tue, 13 Jul 2021 11:16:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175006; bh=oLFPloEMpeQx+9oI6PlArWfyY4u+9iY5nMd33OW8ENk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nMbIk3xwb46PEf7rFXSA4tqNucMCEihup7TB9TCM8vjhXz5wXgEouGjaHCScZIdiw Wu0F0goeHg1la6UWO8Ib0us5f8hNFKkCAusSoeSA0BTFbWSt+X2xD6PhLvyFBIuwcZ 7htXNPgD7V2BX46b+G1mh6aa9wUHosZqLfS7YBgYpJRKwITVgESEOQA3hUZY0F3Hqy IpxbMCZKcEMh4SZHNXg2sdL53UXiZCmjitvYdU+3zxOAGJA3IwlWU8C43RRu13SYKo GyEv4NbrR6hRYijBEzttAvl0Et5rpcIz0aet0AvclrPd37EMmLT6mvCWWjKjwTMXIw CVD+JtqF7bzcw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 16/24] btrfs/inode: allow idmapped BTRFS_IOC_{SNAP,SUBVOL}_CREATE{_V2} ioctl Date: Tue, 13 Jul 2021 13:13:36 +0200 Message-Id: <20210713111344.1149376-17-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8137; h=from:subject; bh=9gp6kXRMxPi3RUK2iOaJ0WQKRxt8MHD9PRxqAG6s7/8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2e7xr0fFHARCtB3buPVAXFbXJ+hd5V/b/3WbThjVhR F7vUjlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgInwlTIyfKtgMTVauKWgYmr+zkTVP0 lmBWn93xrK70x5y+WY1nRahZGh+ZLSnY5HPzfbbvJdLnxa5fbyVMVP8tPij+Q2VHEnHFBgAAA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Creating subvolumes and snapshots is one of the core features of btrfs and is even available to unprivileged users. Make it possible to use subvolume and snapshot creation on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ctree.h | 3 ++- fs/btrfs/inode.c | 5 +++-- fs/btrfs/ioctl.c | 47 +++++++++++++++++++++++++++-------------------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e5e53e592d4f..ee1876571b3f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3145,7 +3145,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, struct extent_state **cached_state); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root); + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns); void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state, unsigned *bits); void btrfs_clear_delalloc_extent(struct inode *inode, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3b537d90172e..7cfa1c5a1534 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8851,7 +8851,8 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) */ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root) + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns) { struct inode *inode; int err; @@ -8862,7 +8863,7 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, if (err < 0) return err; - inode = btrfs_new_inode(trans, new_root, &init_user_ns, NULL, "..", 2, + inode = btrfs_new_inode(trans, new_root, mnt_userns, NULL, "..", 2, ino, ino, S_IFDIR | (~current_umask() & S_IRWXUGO), &index); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f332de258058..31115083f382 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -492,8 +492,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid) return 1; } -static noinline int create_subvol(struct inode *dir, - struct dentry *dentry, +static noinline int create_subvol(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry, const char *name, int namelen, struct btrfs_qgroup_inherit *inherit) { @@ -638,7 +638,7 @@ static noinline int create_subvol(struct inode *dir, goto fail; } - ret = btrfs_create_subvol_root(trans, new_root, root); + ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns); btrfs_put_root(new_root); if (ret) { /* We potentially lose an unused inode item here */ @@ -864,15 +864,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) } /* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct inode *dir, struct dentry *child) +static inline int btrfs_may_create(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *child) { if (d_really_is_positive(child)) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) return -EOVERFLOW; - return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); } /* @@ -881,6 +882,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) * inside this filesystem so it's quite a bit simpler. */ static noinline int btrfs_mksubvol(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *snap_src, bool readonly, @@ -895,12 +897,12 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(&init_user_ns, name, parent->dentry, namelen); + dentry = lookup_one_len(mnt_userns, name, parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; - error = btrfs_may_create(dir, dentry); + error = btrfs_may_create(mnt_userns, dir, dentry); if (error) goto out_dput; @@ -922,7 +924,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (snap_src) error = create_snapshot(snap_src, dir, dentry, readonly, inherit); else - error = create_subvol(dir, dentry, name, namelen, inherit); + error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit); if (!error) fsnotify_mkdir(dir, dentry); @@ -936,6 +938,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, } static noinline int btrfs_mksnapshot(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *root, bool readonly, @@ -965,7 +968,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent, btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); - ret = btrfs_mksubvol(parent, name, namelen, + ret = btrfs_mksubvol(parent, mnt_userns, name, namelen, root, readonly, inherit); out: if (snapshot_force_cow) @@ -1794,6 +1797,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, } static noinline int __btrfs_ioctl_snap_create(struct file *file, + struct user_namespace *mnt_userns, const char *name, unsigned long fd, int subvol, bool readonly, struct btrfs_qgroup_inherit *inherit) @@ -1821,8 +1825,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, } if (subvol) { - ret = btrfs_mksubvol(&file->f_path, name, namelen, - NULL, readonly, inherit); + ret = btrfs_mksubvol(&file->f_path, mnt_userns, name, + namelen, NULL, readonly, inherit); } else { struct fd src = fdget(fd); struct inode *src_inode; @@ -1836,16 +1840,17 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, "Snapshot src from another FS"); ret = -EXDEV; - } else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { + } else if (!inode_owner_or_capable(mnt_userns, src_inode)) { /* * Subvolume creation is not restricted, but snapshots * are limited to own subvolumes only */ ret = -EPERM; } else { - ret = btrfs_mksnapshot(&file->f_path, name, namelen, - BTRFS_I(src_inode)->root, - readonly, inherit); + ret = btrfs_mksnapshot(&file->f_path, mnt_userns, + name, namelen, + BTRFS_I(src_inode)->root, + readonly, inherit); } fdput(src); } @@ -1869,8 +1874,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, return PTR_ERR(vol_args); vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, false, NULL); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + false, NULL); kfree(vol_args); return ret; @@ -1928,8 +1934,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file, } } - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, readonly, inherit); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + readonly, inherit); if (ret) goto free_inherit; free_inherit: From patchwork Tue Jul 13 11:13:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373727 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 76DD2C11F66 for ; Tue, 13 Jul 2021 11:16:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 627F561288 for ; Tue, 13 Jul 2021 11:16:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236014AbhGMLTk (ORCPT ); Tue, 13 Jul 2021 07:19:40 -0400 Received: from mail.kernel.org ([198.145.29.99]:47474 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236007AbhGMLTj (ORCPT ); Tue, 13 Jul 2021 07:19:39 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id EAEB66023F; Tue, 13 Jul 2021 11:16:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175010; bh=qocQmsJnkDSbWi5OjQ8/N1PrnDUwbWKZRALqNmUZZWw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L2I21ATgDi3rUUhAe/zoT1TPWdEopRiUAcz1Ff1LqgscW9Zt2hkWXNfiykploEj1M Kawb1K9bF1o/GAc9m2hBa6eLTpKDs8zzKXvuETUPjDZLj5PYm9KCrCJJuj3jbXCksW 1P2/f5gxh1dcnOatLRmqQ8JeBpbQbuY8RActmD1XsqARI+p85rkpb5laecyJu3aNvY TXikUNwS7z83sFepDiVtrSId2XYwX7PNjhOhom/2NBMHeQsnzYxMuQjCkl+YpKbGW5 u/uYJpf9nt285rSX6fHdn+mpDBFJsVY8I3R09CL5Tkm76s3OjOLU/XD9Ea7FMh5qxR sy/J9ALFfquOA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 17/24] btrfs/ioctl: allow idmapped BTRFS_IOC_SNAP_DESTROY{_V2} ioctl Date: Tue, 13 Jul 2021 13:13:37 +0200 Message-Id: <20210713111344.1149376-18-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6632; h=from:subject; bh=GQyKvv+ajpx5xbmsj8Uq2YG8bptMKevFTEsMTM2rqcg=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3533VtV9E18ezri8q8lzRczBX7eGFmnPvHny+P3302 5bN+akcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBEVn9g+F9kcFXhAU+9faA7x/lpe2 VnX327vLDR7V2XY1h7zUmG4A0Mf/hEapb6JS35PHNCQpaf15oZjjOnvj/Sy7tBbNX0rDMuPkwA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Destroying subvolumes and snapshots are important features of btrfs. Both operations are available to unprivileged users if the filesystem has been mounted with the "user_subvol_rm_allowed" mount option. Allow subvolume and snapshot deletion on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. In addition to regular subvolume or snapshot deletion by specifying the name of the subvolume or snapshot the BTRFS_IOC_SNAP_DESTROY_V2 ioctl allows the deletion of subvolumes and snapshots via subvolume and snapshot ids when the BTRFS_SUBVOL_SPEC_BY_ID flag is raised. This feature is blocked on idmapped mounts as this allows filesystem wide subvolume deletions and thus can escape the scope of what's exposed under the mount identified by the fd passed with the ioctl. Here is an example where a btrfs subvolume is deleted through a subvolume mount that does not expose the subvolume to be delete but it can still be deleted by using the subvolume id: /* Compile the following program as "delete_by_spec". */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static int rm_subvolume_by_id(int fd, uint64_t subvolid) { struct btrfs_ioctl_vol_args_v2 args = {}; int ret; args.flags = BTRFS_SUBVOL_SPEC_BY_ID; args.subvolid = subvolid; ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY_V2, &args); if (ret < 0) return -1; return 0; } int main(int argc, char *argv[]) { int subvolid = 0; if (argc < 3) exit(1); fprintf(stderr, "Opening %s\n", argv[1]); int fd = open(argv[1], O_CLOEXEC | O_DIRECTORY); if (fd < 0) exit(2); subvolid = atoi(argv[2]); fprintf(stderr, "Deleting subvolume with subvolid %d\n", subvolid); int ret = rm_subvolume_by_id(fd, subvolid); if (ret < 0) exit(3); exit(0); } #include " #include " #include sudo umount /mnt sudo mount ${LOOPDEV} -o subvol=B/C,user_subvol_rm_allowed /mnt ./delete_by_spec /mnt ${SUBVOLID} With idmapped mounts this can potentially be used by users to delete subvolumes/snapshots they would otherwise not have access to as the idmapping would be applied to an inode that is not exposed in the mount of the subvolume. The fact that this is a filesystem wide operation suggests it might be a good idea to expose this under a separate ioctl that clearly indicates this. In essence, the file descriptor passed with the ioctl is merely used to identify the filesystem on which to operate when BTRFS_SUBVOL_SPEC_BY_ID is used. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 31115083f382..dd0fabdbeeeb 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -830,7 +830,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, * nfs_async_unlink(). */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *victim, int isdir) { int error; @@ -840,12 +841,12 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) BUG_ON(d_inode(victim->d_parent) != dir); audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(&init_user_ns, dir, d_inode(victim)) || + if (check_sticky(mnt_userns, dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim))) return -EPERM; @@ -2914,6 +2915,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, struct btrfs_root *dest = NULL; struct btrfs_ioctl_vol_args *vol_args = NULL; struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; + struct user_namespace *mnt_userns = file_mnt_user_ns(file); char *subvol_name, *subvol_name_ptr = NULL; int subvol_namelen; int err = 0; @@ -2941,6 +2943,18 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { + /* + * Deleting by subvolume id can be used to delete + * subvolumes/snapshots anywhere in the filesystem. + * Ensure that users can't abuse idmapped mounts of + * btrfs subvolumes/snapshots to perform operations in + * the whole filesystem. + */ + if (mnt_userns != &init_user_ns) { + err = -EINVAL; + goto out; + } + if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; goto out; @@ -3025,7 +3039,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); if (err == -EINTR) goto free_subvol_name; - dentry = lookup_one_len(&init_user_ns, subvol_name, parent, subvol_namelen); + dentry = lookup_one_len(mnt_userns, subvol_name, parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; @@ -3067,14 +3081,14 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (root == dest) goto out_dput; - err = inode_permission(&init_user_ns, inode, + err = inode_permission(mnt_userns, inode, MAY_WRITE | MAY_EXEC); if (err) goto out_dput; } /* check if subvolume may be deleted by a user */ - err = btrfs_may_delete(dir, dentry, 1); + err = btrfs_may_delete(mnt_userns, dir, dentry, 1); if (err) goto out_dput; From patchwork Tue Jul 13 11:13:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373729 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BAD2FC11F66 for ; Tue, 13 Jul 2021 11:16:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A42976127C for ; Tue, 13 Jul 2021 11:16:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236025AbhGMLTn (ORCPT ); Tue, 13 Jul 2021 07:19:43 -0400 Received: from mail.kernel.org ([198.145.29.99]:47530 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235920AbhGMLTn (ORCPT ); Tue, 13 Jul 2021 07:19:43 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id A26BD61164; Tue, 13 Jul 2021 11:16:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175013; bh=RvWedfsmMrpM+9NCCB794l2U7Q955yR624q1FcsFZE0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=g+SKauGSqndxS6gPsPnGmiURh8/p9J5mp77sc1Gw3bEEuKC3jPYi85CXpjewQOCQZ rZTg24VvauNvHAoWmpmI0pISJCHjTCmEQvXd6v7a3khlrddGlX62Ix7hx/gzuaNGTR 5isXtWsfrS19tRLqb+XOEQWyg84vlReCjEnjot07vWm0aALYKGFd3eApBuu/THSVXR Dd4FrIl9folvr5DrO1JvTUbOXvYKwFGXfwp7xOblH5MZ0k7HfP0AO9M8nPeIOYOnDT kFF+iPvnYjjQ3RDJHgkYSqgfl9vp/mczzGEzWOpwthi5VKoyOEJN7HfpqpAYTdVMMJ UHAIJivZ51xKA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 18/24] btrfs/ioctl: relax restrictions for BTRFS_IOC_SNAP_DESTROY_V2 with subvolids Date: Tue, 13 Jul 2021 13:13:38 +0200 Message-Id: <20210713111344.1149376-19-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2791; h=from:subject; bh=Xm+yqr5sDrgKlUCWrejT8RAVn07B35vb1JCtXAGZZ7k=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY1ROLRp1pkPAVPuTWUOyhUv4ptgL/aZv8z/o/IkvvBX d/8d7yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZgI/2pGhuv77DR23TVdlXZ8ckdO6c 5533+acd54H/7fjmunrnTxhrsM/11ceTUceblXFj6tvbF7685TerkSLC9WbFq/QTLz5RzNJn4A X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner So far we prevented the deletion of subvolumes and snapshots using subvolume ids possible with the BTRFS_SUBVOL_SPEC_BY_ID flag. This restriction is necessary on idmapped mounts as this allows filesystem wide subvolume and snapshot deletions and thus can escape the scope of what's exposed under the mount identified by the fd passed with the ioctl. Deletion by subvolume id works by looking for an alias of the parent of the subvolume or snapshot to be deleted. The parent alias can be anywhere in the filesystem. However, as long as the alias of the parent that is found is the same as the one identified by the file descriptor passed through the ioctl we can allow the deletion. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index dd0fabdbeeeb..586ec4af9777 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2943,17 +2943,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { - /* - * Deleting by subvolume id can be used to delete - * subvolumes/snapshots anywhere in the filesystem. - * Ensure that users can't abuse idmapped mounts of - * btrfs subvolumes/snapshots to perform operations in - * the whole filesystem. - */ - if (mnt_userns != &init_user_ns) { - err = -EINVAL; - goto out; - } + struct inode *old_dir; if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; @@ -2991,6 +2981,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = PTR_ERR(parent); goto out_drop_write; } + old_dir = dir; dir = d_inode(parent); /* @@ -3001,6 +2992,20 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, */ destroy_parent = true; + /* + * On idmapped mounts, deletion via subvolid is + * restricted to subvolumes that are immediate + * ancestors of the inode referenced by the file + * descriptor in the ioctl. Otherwise the idmapping + * could potentially be abused to delete subvolumes + * anywhere in the filesystem the user wouldn't be able + * to delete without an idmapped mount. + */ + if (old_dir != dir && mnt_userns != &init_user_ns) { + err = -EINVAL; + goto free_parent; + } + subvol_name_ptr = btrfs_get_subvol_name_from_objectid( fs_info, vol_args2->subvolid); if (IS_ERR(subvol_name_ptr)) { From patchwork Tue Jul 13 11:13:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373731 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0CD1FC07E96 for ; Tue, 13 Jul 2021 11:17:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EA54561353 for ; Tue, 13 Jul 2021 11:17:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235920AbhGMLTt (ORCPT ); Tue, 13 Jul 2021 07:19:49 -0400 Received: from mail.kernel.org ([198.145.29.99]:47560 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236007AbhGMLTr (ORCPT ); Tue, 13 Jul 2021 07:19:47 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E8C686023F; Tue, 13 Jul 2021 11:16:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175016; bh=44+NB6f86AP2RfHvDZDkkq75eLi+o1kIJTeLFI6zi5Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mb5KSHGVsPTqszBVdwW8ljqxGVg6eEWs+a/sAH15Jv0BTc1hFuOmddhVM6+XDl3Lm GIW12n6mey88lTptmnp7qJwh6wpqjnY8rgPOWK6GdIAFOup/wUSE5noghb9SQ1F4QZ 3ATYGlCe8DTSW7ECtMSifzF8tjYec0IbjdJt/se+odgK/DyqvSDoV2JJ7negIHbwQh UsKia6zY+xLRNQoDztWGeI3oux0ygbpIkIA6kDiiW1vEUtI8lZTuN745SQXyyxXJgE IMrGlkT+Sa4+Z0tWxG33Zc33UKSkX1BCRom/OBUIWSmJeYy71N6VGbVQZqrXPnSjcC nPfpTWwabGYGw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 19/24] btrfs/ioctl: allow idmapped BTRFS_IOC_SET_RECEIVED_SUBVOL{_32} ioctl Date: Tue, 13 Jul 2021 13:13:39 +0200 Message-Id: <20210713111344.1149376-20-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2101; h=from:subject; bh=wHvbxGMwoEzqcFYI1X1RkTCuRmJPgRfx+cBa4ag/e3A=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3RulSrO3u3XOFGPZNQfYkpafXucw4eT3l9sevlFdc3 93+bdJSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzkySVGhiP/n9VsbPFomrH18eP7jp 0p2xsCDHWaZmfdXeQj8yVU3JuR4Vzf7QWZ1q731/QvVVuo+fu3bq/56xMX01hqrW5yiSXOZwQA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner The BTRFS_IOC_SET_RECEIVED_SUBVOL{_32} are used to set information about a received subvolume. Make it possible to set information about a received subvolume on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 586ec4af9777..9e6c9dfae981 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4464,6 +4464,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info, } static long _btrfs_ioctl_set_received_subvol(struct file *file, + struct user_namespace *mnt_userns, struct btrfs_ioctl_received_subvol_args *sa) { struct inode *inode = file_inode(file); @@ -4475,7 +4476,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, int ret = 0; int received_uuid_changed; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(mnt_userns, inode)) return -EPERM; ret = mnt_want_write_file(file); @@ -4580,7 +4581,7 @@ static long btrfs_ioctl_set_received_subvol_32(struct file *file, args64->rtime.nsec = args32->rtime.nsec; args64->flags = args32->flags; - ret = _btrfs_ioctl_set_received_subvol(file, args64); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), args64); if (ret) goto out; @@ -4614,7 +4615,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, if (IS_ERR(sa)) return PTR_ERR(sa); - ret = _btrfs_ioctl_set_received_subvol(file, sa); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), sa); if (ret) goto out; From patchwork Tue Jul 13 11:13:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373733 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 08264C11F68 for ; Tue, 13 Jul 2021 11:17:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E3B7D6128B for ; Tue, 13 Jul 2021 11:17:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236039AbhGMLTu (ORCPT ); Tue, 13 Jul 2021 07:19:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:47586 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236007AbhGMLTu (ORCPT ); Tue, 13 Jul 2021 07:19:50 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7922961164; Tue, 13 Jul 2021 11:16:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175020; bh=IGcIPBfrU9vTz05WZWN9O7vJpNWnI/2fhYrQ26qOIk4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ClNJxURB26//KYcOScKdcTwOx5gGEJwPbI1Jhgqyq148nbFPduIKPHDt7VKLSsOjB Rnrg4xybOKM7mRbyYME4NsMSTjbmBDonPwb0JjRHKKCutJkemaMngfW9lww0LWSBGT McGm+djCenFsbgVNivUaB8BHbBO3Gbkh9Bj3FQtW9NZmlqwGMZ2/WauAy1u+4MFSGk 3wht3RN5fREWFO2oJ5UsIphD1ikSLtHxh4lFj+JxBYMutEw1xtWzC42u8y0coU2x9Y VXbPSORP3KW9oJnJkHlIsgflbm3GTXvpkZF/Mjw2RciJaFJ7sRmVHR/G8Plwl3pysB d0tji47B0+InQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 20/24] btrfs/ioctl: allow idmapped BTRFS_IOC_SUBVOL_SETFLAGS ioctl Date: Tue, 13 Jul 2021 13:13:40 +0200 Message-Id: <20210713111344.1149376-21-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1244; h=from:subject; bh=irNrT5eadikahzn34PylxHzj5xyGUZNo1PJgzI9Ke6o=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY3Z/qbNMGmKddCSZwfWf9eKShaOeXb0qm98IrNHRJVN iKRKRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwESsExgZ7m3PKRL+zcS/pOrXUt6F7c XPQr9e3c27z8nvhXHucv3XGowMR3R9S96uiy179tkjavK0Va4Hfp3ofiqut/HRzTtu+2OuMwAA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Setting flags on subvolumes or snapshots are core features of btrfs. The BTRFS_IOC_SUBVOL_SETFLAGS ioctl is especially important as it allows to make subvolumes and snapshots read-only or read-write. Allow setting flags on btrfs subvolumes and snapshots on idmapped mounts. This is a fairly straightforward operation since all the permission checking helpers are already capable of handling idmapped mounts. So we just need to pass down the mount's userns. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9e6c9dfae981..8c1ca9f05f4f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1981,7 +1981,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, u64 flags; int ret = 0; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(file_mnt_user_ns(file), inode)) return -EPERM; ret = mnt_want_write_file(file); From patchwork Tue Jul 13 11:13:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373735 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4A846C11F66 for ; Tue, 13 Jul 2021 11:17:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 369A261178 for ; Tue, 13 Jul 2021 11:17:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236043AbhGMLTy (ORCPT ); Tue, 13 Jul 2021 07:19:54 -0400 Received: from mail.kernel.org ([198.145.29.99]:47634 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235932AbhGMLTx (ORCPT ); Tue, 13 Jul 2021 07:19:53 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id AB24D6023F; Tue, 13 Jul 2021 11:17:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175023; bh=/cn7CXHyFFjdCM6hf0uMa3DF0r9KkC6Av6VBa32UgCM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tF/vv8F9/o6IPtwWG9LmPP8ce7wZobFgP8Pf/fpMbN0oDQXmD13p8RCTcNCKC6wrB Wx2ltCEBxJg1bwWPfLpjB8OIdbNqcUPUurKXmv6O1w2AO18T90ae1hqQX7B46xGDg4 3rFWmcBnihSl2eRB337vOYQsYscvjt5Fehcvooj774+y9zvYJK4+rl814GgYp5ZjPs mL+9n6tmlmuEmmwig/rlWmP2KGUNNGz7iXKjlY/0teFVKdB1QabGBADf9vWOgHUgOc LVEFLq2fGajwp4LJJJmDq4cm9wNzMVDQ4r3qgdtJwzzc/iCb8/z/pc74ftoHi4PIMp 4vjz+zBq2HGaw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 21/24] btrfs/ioctl: allow idmapped BTRFS_IOC_INO_LOOKUP_USER ioctl Date: Tue, 13 Jul 2021 13:13:41 +0200 Message-Id: <20210713111344.1149376-22-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3737; h=from:subject; bh=NuuA7EtYgP3uwobv3xcJsDqZuSmPZC6F7vuwUE3D+Zc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY2NialIn3b/7tI3zXcmrFmRMZm30YPX747uzberdH5t r8h/2lHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRG7sYGc4HVmoIHHy2TnplT+e7pb etn/eE7T5RIP/2R/UEQw25yJ+MDOsyLQy/nTjSq5/2NcU3wqUtXGdzV//BMm+rwLs6Um08jAA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner The BTRFS_IOC_INO_LOOKUP_USER is an unprivileged version of the BTRFS_IOC_INO_LOOKUP ioctl and has the following restrictions. The main difference between the two is that BTRFS_IOC_INO_LOOKUP is filesystem wide operation wheres BTRFS_IOC_INO_LOOKUP_USER is scoped beneath the file descriptor passed with the ioctl. Specifically, BTRFS_IOC_INO_LOOKUP_USER must adhere to the following restrictions: - The caller must be privileged over each inode of each path component for the path they are trying to lookup. - The path for the subvolume the caller is trying to lookup must be reachable from the inode associated with the file descriptor passed with the ioctl. The second condition makes it possible to scope the lookup of the path to the mount identified by the file descriptor passed with the ioctl. This allows us to enable this ioctl on idmapped mounts. Specifically, this is possible because all child subvolumes of a parent subvolume are reachable when the parent subvolume is mounted. So if the user had access to open the parent subvolume or has been given the fd then they can lookup the path if they had access to it provided they were privileged over each path component. Note, the BTRFS_IOC_INO_LOOKUP_USER ioctl allows a user to learn the path and name of a subvolume even though they would otherwise be restricted from doing so via regular vfs-based lookup. So think about a parent subvolume with multiple child subvolumes. Someone could mount he parent subvolume and restrict access to the child subvolumes by overmounting them with empty directories. At this point the user can't traverse the child subvolumes and they can't open files in the child subvolumes. However, they can still learn the path of child subvolumes as long as they have access to the parent subvolume by using the BTRFS_IOC_INO_LOOKUP_USER ioctl. The underlying assumption here is that it's ok that the lookup ioctls can't really take mounts into account other than the original mount the fd belongs to during lookup. Since this assumption is baked into the original BTRFS_IOC_INO_LOOKUP_USER ioctl we can extend it to idmapped mounts. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 8c1ca9f05f4f..f56b4e159099 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2439,7 +2439,8 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, return ret; } -static int btrfs_search_path_in_tree_user(struct inode *inode, +static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns, + struct inode *inode, struct btrfs_ioctl_ino_lookup_user_args *args) { struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; @@ -2537,7 +2538,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode, ret = PTR_ERR(temp_inode); goto out_put; } - ret = inode_permission(&init_user_ns, temp_inode, + ret = inode_permission(mnt_userns, temp_inode, MAY_READ | MAY_EXEC); iput(temp_inode); if (ret) { @@ -2679,7 +2680,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) return -EACCES; } - ret = btrfs_search_path_in_tree_user(inode, args); + ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args); if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) ret = -EFAULT; From patchwork Tue Jul 13 11:13:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373737 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8C022C11F66 for ; Tue, 13 Jul 2021 11:17:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7932861178 for ; Tue, 13 Jul 2021 11:17:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236061AbhGMLT6 (ORCPT ); Tue, 13 Jul 2021 07:19:58 -0400 Received: from mail.kernel.org ([198.145.29.99]:47674 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235874AbhGMLT6 (ORCPT ); Tue, 13 Jul 2021 07:19:58 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6339C61164; Tue, 13 Jul 2021 11:17:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175028; bh=xucsiZLTse/n/hkj2erjU2YUp6WR9DP6biFRcYtaiFw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Blfvjl0bljem/InkziSEcQehbC2ZtqFwa8tXmtMOauUS57LxRwuHkKWsUpaSHyEn2 KVim6s73AoUEv1PflgjnAGug1WL8K6g+ETJgH/oAHLsbNQNt44Ycw02yXZRnMWy3ry 9+Z1zXcQL6HegqTYuRW6SDXeLI68r9xfikaSp/yMz0vBam/kEl58F0SVV0EbWt0rDi xjtJ17/faOVqfYLRLxKLJlkcTULJbgSRLcxtHmoXxISNvKDYwdvtTrrauXjyBRHrQW 3rsr3bZrvArpuH1uKXEQP797BPAHd17Ju1V6i7gNIBGC9ujyHKsHM0D2Z3m7x2phjf AegG47nHGUgfA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 22/24] btrfs/acl: handle idmapped mounts Date: Tue, 13 Jul 2021 13:13:42 +0200 Message-Id: <20210713111344.1149376-23-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2975; h=from:subject; bh=zQsbcE/Ys3WaqckL++eThQdHVcB8JaGOiPlzOTEwyTc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY1d1HWh76XUP6vvFmtVzR8Uztm2cIXmx0KLad+XVTSv WnTtTEcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBE0rwZ/kcHTFCdN8c9MsaL4dLRXX dOr8osXiCwL993V9sUvl+fLk9mZDg15c1BofrJkusO//V7X7TYSmnH5eXalj8ktn6Yc3x9RycTAA== X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Make the btrfs acl code idmapped mount aware. The posix default and posix access acls are the only acls other than some specific xattrs that take dac permissions into account. On an idmapped mount they need to be translated according to the mount's userns. The main change is done to __btrfs_set_acl() which is responsible for translating posix acls to their final on-disk representation. The btrfs_init_acl() helper does not need to take the idmapped mount into account since it is called in the context of file creation operations (mknod, create, mkdir, symlink, tmpfile) and is used for btrfs_init_inode_security() to copy posix default and posix access permissions from the parent directory. These acls need to be inherited unmodified from the parent directory. This is identical to what we do for ext4 and xfs. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/acl.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index d95eb5c8cb37..3ebf415e5211 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -53,7 +53,8 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type) } static int __btrfs_set_acl(struct btrfs_trans_handle *trans, - struct inode *inode, struct posix_acl *acl, int type) + struct user_namespace *mnt_userns, + struct inode *inode, struct posix_acl *acl, int type) { int ret, size = 0; const char *name; @@ -88,7 +89,7 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans, goto out; } - ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); + ret = posix_acl_to_xattr(mnt_userns, acl, value, size); if (ret < 0) goto out; } @@ -114,12 +115,12 @@ int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, umode_t old_mode = inode->i_mode; if (type == ACL_TYPE_ACCESS && acl) { - ret = posix_acl_update_mode(&init_user_ns, inode, + ret = posix_acl_update_mode(mnt_userns, inode, &inode->i_mode, &acl); if (ret) return ret; } - ret = __btrfs_set_acl(NULL, inode, acl, type); + ret = __btrfs_set_acl(NULL, mnt_userns, inode, acl, type); if (ret) inode->i_mode = old_mode; return ret; @@ -140,14 +141,14 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans, return ret; if (default_acl) { - ret = __btrfs_set_acl(trans, inode, default_acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, default_acl, ACL_TYPE_DEFAULT); posix_acl_release(default_acl); } if (acl) { if (!ret) - ret = __btrfs_set_acl(trans, inode, acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, acl, ACL_TYPE_ACCESS); posix_acl_release(acl); } From patchwork Tue Jul 13 11:13:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373739 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 10016C07E95 for ; Tue, 13 Jul 2021 11:17:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F11366128B for ; Tue, 13 Jul 2021 11:17:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236068AbhGMLUE (ORCPT ); Tue, 13 Jul 2021 07:20:04 -0400 Received: from mail.kernel.org ([198.145.29.99]:47754 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235874AbhGMLUE (ORCPT ); Tue, 13 Jul 2021 07:20:04 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2711E6127C; Tue, 13 Jul 2021 11:17:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175034; bh=mewN8GwOBsr6em75my5L++YqP9khb50lzPkGB3nHCUo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hvLVZdSpNQDO0b0LDqiK1ZJf/Hj+hnQQ1/W8YFlEOhHoOyE0pE6FceO+scena5+1i 0u/WKDD5PHst7U3kCtFkG6QJdeZBBbOD00SWtRbxfXK5cM3qW8lG0HvnKsCNd8vz5E icZU01XuDWqeA/zoSr3fwsnGwdas6GOWbCQ0c/JzbdEmQVKytfJUt1th6E10rhife+ F4TzBKXQqBTuMSpHjyWH4VlwMEw+JRYJDJSX4As4aZqqkzZgE6WHIM5lGhfBHohKaU Mt+FZJPAD+civIbn9bE/liQl/vSaBtYYmY/BUu9pPVPXSLepq+JQALYWE3A0i4ewR3 nc5vHj9aaa6eA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH 23/24] btrfs/super: allow idmapped btrfs Date: Tue, 13 Jul 2021 13:13:43 +0200 Message-Id: <20210713111344.1149376-24-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1465; h=from:subject; bh=1Y7u6ks+X4eSZCHhNimeAlM7r25xFG/ef0M9yjEMGzQ=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY1lNGu6Ks7z7Mjaw9ob7aYeUnt04eXRoya5fFMlH5Zq Haly7yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZiI7kmGvwKWtdKaLC2nF96+n9m3f3 sUv0S+58ZctQxTlrwzRz70rGdkmGtYrr/pxBee9bmz7aOSzt+2dXO8uTxts43FEjM7S94JfAA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Now that we converted btrfs internally to account for idmapped mounts allow the creation of idmapped mounts on btrfs by setting the FS_ALLOW_IDMAP flag. We only need to raise this flag on the btrfs_root_fs_type filesystem since btrfs_mount_root() is ultimately responsible for allocating the superblock and is called into from btrfs_mount() associated with btrfs_fs_type. The conversion of the btrfs inode operations was straightforward. Regarding btrfs specific ioctls that perform checks based on inode permissions only those have been allowed that are not filesystem wide operations and hence can be reasonably charged against a specific mount. Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- fs/btrfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d07b18b2b250..5ba21f6b443c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2381,7 +2381,7 @@ static struct file_system_type btrfs_root_fs_type = { .name = "btrfs", .mount = btrfs_mount_root, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("btrfs"); From patchwork Tue Jul 13 11:13:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12373763 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 706C0C11F77 for ; Tue, 13 Jul 2021 11:18:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 337C86023F for ; Tue, 13 Jul 2021 11:18:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236086AbhGMLVF (ORCPT ); Tue, 13 Jul 2021 07:21:05 -0400 Received: from mail.kernel.org ([198.145.29.99]:48066 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235838AbhGMLU7 (ORCPT ); Tue, 13 Jul 2021 07:20:59 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id F3F106023F; Tue, 13 Jul 2021 11:17:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626175089; bh=uSZngAfwn+8N9rDZ0xx4R4dycFDd2+L24fywkwWKXzY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DdhPsOFkIwSdK13wl6UkBteFMv1TEzHq5K//Gq65tqcEeKN8VseY9LPrPlKWtSf/D 35Zy9GhPqOQd4AahDWf64T48pLUcxG+CRDuYED/5vq7guBI4YqNhFRgfS1Gr0jBMwH YxdrTTKxj4dw/lnhTRobAWPECewTm8oeUj6CBFrqfilU3jTj0v02NdjJJ0XUJZDmjd zHNHzB9jG16XMDzuUlgUBmHZ5NXzljoDpRR4OThxN8upsXI76ggrMIC2zJJsBDOiAU 2ZP84wr1mYejOAECmGjLHR9yQasMRRVcNcJaXEjYVE2MFd6okbaJ8lqikj7wflbL2D 1a7fX35TxTMcQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Christian Brauner , Christoph Hellwig , fstests@vger.kernel.org Subject: [PATCH 24/24] btrfs/242: introduce btrfs specific idmapped mounts tests Date: Tue, 13 Jul 2021 13:13:44 +0200 Message-Id: <20210713111344.1149376-25-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210713111344.1149376-1-brauner@kernel.org> References: <20210713111344.1149376-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=124071; h=from:subject; bh=wNYhBJQ30iuK8om89S8QMKDOwTAeGzHQ33sXONcjIYc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8LY0TWL/lh8VfR7UvgsI/WC/NS86dKrDfZdXrydWRPFWb C53CO0pZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACaib8/I0BoTuyhskfDlRZucj84Tfq 2rKxbB4/Xxf6ndMukHL399FGP4n3FKPqiPs2v7Nt7MzN7IB9FPmELFpmhsv3WiOcBpqtpaZgA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner While core vfs functionality that btrfs implements is completely covered by the generic test-suite the btrfs specific ioctls are not. This patch expands the test-suite to cover btrfs specific ioctls that are have required changes to work on idmapped mounts. We deliberately don't use the libbtrfsutil library as we need to know exactly what ioctl's are issued and we need to be in control of all privileges at all times. This test-suite currently tests: - BTRFS_IOC_{SNAP,SUBVOL}_CREATE_V2 - subvolume creation on idmapped mounts where the fsids do have a mapping in the superblock - snapshot creation on idmapped mounts where the fsids do have a mapping in the superblock - subvolume creation on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot creation on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume creation on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot creation on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume creation on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot creation on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - BTRFS_IOC_SNAP_DESTROY_V2 - subvolume deletion on idmapped mounts where the fsids do have a mapping in the superblock - snapshot deletion on idmapped mounts where the fsids do have a mapping in the superblock - subvolume deletion on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot deletion on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - unprivileged subvolume deletion on idmapped mounts where the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - unprivileged snapshot deletion on idmapped mounts where the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - BTRFS_IOC_SUBVOL_SETFLAGS - subvolume flags on idmapped mounts where the fsids do have a mapping in the superblock - snapshot flags on idmapped mounts where the fsids do have a mapping in the superblock - subvolume flags on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot flags on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume flags on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot flags on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume flags on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot flags on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - BTRFS_IOC_INO_LOOKUP_USER - subvolume lookup on idmapped mounts where the fsids do have a mapping in the superblock - subvolume lookup on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume lookup on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume lookup on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: fstests@vger.kernel.org Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- src/idmapped-mounts/idmapped-mounts.c | 4061 +++++++++++++++++++++++-- tests/btrfs/242 | 34 + tests/btrfs/242.out | 2 + 3 files changed, 3903 insertions(+), 194 deletions(-) create mode 100755 tests/btrfs/242 create mode 100644 tests/btrfs/242.out base-commit: 4b1e66c2544b61d55ac0e8d58601bbade31d9f59 diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index f155e0b4..f7ac1c0a 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -91,12 +93,21 @@ const char *t_fstype; /* path of the test device */ const char *t_device; +/* path of the test scratch device */ +const char *t_device_scratch; + /* mountpoint of the test device */ const char *t_mountpoint; +/* mountpoint of the test device */ +const char *t_mountpoint_scratch; + /* fd for @t_mountpoint */ int t_mnt_fd; +/* fd for @t_mountpoint_scratch */ +int t_mnt_scratch_fd; + /* fd for @T_DIR1 */ int t_dir1_fd; @@ -9520,240 +9531,3902 @@ out: return fret; } -static void usage(void) +static int btrfs_delete_subvolume(int parent_fd, const char *name) { - fprintf(stderr, "Description:\n"); - fprintf(stderr, " Run idmapped mount tests\n\n"); + struct btrfs_ioctl_vol_args args = {}; + size_t len; + int ret; - fprintf(stderr, "Arguments:\n"); - fprintf(stderr, "--device Device used in the tests\n"); - fprintf(stderr, "--fstype Filesystem type used in the tests\n"); - fprintf(stderr, "--help Print help\n"); - fprintf(stderr, "--mountpoint Mountpoint of device\n"); - fprintf(stderr, "--supported Test whether idmapped mounts are supported on this filesystem\n"); - fprintf(stderr, "--test-core Run core idmapped mount testsuite\n"); - fprintf(stderr, "--test-fscaps-regression Run fscap regression tests\n"); + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; - _exit(EXIT_SUCCESS); + memcpy(args.name, name, len); + args.name[len] = '\0'; + + ret = ioctl(parent_fd, BTRFS_IOC_SNAP_DESTROY, &args); + if (ret < 0) + return -1; + + return 0; } -static const struct option longopts[] = { - {"device", required_argument, 0, 1}, - {"fstype", required_argument, 0, 2}, - {"mountpoint", required_argument, 0, 3}, - {"supported", no_argument, 0, 4}, - {"help", no_argument, 0, 5}, - {"test-core", no_argument, 0, 6}, - {"test-fscaps-regression", no_argument, 0, 7}, - {"test-nested-userns", no_argument, 0, 8}, - {NULL, 0, 0, 0}, -}; +static int btrfs_delete_subvolume_id(int parent_fd, uint64_t subvolid) +{ + struct btrfs_ioctl_vol_args_v2 args = {}; + int ret; -struct t_idmapped_mounts { - int (*test)(void); - const char *description; -} basic_suite[] = { - { acls, "posix acls on regular mounts", }, - { create_in_userns, "create operations in user namespace", }, - { device_node_in_userns, "device node in user namespace", }, - { expected_uid_gid_idmapped_mounts, "expected ownership on idmapped mounts", }, - { fscaps, "fscaps on regular mounts", }, - { fscaps_idmapped_mounts, "fscaps on idmapped mounts", }, - { fscaps_idmapped_mounts_in_userns, "fscaps on idmapped mounts in user namespace", }, - { fscaps_idmapped_mounts_in_userns_separate_userns, "fscaps on idmapped mounts in user namespace with different id mappings", }, - { fsids_mapped, "mapped fsids", }, - { fsids_unmapped, "unmapped fsids", }, - { hardlink_crossing_mounts, "cross mount hardlink", }, - { hardlink_crossing_idmapped_mounts, "cross idmapped mount hardlink", }, - { hardlink_from_idmapped_mount, "hardlinks from idmapped mounts", }, - { hardlink_from_idmapped_mount_in_userns, "hardlinks from idmapped mounts in user namespace", }, -#ifdef HAVE_LIBURING_H - { io_uring, "io_uring", }, - { io_uring_userns, "io_uring in user namespace", }, - { io_uring_idmapped, "io_uring from idmapped mounts", }, - { io_uring_idmapped_userns, "io_uring from idmapped mounts in user namespace", }, - { io_uring_idmapped_unmapped, "io_uring from idmapped mounts with unmapped ids", }, - { io_uring_idmapped_unmapped_userns, "io_uring from idmapped mounts with unmapped ids in user namespace", }, -#endif - { protected_symlinks, "following protected symlinks on regular mounts", }, - { protected_symlinks_idmapped_mounts, "following protected symlinks on idmapped mounts", }, - { protected_symlinks_idmapped_mounts_in_userns, "following protected symlinks on idmapped mounts in user namespace", }, - { rename_crossing_mounts, "cross mount rename", }, - { rename_crossing_idmapped_mounts, "cross idmapped mount rename", }, - { rename_from_idmapped_mount, "rename from idmapped mounts", }, - { rename_from_idmapped_mount_in_userns, "rename from idmapped mounts in user namespace", }, - { setattr_truncate, "setattr truncate", }, - { setattr_truncate_idmapped, "setattr truncate on idmapped mounts", }, - { setattr_truncate_idmapped_in_userns, "setattr truncate on idmapped mounts in user namespace", }, - { setgid_create, "create operations in directories with setgid bit set", }, - { setgid_create_idmapped, "create operations in directories with setgid bit set on idmapped mounts", }, - { setgid_create_idmapped_in_userns, "create operations in directories with setgid bit set on idmapped mounts in user namespace", }, - { setid_binaries, "setid binaries on regular mounts", }, - { setid_binaries_idmapped_mounts, "setid binaries on idmapped mounts", }, - { setid_binaries_idmapped_mounts_in_userns, "setid binaries on idmapped mounts in user namespace", }, - { setid_binaries_idmapped_mounts_in_userns_separate_userns, "setid binaries on idmapped mounts in user namespace with different id mappings", }, - { sticky_bit_unlink, "sticky bit unlink operations on regular mounts", }, - { sticky_bit_unlink_idmapped_mounts, "sticky bit unlink operations on idmapped mounts", }, - { sticky_bit_unlink_idmapped_mounts_in_userns, "sticky bit unlink operations on idmapped mounts in user namespace", }, - { sticky_bit_rename, "sticky bit rename operations on regular mounts", }, - { sticky_bit_rename_idmapped_mounts, "sticky bit rename operations on idmapped mounts", }, - { sticky_bit_rename_idmapped_mounts_in_userns, "sticky bit rename operations on idmapped mounts in user namespace", }, - { symlink_regular_mounts, "symlink from regular mounts", }, - { symlink_idmapped_mounts, "symlink from idmapped mounts", }, - { symlink_idmapped_mounts_in_userns, "symlink from idmapped mounts in user namespace", }, - { threaded_idmapped_mount_interactions, "threaded operations on idmapped mounts", }, -}; + args.flags = BTRFS_SUBVOL_SPEC_BY_ID; + args.subvolid = subvolid; -struct t_idmapped_mounts fscaps_in_ancestor_userns[] = { - { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns, "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns", }, -}; + ret = ioctl(parent_fd, BTRFS_IOC_SNAP_DESTROY_V2, &args); + if (ret < 0) + return -1; -struct t_idmapped_mounts t_nested_userns[] = { - { nested_userns, "test that nested user namespaces behave correctly when attached to idmapped mounts", }, -}; + return 0; +} -static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size) +static int btrfs_create_subvolume(int parent_fd, const char *name) { - int i; + struct btrfs_ioctl_vol_args_v2 args = {}; + size_t len; + int ret; - for (i = 0; i < suite_size; i++) { - struct t_idmapped_mounts *t = &suite[i]; - int ret; - pid_t pid; + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; - test_setup(); + memcpy(args.name, name, len); + args.name[len] = '\0'; - pid = fork(); - if (pid < 0) - return false; + ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args); + if (ret < 0) + return -1; - if (pid == 0) { - ret = t->test(); - if (ret) - die("failure: %s", t->description); + return 0; +} - exit(EXIT_SUCCESS); - } +static int btrfs_create_snapshot(int fd, int parent_fd, const char *name, + int flags) +{ + struct btrfs_ioctl_vol_args_v2 args = { + .fd = fd, + }; + size_t len; + int ret; - ret = wait_for_pid(pid); - test_cleanup(); + if (flags & ~BTRFS_SUBVOL_RDONLY) + return -EINVAL; - if (ret) - return false; - } + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; + memcpy(args.name, name, len); + args.name[len] = '\0'; - return true; + if (flags & BTRFS_SUBVOL_RDONLY) + args.flags |= BTRFS_SUBVOL_RDONLY; + ret = ioctl(parent_fd, BTRFS_IOC_SNAP_CREATE_V2, &args); + if (ret < 0) + return -1; + + return 0; } -int main(int argc, char *argv[]) +static int btrfs_get_subvolume_ro(int fd, bool *read_only_ret) { - int fret, ret; - int index = 0; - bool supported = false, test_core = false, - test_fscaps_regression = false, test_nested_userns = false; + uint64_t flags; + int ret; - while ((ret = getopt_long(argc, argv, "", longopts, &index)) != -1) { - switch (ret) { - case 1: - t_device = optarg; - break; - case 2: - t_fstype = optarg; - break; - case 3: - t_mountpoint = optarg; - break; - case 4: - supported = true; - break; - case 6: - test_core = true; - break; - case 7: - test_fscaps_regression = true; - break; - case 8: - test_nested_userns = true; - break; - case 5: - /* fallthrough */ - default: - usage(); - } - } + ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); + if (ret < 0) + return -1; - if (!t_device) - die_errno(EINVAL, "test device missing"); + *read_only_ret = flags & BTRFS_SUBVOL_RDONLY; + return 0; +} - if (!t_fstype) - die_errno(EINVAL, "test filesystem type missing"); +static int btrfs_set_subvolume_ro(int fd, bool read_only) +{ + uint64_t flags; + int ret; - if (!t_mountpoint) - die_errno(EINVAL, "mountpoint of test device missing"); + ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); + if (ret < 0) + return -1; - /* create separate mount namespace */ - if (unshare(CLONE_NEWNS)) - die("failure: create new mount namespace"); + if (read_only) + flags |= BTRFS_SUBVOL_RDONLY; + else + flags &= ~BTRFS_SUBVOL_RDONLY; - /* turn off mount propagation */ - if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)) - die("failure: turn mount propagation off"); + ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags); + if (ret < 0) + return -1; - t_mnt_fd = openat(-EBADF, t_mountpoint, O_CLOEXEC | O_DIRECTORY); - if (t_mnt_fd < 0) - die("failed to open %s", t_mountpoint); + return 0; +} - /* - * Caller just wants to know whether the filesystem we're on supports - * idmapped mounts. - */ - if (supported) { - int open_tree_fd = -EBADF; - struct mount_attr attr = { - .attr_set = MOUNT_ATTR_IDMAP, - .userns_fd = -EBADF, - }; +static int btrfs_get_subvolume_id(int fd, uint64_t *id_ret) +{ + struct btrfs_ioctl_ino_lookup_args args = { + .treeid = 0, + .objectid = BTRFS_FIRST_FREE_OBJECTID, + }; + int ret; - /* Changing mount properties on a detached mount. */ - attr.userns_fd = get_userns_fd(0, 1000, 1); - if (attr.userns_fd < 0) - exit(EXIT_FAILURE); + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret < 0) + return -1; - open_tree_fd = sys_open_tree(t_mnt_fd, "", - AT_EMPTY_PATH | - AT_NO_AUTOMOUNT | - AT_SYMLINK_NOFOLLOW | - OPEN_TREE_CLOEXEC | - OPEN_TREE_CLONE); - if (open_tree_fd < 0) - ret = -1; - else - ret = sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)); + *id_ret = args.treeid; - close(open_tree_fd); - close(attr.userns_fd); + return 0; +} - if (ret) - exit(EXIT_FAILURE); +/* + * The following helpers are adapted from the btrfsutils library. We can't use + * the library directly since we need full control over how the subvolume + * iteration happens. We need to be able to check whether unprivileged + * subvolume iteration is possible, i.e. whether BTRFS_IOC_INO_LOOKUP_USER is + * available and also ensure that it is actually used when looking up paths. + */ +struct btrfs_stack { + uint64_t tree_id; + struct btrfs_ioctl_get_subvol_rootref_args rootref_args; + size_t items_pos; + size_t path_len; +}; - exit(EXIT_SUCCESS); +struct btrfs_iter { + int fd; + int cur_fd; + + struct btrfs_stack *search_stack; + size_t stack_len; + size_t stack_capacity; + + char *cur_path; + size_t cur_path_capacity; +}; + +static struct btrfs_stack *top_stack_entry(struct btrfs_iter *iter) +{ + return &iter->search_stack[iter->stack_len - 1]; +} + +static int pop_stack(struct btrfs_iter *iter) +{ + struct btrfs_stack *top, *parent; + int fd, parent_fd; + size_t i; + + if (iter->stack_len == 1) { + iter->stack_len--; + return 0; } - stash_overflowuid(); - stash_overflowgid(); + top = top_stack_entry(iter); + iter->stack_len--; + parent = top_stack_entry(iter); - fret = EXIT_FAILURE; + fd = iter->cur_fd; + for (i = parent->path_len; i < top->path_len; i++) { + if (i == 0 || iter->cur_path[i] == '/') { + parent_fd = openat(fd, "..", O_RDONLY); + if (fd != iter->cur_fd) + close(fd); + if (parent_fd == -1) + return -1; + fd = parent_fd; + } + } + if (iter->cur_fd != iter->fd) + close(iter->cur_fd); + iter->cur_fd = fd; - if (test_core && !run_test(basic_suite, ARRAY_SIZE(basic_suite))) - goto out; + return 0; +} - if (test_fscaps_regression && - !run_test(fscaps_in_ancestor_userns, - ARRAY_SIZE(fscaps_in_ancestor_userns))) - goto out; +static int append_stack(struct btrfs_iter *iter, uint64_t tree_id, size_t path_len) +{ + struct btrfs_stack *entry; - if (test_nested_userns && - !run_test(t_nested_userns, ARRAY_SIZE(t_nested_userns))) + if (iter->stack_len >= iter->stack_capacity) { + size_t new_capacity = iter->stack_capacity * 2; + struct btrfs_stack *new_search_stack; + + new_search_stack = reallocarray(iter->search_stack, new_capacity, + sizeof(*iter->search_stack)); + if (!new_search_stack) + return -ENOMEM; + + iter->stack_capacity = new_capacity; + iter->search_stack = new_search_stack; + } + + entry = &iter->search_stack[iter->stack_len]; + + memset(entry, 0, sizeof(*entry)); + entry->path_len = path_len; + entry->tree_id = tree_id; + + if (iter->stack_len) { + struct btrfs_stack *top; + char *path; + int fd; + + top = top_stack_entry(iter); + path = &iter->cur_path[top->path_len]; + if (*path == '/') + path++; + fd = openat(iter->cur_fd, path, O_RDONLY); + if (fd == -1) + return -errno; + + close(iter->cur_fd); + iter->cur_fd = fd; + } + + iter->stack_len++; + + return 0; +} + +static int btrfs_iterator_start(int fd, uint64_t top, struct btrfs_iter **ret) +{ + struct btrfs_iter *iter; + int err; + + iter = malloc(sizeof(*iter)); + if (!iter) + return -ENOMEM; + + iter->fd = fd; + iter->cur_fd = fd; + + iter->stack_len = 0; + iter->stack_capacity = 4; + iter->search_stack = malloc(sizeof(*iter->search_stack) * + iter->stack_capacity); + if (!iter->search_stack) { + err = -ENOMEM; + goto out_iter; + } + + iter->cur_path_capacity = 256; + iter->cur_path = malloc(iter->cur_path_capacity); + if (!iter->cur_path) { + err = -ENOMEM; + goto out_search_stack; + } + + err = append_stack(iter, top, 0); + if (err) + goto out_cur_path; + + *ret = iter; + + return 0; + +out_cur_path: + free(iter->cur_path); +out_search_stack: + free(iter->search_stack); +out_iter: + free(iter); + return err; +} + +static void btrfs_iterator_end(struct btrfs_iter *iter) +{ + if (iter) { + free(iter->cur_path); + free(iter->search_stack); + if (iter->cur_fd != iter->fd) + close(iter->cur_fd); + close(iter->fd); + free(iter); + } +} + +static int __append_path(struct btrfs_iter *iter, const char *name, + size_t name_len, const char *dir, size_t dir_len, + size_t *path_len_ret) +{ + struct btrfs_stack *top = top_stack_entry(iter); + size_t path_len; + char *p; + + path_len = top->path_len; + /* + * We need a joining slash if we have a current path and a subdirectory. + */ + if (top->path_len && dir_len) + path_len++; + path_len += dir_len; + /* + * We need another joining slash if we have a current path and a name, + * but not if we have a subdirectory, because the lookup ioctl includes + * a trailing slash. + */ + if (top->path_len && !dir_len && name_len) + path_len++; + path_len += name_len; + + /* We need one extra character for the NUL terminator. */ + if (path_len + 1 > iter->cur_path_capacity) { + char *tmp = realloc(iter->cur_path, path_len + 1); + + if (!tmp) + return -ENOMEM; + iter->cur_path = tmp; + iter->cur_path_capacity = path_len + 1; + } + + p = iter->cur_path + top->path_len; + if (top->path_len && dir_len) + *p++ = '/'; + memcpy(p, dir, dir_len); + p += dir_len; + if (top->path_len && !dir_len && name_len) + *p++ = '/'; + memcpy(p, name, name_len); + p += name_len; + *p = '\0'; + + *path_len_ret = path_len; + + return 0; +} + +static int get_subvolume_path(struct btrfs_iter *iter, uint64_t treeid, + uint64_t dirid, size_t *path_len_ret) +{ + struct btrfs_ioctl_ino_lookup_user_args args = { + .treeid = treeid, + .dirid = dirid, + }; + int ret; + + ret = ioctl(iter->cur_fd, BTRFS_IOC_INO_LOOKUP_USER, &args); + if (ret == -1) + return -1; + + return __append_path(iter, args.name, strlen(args.name), args.path, + strlen(args.path), path_len_ret); +} + +static int btrfs_iterator_next(struct btrfs_iter *iter, char **path_ret, + uint64_t *id_ret) +{ + struct btrfs_stack *top; + uint64_t treeid, dirid; + size_t path_len; + int ret, err; + + for (;;) { + for (;;) { + if (iter->stack_len == 0) + return 1; + + top = top_stack_entry(iter); + if (top->items_pos < top->rootref_args.num_items) { + break; + } else { + ret = ioctl(iter->cur_fd, + BTRFS_IOC_GET_SUBVOL_ROOTREF, + &top->rootref_args); + if (ret == -1 && errno != EOVERFLOW) + return -1; + top->items_pos = 0; + + if (top->rootref_args.num_items == 0) { + err = pop_stack(iter); + if (err) + return err; + } + } + } + + treeid = top->rootref_args.rootref[top->items_pos].treeid; + dirid = top->rootref_args.rootref[top->items_pos].dirid; + top->items_pos++; + err = get_subvolume_path(iter, treeid, dirid, &path_len); + if (err) { + /* Skip the subvolume if we can't access it. */ + if (errno == EACCES) + continue; + return err; + } + + err = append_stack(iter, treeid, path_len); + if (err) { + /* + * Skip the subvolume if it does not exist (which can + * happen if there is another filesystem mounted over a + * parent directory) or we don't have permission to + * access it. + */ + if (errno == ENOENT || errno == EACCES) + continue; + return err; + } + + top = top_stack_entry(iter); + goto out; + } + +out: + if (path_ret) { + *path_ret = malloc(top->path_len + 1); + if (!*path_ret) + return -ENOMEM; + memcpy(*path_ret, iter->cur_path, top->path_len); + (*path_ret)[top->path_len] = '\0'; + } + if (id_ret) + *id_ret = top->tree_id; + return 0; +} + +#define BTRFS_SUBVOLUME1 "subvol1" +#define BTRFS_SUBVOLUME1_SNAPSHOT1 "subvol1_snapshot1" +#define BTRFS_SUBVOLUME1_SNAPSHOT1_RO "subvol1_snapshot1_ro" +#define BTRFS_SUBVOLUME1_RENAME "subvol1_rename" +#define BTRFS_SUBVOLUME2 "subvol2" + +static int btrfs_subvolumes_fsids_mapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_up()) + die("failure: raise caps"); + + /* + * The caller's fsids now have mappings in the idmapped mount so + * any file creation must succeed. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: check ownership"); + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: check ownership"); + + if (!caps_down()) + die("failure: lower caps"); + + /* + * The filesystem is not mounted with user_subvol_rm_allowed so + * subvolume deletion must fail. + */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + if (errno != EPERM) + die("failure: errno"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: check ownership"); + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_fsids_mapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* The caller's fsids now have mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: check ownership"); + + /* remove subvolume */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_fsids_unmapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + + /* create directory for rename test */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + /* change ownership of all files to uid 0 */ + if (fchownat(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: fchownat"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + if (!switch_fsids(0, 0)) { + log_stderr("failure: switch_fsids"); + goto out; + } + + /* + * The caller's fsids don't have a mappings in the idmapped mount so + * any file creation must fail. + */ + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (!btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME2)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + if (errno != EOVERFLOW) { + log_stderr("failure: errno"); + goto out; + } + + /* try to rename a subvolume */ + if (!renameat2(open_tree_fd, BTRFS_SUBVOLUME1, open_tree_fd, + BTRFS_SUBVOLUME1_RENAME, 0)) { + log_stderr("failure: renameat2"); + goto out; + } + if (errno != EOVERFLOW) { + log_stderr("failure: errno"); + goto out; + } + + /* The caller is privileged over the inode so file deletion must work. */ + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_fsids_unmapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF, userns_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + /* create directory for rename test */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + /* change ownership of all files to uid 0 */ + if (fchownat(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: fchownat"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + userns_fd = get_userns_fd(0, 30000, 10000); + if (userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_userns(userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + if (!expected_uid_gid(open_tree_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + /* + * The caller's fsids don't have a mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (!btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME2)) + die("failure: btrfs_create_subvolume"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* try to rename a subvolume */ + if (!renameat2(open_tree_fd, BTRFS_SUBVOLUME1, open_tree_fd, + BTRFS_SUBVOLUME1_RENAME, 0)) + die("failure: renameat2"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* + * The caller is not privileged over the inode so subvolume + * deletion must fail. + */ + + /* remove subvolume */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + safe_close(userns_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_mapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_up()) + die("failure: raise caps"); + + /* The caller's fsids now have mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* create read-only snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + safe_close(subvolume_fd); + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + /* remove read-write snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) + die("failure: btrfs_delete_subvolume"); + + /* remove read-only snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) + die("failure: btrfs_delete_subvolume"); + + /* create directory */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* create read-only snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove read-write snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove read-only snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_mapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + /* create read-only snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 0, 0)) + die("failure: expected_uid_gid"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* remove read-write snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* remove read-only snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_unmapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* create directory for rename test */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + /* change ownership of all files to uid 0 */ + if (fchownat(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: fchownat"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, + sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + + if (!switch_fsids(0, 0)) { + log_stderr("failure: switch_fsids"); + goto out; + } + + /* + * The caller's fsids don't have a mappings in the idmapped + * mount so any file creation must fail. + */ + + /* + * The open_tree() syscall returns an O_PATH file descriptor + * which we can't use with ioctl(). So let's reopen it as a + * proper file descriptor. + */ + tree_fd = openat(open_tree_fd, ".", + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) + die("failure: openat"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create directory */ + if (!btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME2)) + die("failure: btrfs_create_subvolume"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* create read-write snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* create read-only snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* try to rename a directory */ + if (!renameat2(open_tree_fd, BTRFS_SUBVOLUME1, open_tree_fd, + BTRFS_SUBVOLUME1_RENAME, 0)) + die("failure: renameat2"); + if (errno != EOVERFLOW) + die("failure: errno"); + + if (!caps_down()) + die("failure: caps_down"); + + /* create read-write snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + if (errno != EPERM) + die("failure: errno"); + + /* create read-only snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + if (errno != EPERM) + die("failure: errno"); + + /* + * The caller is not privileged over the inode so subvolume + * deletion must fail. + */ + + /* remove directory */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + if (errno != EPERM) + die("failure: errno"); + + if (!caps_up()) + die("failure: caps_down"); + + /* + * The caller is privileged over the inode so subvolume + * deletion must work. + */ + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_unmapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, subvolume_fd = -EBADF, tree_fd = -EBADF, + userns_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* create directory for rename test */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + /* change ownership of all files to uid 0 */ + if (fchownat(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: fchownat"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + userns_fd = get_userns_fd(0, 30000, 10000); + if (userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, + sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor + * which we can't use with ioctl(). So let's reopen it as a + * proper file descriptor. + */ + tree_fd = openat(open_tree_fd, ".", + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_userns(userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + if (!expected_uid_gid(open_tree_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + /* + * The caller's fsids don't have a mappings in the idmapped + * mount so any file creation must fail. + */ + + /* create directory */ + if (!btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME2)) + die("failure: btrfs_create_subvolume"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* create read-write snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + if (errno != EPERM) + die("failure: errno"); + + /* create read-only snapshot */ + if (!btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + if (errno != EPERM) + die("failure: errno"); + + /* try to rename a directory */ + if (!renameat2(open_tree_fd, BTRFS_SUBVOLUME1, open_tree_fd, + BTRFS_SUBVOLUME1_RENAME, 0)) + die("failure: renameat2"); + if (errno != EOVERFLOW) + die("failure: errno"); + + /* + * The caller is not privileged over the inode so subvolume + * deletion must fail. + */ + + /* remove directory */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + if (errno != EPERM) + die("failure: errno"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(subvolume_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_mnt_scratch_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* + * The caller's fsids now have mappings in the idmapped mount so + * any file creation must succedd. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: check ownership"); + + /* + * The scratch device is mounted with user_subvol_rm_allowed so + * subvolume deletion must succeed. + */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_fsids_mapped_userns_user_subvol_rm_allowed(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_mnt_scratch_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* The caller's fsids now have mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: check ownership"); + + /* + * The scratch device is mounted with user_subvol_rm_allowed so + * subvolume deletion must succeed. + */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_mapped_user_subvol_rm_allowed(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_mnt_scratch_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* + * The caller's fsids now have mappings in the idmapped mount so + * any file creation must succeed. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + /* create read-only snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + safe_close(subvolume_fd); + + /* remove subvolume */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + /* remove read-write snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) + die("failure: btrfs_delete_subvolume"); + + /* remove read-only snapshot */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) + die("failure: btrfs_delete_subvolume"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (btrfs_set_subvolume_ro(subvolume_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + safe_close(subvolume_fd); + + /* remove read-only snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_fsids_mapped_userns_user_subvol_rm_allowed(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_mnt_scratch_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + /* create read-only snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + BTRFS_SUBVOL_RDONLY)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 0, 0)) + die("failure: expected_uid_gid"); + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_delete_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + /* remove read-write snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) + die("failure: btrfs_delete_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, 0, 0, 0)) + die("failure: expected_uid_gid"); + + /* remove read-only snapshot */ + if (!btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) + die("failure: btrfs_delete_subvolume"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (btrfs_set_subvolume_ro(subvolume_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + safe_close(subvolume_fd); + + /* remove read-only snapshot */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1_RO)) + die("failure: btrfs_delete_subvolume"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_delete_by_spec_id(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, subvolume_fd = -EBADF, tree_fd = -EBADF; + uint64_t subvolume_id1 = -EINVAL, subvolume_id2 = -EINVAL; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_mnt_scratch_fd, "A")) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_mnt_scratch_fd, "B")) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + subvolume_fd = openat(t_mnt_scratch_fd, "B", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(subvolume_fd, "C")) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + safe_close(subvolume_fd); + + subvolume_fd = openat(t_mnt_scratch_fd, "A", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fd, &subvolume_id1)) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + subvolume_fd = openat(t_mnt_scratch_fd, "B/C", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fd, &subvolume_id2)) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + if (sys_mount(t_device_scratch, t_mountpoint, "btrfs", 0, "subvol=B/C")) { + log_stderr("failure: mount"); + goto out; + } + + open_tree_fd = sys_open_tree(-EBADF, t_mountpoint, + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* + * The subvolume isn't exposed in the idmapped mount so + * delation via spec id must fail. + */ + if (!btrfs_delete_subvolume_id(tree_fd, subvolume_id1)) + die("failure: btrfs_delete_subvolume_id"); + if (errno != EINVAL) + die("failure: errno"); + + if (btrfs_delete_subvolume_id(t_mnt_scratch_fd, subvolume_id1)) + die("failure: btrfs_delete_subvolume_id"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + sys_umount2(t_mountpoint, MNT_DETACH); + btrfs_delete_subvolume_id(t_mnt_scratch_fd, subvolume_id2); + btrfs_delete_subvolume(t_mnt_scratch_fd, "B"); + + return fret; +} + +static int btrfs_subvolumes_setflags_fsids_mapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + bool read_only = false; + + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* The caller's fsids now have mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (btrfs_set_subvolume_ro(subvolume_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (!read_only) + die("failure: not read_only"); + + if (btrfs_set_subvolume_ro(subvolume_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_setflags_fsids_mapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + bool read_only = false; + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* The caller's fsids now have mappings in the idmapped mount so + * any file creation must fail. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (btrfs_set_subvolume_ro(subvolume_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (!read_only) + die("failure: not read_only"); + + if (btrfs_set_subvolume_ro(subvolume_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_setflags_fsids_unmapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + bool read_only = false; + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (!switch_fsids(0, 0)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* + * The caller's fsids don't have mappings in the idmapped mount + * so any file creation must fail. + */ + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (!btrfs_set_subvolume_ro(subvolume_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + if (errno != EPERM) + die("failure: errno"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_subvolumes_setflags_fsids_unmapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF, userns_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + userns_fd = get_userns_fd(0, 30000, 10000); + if (userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int subvolume_fd = -EBADF; + bool read_only = false; + + /* + * The caller's fsids don't have mappings in the idmapped mount + * so any file creation must fail. + */ + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + if (!switch_userns(userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + if (!expected_uid_gid(open_tree_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + if (btrfs_get_subvolume_ro(subvolume_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (!btrfs_set_subvolume_ro(subvolume_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + if (errno != EPERM) + die("failure: errno"); + + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + safe_close(userns_fd); + + return fret; +} + +static int btrfs_snapshots_setflags_fsids_mapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int snapshot_fd = -EBADF, subvolume_fd = -EBADF; + bool read_only = false; + + if (!switch_fsids(10000, 10000)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* + * The caller's fsids now have mappings in the idmapped mount + * so any file creation must succeed. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) + die("failure: expected_uid_gid"); + + snapshot_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (snapshot_fd < 0) + die("failure: openat"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (btrfs_set_subvolume_ro(snapshot_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (!read_only) + die("failure: not read_only"); + + if (btrfs_set_subvolume_ro(snapshot_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + safe_close(snapshot_fd); + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_setflags_fsids_mapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int snapshot_fd = -EBADF, subvolume_fd = -EBADF; + bool read_only = false; + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + /* + * The caller's fsids now have mappings in the idmapped mount so + * any file creation must succeed. + */ + + /* create subvolume */ + if (btrfs_create_subvolume(tree_fd, BTRFS_SUBVOLUME1)) + die("failure: btrfs_create_subvolume"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + subvolume_fd = openat(tree_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) + die("failure: openat"); + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, tree_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) + die("failure: btrfs_create_snapshot"); + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) + die("failure: expected_uid_gid"); + + snapshot_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (snapshot_fd < 0) + die("failure: openat"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (btrfs_set_subvolume_ro(snapshot_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (!read_only) + die("failure: not read_only"); + + if (btrfs_set_subvolume_ro(snapshot_fd, false)) + die("failure: btrfs_set_subvolume_ro"); + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + safe_close(snapshot_fd); + safe_close(subvolume_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_setflags_fsids_unmapped(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, subvolume_fd = -EBADF, tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + subvolume_fd = openat(t_dir1_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, t_dir1_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) { + log_stderr("failure: btrfs_create_snapshot"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int snapshot_fd = -EBADF; + bool read_only = false; + + snapshot_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (snapshot_fd < 0) + die("failure: openat"); + + if (!switch_fsids(0, 0)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: raise caps"); + + /* + * The caller's fsids don't have mappings in the idmapped mount + * so any file creation must fail. + */ + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (!btrfs_set_subvolume_ro(snapshot_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + if (errno != EPERM) + die("failure: errno"); + + safe_close(snapshot_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(subvolume_fd); + safe_close(tree_fd); + + return fret; +} + +static int btrfs_snapshots_setflags_fsids_unmapped_userns(void) +{ + int fret = -1; + int open_tree_fd = -EBADF, subvolume_fd = -EBADF, tree_fd = -EBADF, + userns_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + + if (!caps_supported()) + return 0; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + userns_fd = get_userns_fd(0, 30000, 10000); + if (userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(t_dir1_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + subvolume_fd = openat(t_dir1_fd, BTRFS_SUBVOLUME1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (subvolume_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create read-write snapshot */ + if (btrfs_create_snapshot(subvolume_fd, t_dir1_fd, + BTRFS_SUBVOLUME1_SNAPSHOT1, 0)) { + log_stderr("failure: btrfs_create_snapshot"); + goto out; + } + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 0, 0)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + if (!expected_uid_gid(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, 0, 10000, 10000)) { + log_stderr("failure: expected_uid_gid"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + int snapshot_fd = -EBADF; + bool read_only = false; + + /* + * The caller's fsids don't have mappings in the idmapped mount + * so any file creation must fail. + */ + + snapshot_fd = openat(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1, + O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (snapshot_fd < 0) + die("failure: openat"); + + + if (!switch_userns(userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!expected_uid_gid(t_dir1_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + if (!expected_uid_gid(open_tree_fd, BTRFS_SUBVOLUME1, 0, + t_overflowuid, t_overflowgid)) + die("failure: expected_uid_gid"); + + /* + * The caller's fsids don't have mappings in the idmapped mount + * so any file creation must fail. + */ + + if (btrfs_get_subvolume_ro(snapshot_fd, &read_only)) + die("failure: btrfs_get_subvolume_ro"); + + if (read_only) + die("failure: read_only"); + + if (!btrfs_set_subvolume_ro(snapshot_fd, true)) + die("failure: btrfs_set_subvolume_ro"); + if (errno != EPERM) + die("failure: errno"); + + safe_close(snapshot_fd); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + /* remove directory */ + if (btrfs_delete_subvolume(tree_fd, BTRFS_SUBVOLUME1_SNAPSHOT1)) { + log_stderr("failure: btrfs_delete_subvolume"); + goto out; + } + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(open_tree_fd); + safe_close(subvolume_fd); + safe_close(tree_fd); + safe_close(userns_fd); + + return fret; +} + +#define BTRFS_SUBVOLUME_SUBVOL1 "subvol1" +#define BTRFS_SUBVOLUME_SUBVOL2 "subvol2" +#define BTRFS_SUBVOLUME_SUBVOL3 "subvol3" +#define BTRFS_SUBVOLUME_SUBVOL4 "subvol4" + +#define BTRFS_SUBVOLUME_SUBVOL1_ID 0 +#define BTRFS_SUBVOLUME_SUBVOL2_ID 1 +#define BTRFS_SUBVOLUME_SUBVOL3_ID 2 +#define BTRFS_SUBVOLUME_SUBVOL4_ID 3 + +#define BTRFS_SUBVOLUME_DIR1 "dir1" +#define BTRFS_SUBVOLUME_DIR2 "dir2" + +#define BTRFS_SUBVOLUME_MNT "mnt_subvolume1" + +#define BTRFS_SUBVOLUME_SUBVOL1xSUBVOL3 "subvol1/subvol3" +#define BTRFS_SUBVOLUME_SUBVOL1xDIR1xDIR2 "subvol1/dir1/dir2" +#define BTRFS_SUBVOLUME_SUBVOL1xDIR1xDIR2xSUBVOL4 "subvol1/dir1/dir2/subvol4" + +/* + * We create the following mount layout to test lookup: + * + * |-/mnt/test /dev/loop0 btrfs rw,relatime,space_cache,subvolid=5,subvol=/ + * | |-/mnt/test/mnt1 /dev/loop1[/subvol1] btrfs rw,relatime,space_cache,user_subvol_rm_allowed,subvolid=268,subvol=/subvol1 + * '-/mnt/scratch /dev/loop1 btrfs rw,relatime,space_cache,user_subvol_rm_allowed,subvolid=5,subvol=/ + */ +static int btrfs_subvolume_lookup_user(void) +{ + int fret = -1; + int dir1_fd = -EBADF, dir2_fd = -EBADF, mnt_fd = -EBADF, + open_tree_fd = -EBADF, tree_fd = -EBADF, userns_fd = -EBADF; + int subvolume_fds[BTRFS_SUBVOLUME_SUBVOL4_ID + 1]; + uint64_t subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID + 1]; + uint64_t subvolid = -EINVAL; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + struct btrfs_iter *iter; + + if (!caps_supported()) + return 0; + + for (int i = 0; i < ARRAY_SIZE(subvolume_fds); i++) + subvolume_fds[i] = -EBADF; + + for (int i = 0; i < ARRAY_SIZE(subvolume_ids); i++) + subvolume_ids[i] = -EINVAL; + + if (btrfs_create_subvolume(t_mnt_scratch_fd, BTRFS_SUBVOLUME_SUBVOL1)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (btrfs_create_subvolume(t_mnt_scratch_fd, BTRFS_SUBVOLUME_SUBVOL2)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID] = openat(t_mnt_scratch_fd, + BTRFS_SUBVOLUME_SUBVOL1, + O_CLOEXEC | O_DIRECTORY); + if (subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID] < 0) { + log_stderr("failure: openat"); + goto out; + } + + /* create subvolume */ + if (btrfs_create_subvolume(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID], BTRFS_SUBVOLUME_SUBVOL3)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (mkdirat(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID], BTRFS_SUBVOLUME_DIR1, 0777)) { + log_stderr("failure: mkdirat"); + goto out; + } + + dir1_fd = openat(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID], BTRFS_SUBVOLUME_DIR1, + O_CLOEXEC | O_DIRECTORY); + if (dir1_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (mkdirat(dir1_fd, BTRFS_SUBVOLUME_DIR2, 0777)) { + log_stderr("failure: mkdirat"); + goto out; + } + + dir2_fd = openat(dir1_fd, BTRFS_SUBVOLUME_DIR2, O_CLOEXEC | O_DIRECTORY); + if (dir2_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_create_subvolume(dir2_fd, BTRFS_SUBVOLUME_SUBVOL4)) { + log_stderr("failure: btrfs_create_subvolume"); + goto out; + } + + if (mkdirat(t_mnt_fd, BTRFS_SUBVOLUME_MNT, 0777)) { + log_stderr("failure: mkdirat"); + goto out; + } + + snprintf(t_buf, sizeof(t_buf), "%s/%s", t_mountpoint, BTRFS_SUBVOLUME_MNT); + if (sys_mount(t_device_scratch, t_buf, "btrfs", 0, + "subvol=" BTRFS_SUBVOLUME_SUBVOL1)) { + log_stderr("failure: mount"); + goto out; + } + + mnt_fd = openat(t_mnt_fd, BTRFS_SUBVOLUME_MNT, O_CLOEXEC | O_DIRECTORY); + if (mnt_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (chown_r(t_mnt_scratch_fd, ".", 1000, 1000)) { + log_stderr("failure: chown_r"); + goto out; + } + + subvolume_fds[BTRFS_SUBVOLUME_SUBVOL2_ID] = openat(t_mnt_scratch_fd, + BTRFS_SUBVOLUME_SUBVOL2, + O_CLOEXEC | O_DIRECTORY); + if (subvolume_fds[BTRFS_SUBVOLUME_SUBVOL2_ID] < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL1_ID], + &subvolume_ids[BTRFS_SUBVOLUME_SUBVOL1_ID])) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL2_ID], + &subvolume_ids[BTRFS_SUBVOLUME_SUBVOL2_ID])) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + subvolume_fds[BTRFS_SUBVOLUME_SUBVOL3_ID] = openat(t_mnt_scratch_fd, + BTRFS_SUBVOLUME_SUBVOL1xSUBVOL3, + O_CLOEXEC | O_DIRECTORY); + if (subvolume_fds[BTRFS_SUBVOLUME_SUBVOL3_ID] < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL3_ID], + &subvolume_ids[BTRFS_SUBVOLUME_SUBVOL3_ID])) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + subvolume_fds[BTRFS_SUBVOLUME_SUBVOL4_ID] = openat(t_mnt_scratch_fd, + BTRFS_SUBVOLUME_SUBVOL1xDIR1xDIR2xSUBVOL4, + O_CLOEXEC | O_DIRECTORY); + if (subvolume_fds[BTRFS_SUBVOLUME_SUBVOL4_ID] < 0) { + log_stderr("failure: openat"); + goto out; + } + + if (btrfs_get_subvolume_id(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL4_ID], + &subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID])) { + log_stderr("failure: btrfs_get_subvolume_id"); + goto out; + } + + + if (fchmod(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL3_ID], S_IRUSR | S_IWUSR | S_IXUSR), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + if (fchmod(subvolume_fds[BTRFS_SUBVOLUME_SUBVOL4_ID], S_IRUSR | S_IWUSR | S_IXUSR), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(mnt_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + /* + * The open_tree() syscall returns an O_PATH file descriptor which we + * can't use with ioctl(). So let's reopen it as a proper file + * descriptor. + */ + tree_fd = openat(open_tree_fd, ".", O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (tree_fd < 0) { + log_stderr("failure: openat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + bool subvolume3_found = false, subvolume4_found = false; + + if (!switch_fsids(11000, 11000)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: lower caps"); + + if (btrfs_iterator_start(tree_fd, 0, &iter)) + die("failure: btrfs_iterator_start"); + + for (;;) { + char *subvol_path = NULL; + int ret; + + ret = btrfs_iterator_next(iter, &subvol_path, &subvolid); + if (ret == 1) + break; + else if (ret) + die("failure: btrfs_iterator_next"); + + if (subvolid != subvolume_ids[BTRFS_SUBVOLUME_SUBVOL3_ID] && + subvolid != subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID]) + die("failure: subvolume id %llu->%s", + (long long unsigned)subvolid, subvol_path); + + if (subvolid == subvolume_ids[BTRFS_SUBVOLUME_SUBVOL3_ID]) + subvolume3_found = true; + + if (subvolid == subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID]) + subvolume4_found = true; + + free(subvol_path); + } + btrfs_iterator_end(iter); + + if (!subvolume3_found || !subvolume4_found) + die("failure: subvolume id"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + bool subvolume3_found = false, subvolume4_found = false; + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (btrfs_iterator_start(tree_fd, 0, &iter)) + die("failure: btrfs_iterator_start"); + + for (;;) { + char *subvol_path = NULL; + int ret; + + ret = btrfs_iterator_next(iter, &subvol_path, &subvolid); + if (ret == 1) + break; + else if (ret) + die("failure: btrfs_iterator_next"); + + if (subvolid != subvolume_ids[BTRFS_SUBVOLUME_SUBVOL3_ID] && + subvolid != subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID]) + die("failure: subvolume id %llu->%s", + (long long unsigned)subvolid, subvol_path); + + if (subvolid == subvolume_ids[BTRFS_SUBVOLUME_SUBVOL3_ID]) + subvolume3_found = true; + + if (subvolid == subvolume_ids[BTRFS_SUBVOLUME_SUBVOL4_ID]) + subvolume4_found = true; + + free(subvol_path); + } + btrfs_iterator_end(iter); + + if (!subvolume3_found || !subvolume4_found) + die("failure: subvolume id"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + bool subvolume_found = false; + + if (!switch_fsids(0, 0)) + die("failure: switch fsids"); + + if (!caps_down()) + die("failure: lower caps"); + + if (btrfs_iterator_start(tree_fd, 0, &iter)) + die("failure: btrfs_iterator_start"); + + for (;;) { + char *subvol_path = NULL; + int ret; + + ret = btrfs_iterator_next(iter, &subvol_path, &subvolid); + if (ret == 1) + break; + else if (ret) + die("failure: btrfs_iterator_next"); + + free(subvol_path); + + subvolume_found = true; + break; + } + btrfs_iterator_end(iter); + + if (subvolume_found) + die("failure: subvolume id"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + userns_fd = get_userns_fd(0, 30000, 10000); + if (userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + bool subvolume_found = false; + + if (!switch_userns(userns_fd, 0, 0, true)) + die("failure: switch_userns"); + + if (btrfs_iterator_start(tree_fd, 0, &iter)) + die("failure: btrfs_iterator_start"); + + for (;;) { + char *subvol_path = NULL; + int ret; + + ret = btrfs_iterator_next(iter, &subvol_path, &subvolid); + if (ret == 1) + break; + else if (ret) + die("failure: btrfs_iterator_next"); + + free(subvol_path); + + subvolume_found = true; + break; + } + btrfs_iterator_end(iter); + + if (subvolume_found) + die("failure: subvolume id"); + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(dir1_fd); + safe_close(dir2_fd); + safe_close(open_tree_fd); + safe_close(tree_fd); + safe_close(userns_fd); + for (int i = 0; i < ARRAY_SIZE(subvolume_fds); i++) + safe_close(subvolume_fds[i]); + snprintf(t_buf, sizeof(t_buf), "%s/%s", t_mountpoint, BTRFS_SUBVOLUME_MNT); + sys_umount2(t_buf, MNT_DETACH); + unlinkat(t_mnt_fd, BTRFS_SUBVOLUME_MNT, AT_REMOVEDIR); + + return fret; +} + +static void usage(void) +{ + fprintf(stderr, "Description:\n"); + fprintf(stderr, " Run idmapped mount tests\n\n"); + + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, "--device Device used in the tests\n"); + fprintf(stderr, "--fstype Filesystem type used in the tests\n"); + fprintf(stderr, "--help Print help\n"); + fprintf(stderr, "--mountpoint Mountpoint of device\n"); + fprintf(stderr, "--supported Test whether idmapped mounts are supported on this filesystem\n"); + fprintf(stderr, "--test-core Run core idmapped mount testsuite\n"); + fprintf(stderr, "--test-fscaps-regression Run fscap regression tests\n"); + fprintf(stderr, "--scratch-mountpoint Mountpoint of scratch device used in the tests\n"); + fprintf(stderr, "--scratch-device Scratch device used in the tests\n"); + + _exit(EXIT_SUCCESS); +} + +static const struct option longopts[] = { + {"device", required_argument, 0, 1}, + {"fstype", required_argument, 0, 2}, + {"mountpoint", required_argument, 0, 3}, + {"supported", no_argument, 0, 4}, + {"help", no_argument, 0, 5}, + {"test-core", no_argument, 0, 6}, + {"test-fscaps-regression", no_argument, 0, 7}, + {"test-nested-userns", no_argument, 0, 8}, + {"test-btrfs", no_argument, 0, 9}, + {"scratch-mountpoint", required_argument, 0, 10}, + {"scratch-device", required_argument, 0, 11}, + {NULL, 0, 0, 0}, +}; + +struct t_idmapped_mounts { + int (*test)(void); + const char *description; +} basic_suite[] = { + { acls, "posix acls on regular mounts", }, + { create_in_userns, "create operations in user namespace", }, + { device_node_in_userns, "device node in user namespace", }, + { expected_uid_gid_idmapped_mounts, "expected ownership on idmapped mounts", }, + { fscaps, "fscaps on regular mounts", }, + { fscaps_idmapped_mounts, "fscaps on idmapped mounts", }, + { fscaps_idmapped_mounts_in_userns, "fscaps on idmapped mounts in user namespace", }, + { fscaps_idmapped_mounts_in_userns_separate_userns, "fscaps on idmapped mounts in user namespace with different id mappings", }, + { fsids_mapped, "mapped fsids", }, + { fsids_unmapped, "unmapped fsids", }, + { hardlink_crossing_mounts, "cross mount hardlink", }, + { hardlink_crossing_idmapped_mounts, "cross idmapped mount hardlink", }, + { hardlink_from_idmapped_mount, "hardlinks from idmapped mounts", }, + { hardlink_from_idmapped_mount_in_userns, "hardlinks from idmapped mounts in user namespace", }, +#ifdef HAVE_LIBURING_H + { io_uring, "io_uring", }, + { io_uring_userns, "io_uring in user namespace", }, + { io_uring_idmapped, "io_uring from idmapped mounts", }, + { io_uring_idmapped_userns, "io_uring from idmapped mounts in user namespace", }, + { io_uring_idmapped_unmapped, "io_uring from idmapped mounts with unmapped ids", }, + { io_uring_idmapped_unmapped_userns, "io_uring from idmapped mounts with unmapped ids in user namespace", }, +#endif + { protected_symlinks, "following protected symlinks on regular mounts", }, + { protected_symlinks_idmapped_mounts, "following protected symlinks on idmapped mounts", }, + { protected_symlinks_idmapped_mounts_in_userns, "following protected symlinks on idmapped mounts in user namespace", }, + { rename_crossing_mounts, "cross mount rename", }, + { rename_crossing_idmapped_mounts, "cross idmapped mount rename", }, + { rename_from_idmapped_mount, "rename from idmapped mounts", }, + { rename_from_idmapped_mount_in_userns, "rename from idmapped mounts in user namespace", }, + { setattr_truncate, "setattr truncate", }, + { setattr_truncate_idmapped, "setattr truncate on idmapped mounts", }, + { setattr_truncate_idmapped_in_userns, "setattr truncate on idmapped mounts in user namespace", }, + { setgid_create, "create operations in directories with setgid bit set", }, + { setgid_create_idmapped, "create operations in directories with setgid bit set on idmapped mounts", }, + { setgid_create_idmapped_in_userns, "create operations in directories with setgid bit set on idmapped mounts in user namespace", }, + { setid_binaries, "setid binaries on regular mounts", }, + { setid_binaries_idmapped_mounts, "setid binaries on idmapped mounts", }, + { setid_binaries_idmapped_mounts_in_userns, "setid binaries on idmapped mounts in user namespace", }, + { setid_binaries_idmapped_mounts_in_userns_separate_userns, "setid binaries on idmapped mounts in user namespace with different id mappings", }, + { sticky_bit_unlink, "sticky bit unlink operations on regular mounts", }, + { sticky_bit_unlink_idmapped_mounts, "sticky bit unlink operations on idmapped mounts", }, + { sticky_bit_unlink_idmapped_mounts_in_userns, "sticky bit unlink operations on idmapped mounts in user namespace", }, + { sticky_bit_rename, "sticky bit rename operations on regular mounts", }, + { sticky_bit_rename_idmapped_mounts, "sticky bit rename operations on idmapped mounts", }, + { sticky_bit_rename_idmapped_mounts_in_userns, "sticky bit rename operations on idmapped mounts in user namespace", }, + { symlink_regular_mounts, "symlink from regular mounts", }, + { symlink_idmapped_mounts, "symlink from idmapped mounts", }, + { symlink_idmapped_mounts_in_userns, "symlink from idmapped mounts in user namespace", }, + { threaded_idmapped_mount_interactions, "threaded operations on idmapped mounts", }, +}; + +struct t_idmapped_mounts fscaps_in_ancestor_userns[] = { + { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns, "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns", }, +}; + +struct t_idmapped_mounts t_nested_userns[] = { + { nested_userns, "test that nested user namespaces behave correctly when attached to idmapped mounts", }, +}; + +struct t_idmapped_mounts t_btrfs[] = { + { btrfs_subvolumes_fsids_mapped, "test subvolumes with mapped fsids", }, + { btrfs_subvolumes_fsids_mapped_userns, "test subvolumes with mapped fsids inside user namespace", }, + { btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed, "test subvolume deletion with user_subvol_rm_allowed mount option", }, + { btrfs_subvolumes_fsids_mapped_userns_user_subvol_rm_allowed, "test subvolume deletion with user_subvol_rm_allowed mount option inside user namespace", }, + { btrfs_subvolumes_fsids_unmapped, "test subvolumes with unmapped fsids", }, + { btrfs_subvolumes_fsids_unmapped_userns, "test subvolumes with unmapped fsids inside user namespace", }, + { btrfs_snapshots_fsids_mapped, "test snapshots with mapped fsids", }, + { btrfs_snapshots_fsids_mapped_userns, "test snapshots with mapped fsids inside user namespace", }, + { btrfs_snapshots_fsids_mapped_user_subvol_rm_allowed, "test snapshots deletion with user_subvol_rm_allowed mount option", }, + { btrfs_snapshots_fsids_mapped_userns_user_subvol_rm_allowed, "test snapshots deletion with user_subvol_rm_allowed mount option inside user namespace", }, + { btrfs_snapshots_fsids_unmapped, "test snapshots with unmapped fsids", }, + { btrfs_snapshots_fsids_unmapped_userns, "test snapshots with unmapped fsids inside user namespace", }, + { btrfs_delete_by_spec_id, "test subvolume deletion by spec id", }, + { btrfs_subvolumes_setflags_fsids_mapped, "test subvolume flags with mapped fsids", }, + { btrfs_subvolumes_setflags_fsids_mapped_userns, "test subvolume flags with mapped fsids inside user namespace", }, + { btrfs_subvolumes_setflags_fsids_unmapped, "test subvolume flags with unmapped fsids", }, + { btrfs_subvolumes_setflags_fsids_unmapped_userns, "test subvolume flags with unmapped fsids inside user namespace", }, + { btrfs_snapshots_setflags_fsids_mapped, "test snapshots flags with mapped fsids", }, + { btrfs_snapshots_setflags_fsids_mapped_userns, "test snapshots flags with mapped fsids inside user namespace", }, + { btrfs_snapshots_setflags_fsids_unmapped, "test snapshots flags with unmapped fsids", }, + { btrfs_snapshots_setflags_fsids_unmapped_userns, "test snapshots flags with unmapped fsids inside user namespace", }, + { btrfs_subvolume_lookup_user, "test unprivileged subvolume lookup", }, +}; + +static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size) +{ + int i; + + for (i = 0; i < suite_size; i++) { + struct t_idmapped_mounts *t = &suite[i]; + int ret; + pid_t pid; + + test_setup(); + + pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) { + ret = t->test(); + if (ret) + die("failure: %s", t->description); + + exit(EXIT_SUCCESS); + } + + ret = wait_for_pid(pid); + test_cleanup(); + + if (ret) + return false; + } + + return true; +} + +int main(int argc, char *argv[]) +{ + int fret, ret; + int index = 0; + bool supported = false, test_btrfs = false, test_core = false, + test_fscaps_regression = false, test_nested_userns = false; + + while ((ret = getopt_long(argc, argv, "", longopts, &index)) != -1) { + switch (ret) { + case 1: + t_device = optarg; + break; + case 2: + t_fstype = optarg; + break; + case 3: + t_mountpoint = optarg; + break; + case 4: + supported = true; + break; + case 6: + test_core = true; + break; + case 7: + test_fscaps_regression = true; + break; + case 8: + test_nested_userns = true; + break; + case 9: + test_btrfs = true; + break; + case 10: + t_mountpoint_scratch = optarg; + break; + case 11: + t_device_scratch = optarg; + break; + case 5: + /* fallthrough */ + default: + usage(); + } + } + + if (!t_device) + die_errno(EINVAL, "test device missing"); + + if (!t_fstype) + die_errno(EINVAL, "test filesystem type missing"); + + if (!t_mountpoint) + die_errno(EINVAL, "mountpoint of test device missing"); + + /* create separate mount namespace */ + if (unshare(CLONE_NEWNS)) + die("failure: create new mount namespace"); + + /* turn off mount propagation */ + if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)) + die("failure: turn mount propagation off"); + + t_mnt_fd = openat(-EBADF, t_mountpoint, O_CLOEXEC | O_DIRECTORY); + if (t_mnt_fd < 0) + die("failed to open %s", t_mountpoint); + + t_mnt_scratch_fd = openat(-EBADF, t_mountpoint_scratch, O_CLOEXEC | O_DIRECTORY); + if (t_mnt_fd < 0) + die("failed to open %s", t_mountpoint_scratch); + + /* + * Caller just wants to know whether the filesystem we're on supports + * idmapped mounts. + */ + if (supported) { + int open_tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + .userns_fd = -EBADF, + }; + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 1000, 1); + if (attr.userns_fd < 0) + exit(EXIT_FAILURE); + + open_tree_fd = sys_open_tree(t_mnt_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) + ret = -1; + else + ret = sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)); + + close(open_tree_fd); + close(attr.userns_fd); + + if (ret) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); + } + + stash_overflowuid(); + stash_overflowgid(); + + fret = EXIT_FAILURE; + + if (test_core && !run_test(basic_suite, ARRAY_SIZE(basic_suite))) + goto out; + + if (test_fscaps_regression && + !run_test(fscaps_in_ancestor_userns, + ARRAY_SIZE(fscaps_in_ancestor_userns))) + goto out; + + if (test_nested_userns && + !run_test(t_nested_userns, ARRAY_SIZE(t_nested_userns))) + goto out; + + if (test_btrfs && !run_test(t_btrfs, ARRAY_SIZE(t_btrfs))) goto out; fret = EXIT_SUCCESS; diff --git a/tests/btrfs/242 b/tests/btrfs/242 new file mode 100755 index 00000000..bb833842 --- /dev/null +++ b/tests/btrfs/242 @@ -0,0 +1,34 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2021 Christian Brauner. All Rights Reserved. +# +# FS QA Test 242 +# +# Test that idmapped mounts behave correctly with btrfs specific features such +# as subvolume and snapshot creation and deletion. +# +. ./common/preamble +_begin_fstest auto quick idmapped subvolume + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# real QA test starts here + +_supported_fs btrfs +_require_idmapped_mounts +_require_test +_require_scratch + +_scratch_mkfs >> $seqres.full +_scratch_mount "-o user_subvol_rm_allowed" >> $seqres.full + +echo "Silence is golden" + +$here/src/idmapped-mounts/idmapped-mounts --test-btrfs --device "$TEST_DEV" \ + --mountpoint "$TEST_DIR" --scratch-device "$SCRATCH_DEV" \ + --scratch-mountpoint "$SCRATCH_MNT" --fstype "$FSTYP" + +status=$? +exit diff --git a/tests/btrfs/242.out b/tests/btrfs/242.out new file mode 100644 index 00000000..a46d7770 --- /dev/null +++ b/tests/btrfs/242.out @@ -0,0 +1,2 @@ +QA output created by 242 +Silence is golden