From patchwork Mon Jul 19 11:10: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: 12385535 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=-20.5 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 4F5E5C07E9D for ; Mon, 19 Jul 2021 11:11:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3989C6115B for ; Mon, 19 Jul 2021 11:11:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236523AbhGSKaX (ORCPT ); Mon, 19 Jul 2021 06:30:23 -0400 Received: from mail.kernel.org ([198.145.29.99]:59284 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKaX (ORCPT ); Mon, 19 Jul 2021 06:30:23 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id B71B5610FB; Mon, 19 Jul 2021 11:11:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693063; bh=B5eoBNW0x4lkDFlY32IvAsrLNWgNmdCuphxwahLGoE4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pgnWWfD/+CnweO6UQlJ09ikGdFHQq6jzBLa9Jso72l9XAVK95PTwaoUZf1UvZKC9t xR/vV0Uqp5VAsGyAZ5sybywKlZZ+CGu+5h1+yMPrcPP+MenizEpm+jYnuP0LDX44n9 XPPUgJvHTMdDHpD0tJrJ3MdPHO7OnvHxF34278JODasN2ZdClShKtKAiiwrUZ1IR7S D8rM5dfBGVJMW5bKBJsGitk5exdmOTzFrxgJvGBSSQkBJaP71du0Wbqn2ONe76N0Ja 6pN6dUmKr05f8pxCa5T0HInsRS7iIUWJxhPUZYFLz/VMuh1xAMq5SihepTKCF7kO0u 0afy5XznQ6Ilg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , linux-fsdevel@vger.kernel.org Subject: [PATCH v2 01/21] namei: add mapping aware lookup helper Date: Mon, 19 Jul 2021 13:10:32 +0200 Message-Id: <20210719111052.1626299-2-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4582; h=from:subject; bh=7bSgb7NEdlPbHqjtamXABcS39sKzhqLV9+DKcg49aY0=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd08afEalcecs/c/PHvu/Yw/4jfdGb5fPdJ0yPjvqfJE 7yyjjI5SFgYxLgZZMUUWh3aTcLnlPBWbjTI1YOawMoEMYeDiFICJhNYz/DN+fqwmqtB4R5rQhtls7Y z5YgkvP/E1fdOZ/f/jPctuhTWMDH8Fs77PfvP03nKu2n0J9T4pE2QTGG8vVrtQ/viOxpbJhrwA 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 rely on 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 adding a version of this helper 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 --- /* v2 */ - Al Viro : - Add a new lookup helper instead of changing the old ones. --- fs/namei.c | 44 +++++++++++++++++++++++++++++++++++++------ include/linux/namei.h | 2 ++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..8f416698ee34 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2575,8 +2575,9 @@ 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, - int len, struct qstr *this) +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; this->len = len; @@ -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); @@ -2655,7 +2656,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(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2664,6 +2665,37 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) } EXPORT_SYMBOL(lookup_one_len); +/** + * lookup_mapped_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 + * + * Note that this routine is purely a helper for filesystem usage and should + * not be called by generic code. + * + * The caller must hold base->i_mutex. + */ +struct dentry *lookup_mapped_one_len(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, + int len) +{ + struct dentry *dentry; + struct qstr this; + int err; + + WARN_ON_ONCE(!inode_is_locked(base->d_inode)); + + err = lookup_one_len_common(mnt_userns, name, base, len, &this); + if (err) + return ERR_PTR(err); + + dentry = lookup_dcache(&this, base, 0); + return dentry ? dentry : __lookup_slow(&this, base, 0); +} +EXPORT_SYMBOL(lookup_mapped_one_len); + /** * lookup_one_len_unlocked - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2683,7 +2715,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/include/linux/namei.h b/include/linux/namei.h index be9a2b349ca7..fd9d22128df6 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -68,6 +68,8 @@ 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_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_mapped_one_len(struct user_namespace *, + const char *, struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); From patchwork Mon Jul 19 11:10: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: 12385537 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=-20.5 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 090FAC12002 for ; Mon, 19 Jul 2021 11:11:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EA2FC61165 for ; Mon, 19 Jul 2021 11:11:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236442AbhGSKaZ (ORCPT ); Mon, 19 Jul 2021 06:30:25 -0400 Received: from mail.kernel.org ([198.145.29.99]:59322 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKaZ (ORCPT ); Mon, 19 Jul 2021 06:30:25 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 94C9B6113C; Mon, 19 Jul 2021 11:11:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693065; bh=5GH3IZAKq/cw7ybT8neP42F5HkBTerTYEqa8fFU09eg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NZN1XSTCR0nSzXLR3HVT84tiMBJSQgYPwMv3o0rFLtWg2mFBKCRoMo1hXvH4Aqoo4 GmxUciXB2VVUr2loaKgwJeLF22xwfhEl8tk5t/EzgSF5e7kcxghmL5wd/SkB4oL6sJ spLVbGPfH/FwCk6ruvO96zcBOo5zK7lgnpDxa/nRRBdXDdB570iNLZkenzNh7tn0z3 SJJJ1oQayehNMdEswSnKpZ3N6npA1+F7taPaRS79FPfbAheUdkwoNKBtBFHKls5Nms azouKwRTfncsktdQ+N6mc3NnYURm26wvxB6hMp+g2EY0Q/sh1LLCIO6VwBepHEEnbh fE5OtNELBropw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 02/21] btrfs/inode: handle idmaps in btrfs_new_inode() Date: Mon, 19 Jul 2021 13:10:33 +0200 Message-Id: <20210719111052.1626299-3-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4635; h=from:subject; bh=T6CniUBhfqWP/zsl4QdNeVmJV9K4pg35sN/QaYOiakU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd1cf/N+q+qPaoZHprHzTNdPFLM08bxt8m37stLTn9QL 5jO87yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZiIHA/Df6/0idoHDunLP/teMfXp+x qHhOM8B6/5bXi94OHfB0Wl2/IZGVZcivJ22BWa9ky5ceGRev6fp81WXP/zgfdv3JTyhw1Mt1gA 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 --- /* v2 */ unchanged --- 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 8f60314c36c5..19e83afc7d45 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6370,6 +6370,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, @@ -6479,7 +6480,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); @@ -6664,9 +6665,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; @@ -6728,9 +6729,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; @@ -6873,8 +6874,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); @@ -8949,7 +8951,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)) @@ -9442,7 +9445,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)), @@ -9941,9 +9944,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; @@ -10292,7 +10296,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 Mon Jul 19 11:10: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: 12385539 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=-20.5 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 33479C07E9B for ; Mon, 19 Jul 2021 11:11:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 197706115B for ; Mon, 19 Jul 2021 11:11:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236560AbhGSKa2 (ORCPT ); Mon, 19 Jul 2021 06:30:28 -0400 Received: from mail.kernel.org ([198.145.29.99]:59346 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKa1 (ORCPT ); Mon, 19 Jul 2021 06:30:27 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id F344F6112D; Mon, 19 Jul 2021 11:11:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693067; bh=YEFooLh98vJPJNJ+f29kzJlURCakQOXb6R8rjNFwqoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eYXrrYox4VgiNF99PV2963MVMRZsXDAYtsSEAMF1FacMc2J++2d/YxbVYKf17wIG4 G7HgmtGaolxCKd0P0dmX9PHuon4qTDpbUJGtzPhDXMPNI4aiSbL7JAU1i5YCE3CgaA aDUYs0+QGc+fX5Dh+LRsjDuBjoKIcZI8EwvjnmHIflLX729EZJKSiojmVfczQzmE1Z 3q0WzUe2CS7TdxhwON/exIIGAsjwas6ZTTPK20G/Snyl/y6zaqZqSfAzk7LYFzH3QT JVowXA8UFJdv5V19NOr8H5l7fVwMhzXmdeQ7QfwGgQkeXc0wkb6FlSEQPV7P6R77Rv 116q2QqMFmxxA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 03/21] btrfs/inode: allow idmapped rename iop Date: Mon, 19 Jul 2021 13:10:34 +0200 Message-Id: <20210719111052.1626299-4-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2610; h=from:subject; bh=grrzOjavKLZWLLGezpK1WrwE3NMHr10v0YlE7JCxiQ8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd0y/3Dlj7gz9xeH7v7Ytr7LhUP2yeHohETfDgN1DtP3 15fydZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAExEVoXhJ+PvhVN+TfjwZWIoG6/HqQ ZH5gs3Ts/dKcfsGHa407KuUY/hr9D8dNcz9VOL+/TKagXvvgtf+iJs/3KXS7IaarwXzGXvcAAA 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 --- /* v2 */ unchanged --- 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 19e83afc7d45..6e4e9f4fbdf3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9433,6 +9433,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) { @@ -9445,7 +9446,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)), @@ -9482,9 +9483,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; @@ -9657,8 +9659,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); @@ -9708,7 +9710,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 Mon Jul 19 11:10: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: 12385541 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=-20.5 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 9B7A1C07E9B for ; Mon, 19 Jul 2021 11:11:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 87E2861165 for ; Mon, 19 Jul 2021 11:11:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236569AbhGSKaa (ORCPT ); Mon, 19 Jul 2021 06:30:30 -0400 Received: from mail.kernel.org ([198.145.29.99]:59368 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKaa (ORCPT ); Mon, 19 Jul 2021 06:30:30 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5E8436108B; Mon, 19 Jul 2021 11:11:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693070; bh=qNhbnO0VWX/6D4T+zgjTmjl3LP3DUGsO0RSESM4YihA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R8tRzLJfQUKgNFB22IGVnPBkyt1VqonOONuDEw2EtLcOvT6+o12tDBGRxfXfPbJgT JEdhqw4dqOZK1OBn3SvEyRiHwzx+4xAfOUp1t2zjFpeaVaU/sVbEMWEdCeMFfYjjZ+ V1TgGIuK1z7a2ANaf8Q4z9Oab5D3R3hmJExZBpVHLdsIXaF3aMFsBNo/QiT35Xr/9Y kHkDIbrwh/CsnjAz3PMx85onyi8dISiqwwAE2n5StHaCujk5BMCmDl1mF7sZbUsqRt AL8BTxa7d+WlF1qsSUssADZoZgCI67nOCXECGta3yhruBXlKXZBGbIw+/4KViw0pDr 9cEQGLevCRQYg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 04/21] btrfs/inode: allow idmapped getattr iop Date: Mon, 19 Jul 2021 13:10:35 +0200 Message-Id: <20210719111052.1626299-5-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=946; h=from:subject; bh=cQVCA+o4p9p+j6MuVDM6PxlYmBDNqjbRk309Wy8Chs4=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd0SueDzg8IzHMuNq83SJtj35U057G52tG3BnA9NS8Xi Xlm0dZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyE4TQjw3++zvsHTCdYzLZSXuduaL bkuuomU9UHZyV63qjffe4iOY2R4eu3bSUCiS7bvav4D73bGdtnUHr0jd9zpVmPjhmeNTygzwwA 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 --- /* v2 */ unchanged --- 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 6e4e9f4fbdf3..9b345af1c3e0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9195,7 +9195,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 Mon Jul 19 11:10: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: 12385543 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=-20.5 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 04698C12002 for ; Mon, 19 Jul 2021 11:11:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E520561175 for ; Mon, 19 Jul 2021 11:11:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236571AbhGSKad (ORCPT ); Mon, 19 Jul 2021 06:30:33 -0400 Received: from mail.kernel.org ([198.145.29.99]:59396 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKac (ORCPT ); Mon, 19 Jul 2021 06:30:32 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id A7617610F7; Mon, 19 Jul 2021 11:11:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693072; bh=C/bfjpvy2X9vKObx4pY1O6Hk4QoLLjdLZoWTFAim0N4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RwS266Zwn3mM9b96FnNGmSC3hwrg42ARITB2L/zQOUTm6fHJssjDPlMwhUTAFJA0V C9/G2C1UhM1vfRUQf/Ackfjdfj222lxA7n6UusF9xET0inTIKORF9lpaKzPSh0tarD Qpk0+e1J8GVLrDQEDSIlgOk4NZX6doFcv/p8qjmIbSM7HwBeB7SWfamb6UkV5yF2OV 52xsJoJ09/b6HPdBFiLV6U9ShSpIZ7/xA/lGw4N0WyX/sQwzuX25xN5lOeNp1nWCcI ZmIuaSCu1PAZDu1kWYt2tE5J1FALfJKix9ZBE6e/l6poLII2Zf4rBRNiUgYYMNxln1 ASiDDug5AKu2w== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 05/21] btrfs/inode: allow idmapped mknod iop Date: Mon, 19 Jul 2021 13:10:36 +0200 Message-Id: <20210719111052.1626299-6-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=995; h=from:subject; bh=APpgDIYKdPKMH3ZuZmxTjcDSjOi/dUdt7nAZdxo1CzY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd3yXy5E5kjeK6enh3cXNPJtuxXifkJE6kXO/DNVUheb Jpxf0VHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCR9FiG/2EeGwqetFf8c2JR8o6/cT Lt3FbL3tlvgg45GZhcMrbMusHIsKYmmePBhemW/Dz+Rn48r+06RTyt5vs4bmLcUuq22rqVDQA= 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 --- /* v2 */ unchanged --- 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 9b345af1c3e0..1a50a039dc43 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6665,7 +6665,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 Mon Jul 19 11:10: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: 12385545 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=-20.5 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 F36E7C07E9D for ; Mon, 19 Jul 2021 11:11:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DED8A61175 for ; Mon, 19 Jul 2021 11:11:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236580AbhGSKaf (ORCPT ); Mon, 19 Jul 2021 06:30:35 -0400 Received: from mail.kernel.org ([198.145.29.99]:59418 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKaf (ORCPT ); Mon, 19 Jul 2021 06:30:35 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 3B5A86115B; Mon, 19 Jul 2021 11:11:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693075; bh=iL4KHWPCNL9PVntTO84YtX5UmPi7ZxWMD2s2MdlFlWM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iw1IIYX1ztZIhl9PqRNknQUHlsKX42nVSONqHlJ8OnGk67Cnkll26Bwny3JfO7Htj erEulbdnGmXidXYBuYXMNM19LPVxxxR4NZsk/krijidmfXq5QiP+itmmapBlJdtve5 iWLU/Bb4LJZY0b/VOFaukHfsAyyhNnEt17jtFc+nS02SjzlIPknFvaCXUOakyzyHIA EAvaVFgpnuYbHarcsQn6T3LUu9YXqa+mR5O9eFLVUeF5hdLzTfOTEDXdicf6VThXCY oFckbkvdyCb/vw8rF5o4kb/6Rh1DzZUYJRP3dATfB0WIzK9p37B1aw0PfjULil9JSP 8iI7Fe+ty+L/Q== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 06/21] btrfs/inode: allow idmapped create iop Date: Mon, 19 Jul 2021 13:10:37 +0200 Message-Id: <20210719111052.1626299-7-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=997; h=from:subject; bh=8RKG9mSvr0SFetOJO2TELuKVry0Yzw+U+dIyyTPtWM8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd2y491RP7fSiXUv3jAcfGjq5Z+iw/5n5wPe4ANB62oS dz5921HKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjARQVGG/4UlM7t+T/u064uRd6dRts RpKf6alR7fwy7pLo2SC1Rp+cbwv7w97OjU4mCxtt9Vy1MvK71uZnUu37vQxEB603xp01k2rAA= 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 --- /* v2 */ unchanged --- 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 1a50a039dc43..1fa290bb5272 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6729,7 +6729,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 Mon Jul 19 11:10: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: 12385547 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=-20.5 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 57A57C636C9 for ; Mon, 19 Jul 2021 11:11:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 44BB661181 for ; Mon, 19 Jul 2021 11:11:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236599AbhGSKai (ORCPT ); Mon, 19 Jul 2021 06:30:38 -0400 Received: from mail.kernel.org ([198.145.29.99]:59440 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKah (ORCPT ); Mon, 19 Jul 2021 06:30:37 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id DDE6B61164; Mon, 19 Jul 2021 11:11:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693078; bh=eYLJ17pWsfGbJMXLUA7WIN1te8q8LYuL3ubXp53nkEE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lBTy0MrhQc41/mhaf1Un/tm0AFzuMoyOvGFuvr+MklpyeLLwRVnKDw50wXcrJmb30 9O7qvqkBTNo2IeBcB2aIA6XTv0mSXYm1PZ+t8gyskdCblpasnpn59IishU658B5DzR 8u5G+juoMZ/sp3iB5D9KXwmnZD2/sZyKJerovVhkLbVWatNeacGlYnqgsaG1OWxC2U MCrWND9D9gQT4lHNeGpuBnBvKAAbpLiftkp6RFfo2SB5qbAt5zvev6QVSa23iwdBH4 M1pBtW324raK77jM5l2SKgxPxUhD/mj89DQQHGFF6vh/HZ/wBKp4bgTDL2Rvygst22 GjK/V1lQWMBsQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 07/21] btrfs/inode: allow idmapped mkdir iop Date: Mon, 19 Jul 2021 13:10:38 +0200 Message-Id: <20210719111052.1626299-8-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=984; h=from:subject; bh=kgLQNybeZskpDLxMR1mPPzmKcyFOn39hYXRVzLKQOGw=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd0q/Jf70u+tv/esvLk7+tp0mU/3ko0uLDTXPTJT3o1X LH3l145SFgYxLgZZMUUWh3aTcLnlPBWbjTI1YOawMoEMYeDiFICJsAsx/E/scSmdFvh6WezxmSyyLJ P3bj315hz7/LQ0+3n3dkuv+qPO8E/J9nFJ1+M3xgYN82+kvORe5+O/NUVxjqaWVuFG6+sv5rIAAA== 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 --- /* v2 */ unchanged --- 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 1fa290bb5272..9f0af257f89f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6874,7 +6874,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 Mon Jul 19 11:10: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: 12385549 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=-20.5 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 18DC5C12002 for ; Mon, 19 Jul 2021 11:11:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 03CAA611ED for ; Mon, 19 Jul 2021 11:11:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236469AbhGSKal (ORCPT ); Mon, 19 Jul 2021 06:30:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:59462 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKak (ORCPT ); Mon, 19 Jul 2021 06:30:40 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 8A8C361165; Mon, 19 Jul 2021 11:11:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693080; bh=GlTbanHlGqytxA7VyiW6PfWTtYO1uQSd3xXy3D6Md+k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DKM2vAgDIt152NLEDEOFvzI8I4Cntaef3YiV/HzntT1aRgd+rPyvbq7aB30tto0/e thN6yZ+SxOt9lNNGbAMYv32p2Q3VAWvUv5m4R5mFovMUrJ3BYdVMb7VVASDYrZnZFb pNTDGGpYM97OpdGN5pI2o5OZf+Ov1FANL8PyGwQEg6lhBFuWO/ONEgN1HdVct941S2 MB7qScNxO5Qax2XVixq59X++SNpPQHWJ7V/DdiANPbO+lMwjBKpw40wu0J3Rtqel2s EU8DVjs6cF2aaiQt3EBzJEA+ccRP4rG4byR+i2AaS6dTUCzjv8Oyghvp8vsDdebLtC TZeUvFmkCQuWQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 08/21] btrfs/inode: allow idmapped symlink iop Date: Mon, 19 Jul 2021 13:10:39 +0200 Message-Id: <20210719111052.1626299-9-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=998; h=from:subject; bh=mbPwWxbkNbTxpdh5z2P8s+TG3a/hIPCFOcR0hSm3Rkw=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd36qcxvSsBxXsYppxO0srYcXbGwRD6zqUJeSdl3srYz C49WRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwES6oxkZVnZ15Na4cG8vtXicwdV2JX Qf09+3UgKlFnFfZJOVuCYsYWS4/ezVbPfXbz8ymVqryxprvX2gz28pra7f27A8r3z3p2QmAA== 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 --- /* v2 */ unchanged --- 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 9f0af257f89f..d7ccbd9a2723 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9947,7 +9947,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 Mon Jul 19 11:10: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: 12385551 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=-20.5 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 24D11C07E9B for ; Mon, 19 Jul 2021 11:11:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0FC64611C1 for ; Mon, 19 Jul 2021 11:11:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236607AbhGSKap (ORCPT ); Mon, 19 Jul 2021 06:30:45 -0400 Received: from mail.kernel.org ([198.145.29.99]:59484 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236234AbhGSKam (ORCPT ); Mon, 19 Jul 2021 06:30:42 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1475D61166; Mon, 19 Jul 2021 11:11:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693082; bh=QCV+GpNkTo0y4JvC9RYQUdviGDle7/zmA60ZHUc524Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=neNrqUweAJX+zxaOa1mL6gBXDNImbmbKfJB6Z8mh9x+ROW4X/TqYehlp49fAfQ4qn kRTQOTMQ12hreslCITYrtzcuZmAU4D6c9k5xZKE6Lq78+IZA6GA/cyeIFl8hVqZtZx 01XiUMHovSjbipEpuRFpJAQtGOfgR4wV3qBVQHQoHmBRKeXnx80xvDw+rTPlNj9j/U IxrDSvsPd80zi2YhYvnt1YijcwL2m5SqjfuX/4kMAvUZUxe1ZWhfG4NQyBiMfJ3Frf cXNlcycMHNGJG8DlMx9sMfQKA7GYJm3oCvvZHWxwEuHDQszUBI9Az5gR7dDG4C3ndV MJtQ0Y+u66aug== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 09/21] btrfs/inode: allow idmapped tmpfile iop Date: Mon, 19 Jul 2021 13:10:40 +0200 Message-Id: <20210719111052.1626299-10-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=992; h=from:subject; bh=Ky4zY6yxQDSLvjxKhqLrj2QdxlTIopmvKtTY1x8KkTA=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd26Ifuq79OOB/fVrGT+vX2yQWEF31MtH1OOov2KKRef f9SM7ChlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZgI82uG3+y/uiYY7+EJWeWkaLol26 faZYIwR5CXjdire2eZ22at9mf4Z88l6tCk8dFfwNGoT+5IQvC87tUJec+tNGty2jrOLnPjBwA= 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 --- /* v2 */ unchanged --- 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 d7ccbd9a2723..dc368394722a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10299,7 +10299,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 Mon Jul 19 11:10: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: 12385553 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=-20.5 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 09E19C12002 for ; Mon, 19 Jul 2021 11:11:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E9204610FB for ; Mon, 19 Jul 2021 11:11:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236628AbhGSKar (ORCPT ); Mon, 19 Jul 2021 06:30:47 -0400 Received: from mail.kernel.org ([198.145.29.99]:59508 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236615AbhGSKap (ORCPT ); Mon, 19 Jul 2021 06:30:45 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 543DC61175; Mon, 19 Jul 2021 11:11:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693085; bh=ZuOgNGFu5VtL8lybo20fI0VubHlffH92/itsSCW+9TI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BzvFk2Cp2OpfFhwf4zYkqv1EnBDJof/IkZr0O5YWsrJ9Qwt6BbhGrEYqq3HWBA6zd 7EJ8xDkqDauCIN1lWrUKqi3CQTFC4s8mXXsZE7pIAWTrMgFwjIOaqkXecieUPXbNWO hlHkm1Ceg6oBSEw9F6ea+IRCXxzxN8OYBTTlCcqWWZURjoM5DIFnRjwaAJlif0XMf3 A26nYc/IEsCLh29PMi4KIdBFCyg8RSoZGcsdPirS0KdaN6fIdX/Ngjpg6d5TI6tnRt GwE1ECetjihPsY+7/lNvhy4fO9WwlJmEMVspzEDlmUilxpnPk41KSutk5ky/n7C0LI nGuTNF599qAcw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 10/21] btrfs/inode: allow idmapped setattr iop Date: Mon, 19 Jul 2021 13:10:41 +0200 Message-Id: <20210719111052.1626299-11-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1397; h=from:subject; bh=i9z5ckMKBoh7zmQy2KHIxk/ORQym3WjYxk7MqoB3OHY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd32c/f7ZMf8Rzt5duluurBsZ9KOhAt8e3p9suWON13/ Fuvk3lHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjARnkOMDH9z/v576uoRpLbPdF+cR0 Ny/t4nra2sgR2s0c/61/D92cfIsEFGTmu9/9vtmQEH+Nuf6d7pt/oVmFoz8QfTOhlmxrBMFgA= 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 --- /* v2 */ unchanged --- 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 dc368394722a..53b038029440 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5342,7 +5342,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; @@ -5353,12 +5353,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 Mon Jul 19 11:10: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: 12385557 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=-20.5 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 41CA6C636C9 for ; Mon, 19 Jul 2021 11:11:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2B7D8600EF for ; Mon, 19 Jul 2021 11:11:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236641AbhGSKau (ORCPT ); Mon, 19 Jul 2021 06:30:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:59538 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236619AbhGSKar (ORCPT ); Mon, 19 Jul 2021 06:30:47 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 86FE961181; Mon, 19 Jul 2021 11:11:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693087; bh=nMTMNxyJcEo9K1ozLBMPSdFnOdaMIeQIOWxmveqYTrE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Quww9GjPZYJwfxx/DXote/Ek//DMiq49PWm/Jx6OKRkIFcrbidZIKDtEVCmZUMMYm oLCNMxHfXLZ8YI5baAhWoMFb8wF31ceIuO+1U8BF4KoVRF6FVUCQhG/dBR8YMHX9Or 66pUofXCwKLXXsR2w9cHfcrW32sJLHiyupGREDuQGteK/3JJgToZLSKTwkRiev0GSQ zIExotK4wOJ66h2vulXH5372Avzh8Yv3slCp2qO40Xlqq+Qu9kw/Kz+dy5c7FtZMhx rFQ2TiXIeWywPCC1DLvKHITRmrBNXijSkxM+gZh64xH+kimIZjWpTaM1z13YxzWwad H3FeT5lyGwLxg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 11/21] btrfs/inode: allow idmapped permission iop Date: Mon, 19 Jul 2021 13:10:42 +0200 Message-Id: <20210719111052.1626299-12-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=991; h=from:subject; bh=UB3qI906TzplOANkGHMYQ0hDC06vJuELU9QDNAzTHgU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd129eGVWL4nt7QXNd0P+BNZI3YgyI3/6Mx167n8+38z Ly4t7ChlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZiIswLD/5y+q3oNFzMPWfyo+7L3TK Uje/vL+40O21c9Fzhg03mpei/DXxmN+JuVrKtvXX7rviyf4WXX0TyTfcmLXpb/k3xW6Wq1mRcA 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 --- /* v2 */ unchanged --- 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 53b038029440..a2a36a45998e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10274,7 +10274,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 Mon Jul 19 11:10: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: 12385555 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=-20.5 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 18B3BC6377A for ; Mon, 19 Jul 2021 11:11:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0708761164 for ; Mon, 19 Jul 2021 11:11:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236644AbhGSKau (ORCPT ); Mon, 19 Jul 2021 06:30:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:59576 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236565AbhGSKat (ORCPT ); Mon, 19 Jul 2021 06:30:49 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id C10B9611C1; Mon, 19 Jul 2021 11:11:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693089; bh=6pC1/Vg74n3UrLGPxr9y7KPh8T2eieOU7Tbw0Pqaa9w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p+kEU8CELUb+y5XamMZdUb2lLyXSZWk/iqhvgeCBtl5SY6t9aYRrLUkpIGIajg8yH HNfdJgNo8dnvmIiY07YKDrzQ+W9dfAuLsiYStTMPX/zkA8VJv2mlFXhGhoXLMwTqHg BY1/Y5G7U6zksyJwTq6cFc98vlophIdG0AkulBLLs8aWye745+nNWqF0oZaHh3EOHD OCDKf9nsHdPqVx8FPkcdct0wxrt+8HTIvy0nkNKBzSxjCiQTSxFRRkRiQTI2fgYHgx HV9+1Rarl5seka1pUviM5+JFZMhY7bFKcsUB/sg0qBN+K97Y1nuuo1F5GEJFTAx4Cp 05zTDLuOIZaaw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 12/21] btrfs/ioctl: check whether fs{g,u}id are mapped during subvolume creation Date: Mon, 19 Jul 2021 13:10:43 +0200 Message-Id: <20210719111052.1626299-13-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1639; h=from:subject; bh=h6XmDJ4Nl/4O15SVz//vkf2Wr/ioFFzGSyvbstNw14k=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd12wOnTYkOPf34dB0z0fJeqWy6vf/U9xJYtPm9dx+NK eS2+jlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgIn84mVkWPX0VEaqOUNd1r+edxufCi kuYpw9+2Nu8to/J9fM4T453ZXhf9XM0+timkvCbt1eIpv/R6Skmy38khi/004hwYP5RX/j2QA= 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 --- /* v2 */ unchanged --- fs/btrfs/ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0ba98e08a029..7a6a886df7c4 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 Mon Jul 19 11:10: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: 12385559 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=-20.5 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 5A8C5C07E9D for ; Mon, 19 Jul 2021 11:11:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 49BB561164 for ; Mon, 19 Jul 2021 11:11:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236638AbhGSKaw (ORCPT ); Mon, 19 Jul 2021 06:30:52 -0400 Received: from mail.kernel.org ([198.145.29.99]:59616 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236565AbhGSKav (ORCPT ); Mon, 19 Jul 2021 06:30:51 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 293F3610FB; Mon, 19 Jul 2021 11:11:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693091; bh=jrsTlB5aH/oGmwuzPWogVfh8GOFsiWt44Xhk1ytIt+s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uYKW8PgHDB1atflqo+MD1UpxMdoq53iRhiLT2N7vitaRh2ZLIAegL4kL7HUjcxNEE arlXvswDHDSJk81aozI41Dtvb7dex9vPk8cc/esCKqDGwazN5IV5GNS0vqp7kZXzOJ NxdjdTyfVbXEbI1JXG0et9aCqtulHQOcIgI4W4m6N6nRhBPB5LyknGxtzIJ6akYk4z BbaoqtN85AGsMFdDHfduKMDJAtA95t5Rx7DywW6T2TyVdAvJiu6JOt4fpJNkdAmvWx BeqxSTEWsYAHoAXeM4M4gBhDmGXioXYeCDxZa2eLE/WqCTtkRv+9utw6jPFfL9U8HN 9Q+c1SCW7lJQg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 13/21] btrfs/inode: allow idmapped BTRFS_IOC_{SNAP,SUBVOL}_CREATE{_V2} ioctl Date: Mon, 19 Jul 2021 13:10:44 +0200 Message-Id: <20210719111052.1626299-14-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8169; h=from:subject; bh=Qd/ossu19EBjuNoVKBILraaqsYqlGubb3Nx4OcwJeds=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd3mkcoqo632lyFBaWrF8VTr+XJ/wo+qm0x8ErLilKHA 8uk9HaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABP5e4nhn9mP5aF/yhe8cnDse7TK7k HMLInqA7xZsjujcsy+pbWGRjIyXL49o0gz1XLp5qWT1PZWy8dxSXX4WUUL3Prj1tFXxbKOGwA= 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 --- /* v2 */ unchanged --- fs/btrfs/ctree.h | 3 ++- fs/btrfs/inode.c | 5 +++-- fs/btrfs/ioctl.c | 48 ++++++++++++++++++++++++++++-------------------- 3 files changed, 33 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 a2a36a45998e..84f732b062dd 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8940,7 +8940,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; @@ -8951,7 +8952,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 7a6a886df7c4..be52891ba571 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,13 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(name, parent->dentry, namelen); + dentry = lookup_mapped_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 +925,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 +939,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 +969,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 +1798,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 +1826,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 +1841,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 +1875,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 +1935,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 Mon Jul 19 11:10:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385561 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=-20.5 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 366C6C12002 for ; Mon, 19 Jul 2021 11:11:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 21BB46115B for ; Mon, 19 Jul 2021 11:11:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236606AbhGSKaz (ORCPT ); Mon, 19 Jul 2021 06:30:55 -0400 Received: from mail.kernel.org ([198.145.29.99]:59654 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236654AbhGSKay (ORCPT ); Mon, 19 Jul 2021 06:30:54 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 788B16113C; Mon, 19 Jul 2021 11:11:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693094; bh=JVZJSiPoLcf87SE2d84C8rYEryh3+Fl8EOWxRg/TEGY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aJxMsHDikePiWPwqap2PQeNBRUevmgNA1C8iFiY2DuF4q19ymtyC3gLFU51o/V2IK Vi9C7R8cFen8m08Cdh5rPvYY+uE1LFQW38C5Q6ec42OHR9iumeRZkfwmHHHzpe20OQ cdfHGP4FpQLPaG4ZWaLceoZo34cCPr0Tf/FbRJx67rLtougqynljUYssTXgG/ofNTX /kREdBEW1OiDboacl/v5lRQKf2KU/e8stSKg590JXABCO9+cVvqG9qv/R1ITsDOPym N2Ll8p71/fRMd2g0YPt1XU43mRl3Iy6orJ9ycmv0mU5GPkIGi3dw9w86rJBD1/+af5 7fM4fVo1zgCNw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 14/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SNAP_DESTROY{_V2} ioctl Date: Mon, 19 Jul 2021 13:10:45 +0200 Message-Id: <20210719111052.1626299-15-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6664; h=from:subject; bh=+/84HNBCIA8BKlk1P0cojefgH3oJoGOQx1chGyd57I8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd2eervSbWnuTv3JQdsMPhx/vOvla/NFJa6PZ3Wvb3ne vzTbpKOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAisrMY/qcsZF/4IXd50uuqmwfnmM r//msr0dL0Ib74y7fwf/vWyxgyMrzX+vG9K9vnyRI5s+0HzRv19oYWVzmt3Fxo/aU87UJMJx8A 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 --- /* v2 */ unchanged --- fs/btrfs/ioctl.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index be52891ba571..5416b0c0ee7a 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; @@ -2915,6 +2916,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; @@ -2942,6 +2944,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; @@ -3026,7 +3040,8 @@ 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_mapped_one_len(mnt_userns, subvol_name, + parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; @@ -3068,14 +3083,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 Mon Jul 19 11:10:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385563 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=-20.5 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 C1234C636C9 for ; Mon, 19 Jul 2021 11:11:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A918361164 for ; Mon, 19 Jul 2021 11:11:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236663AbhGSKa5 (ORCPT ); Mon, 19 Jul 2021 06:30:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:59686 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236654AbhGSKa4 (ORCPT ); Mon, 19 Jul 2021 06:30:56 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E94D3600EF; Mon, 19 Jul 2021 11:11:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693096; bh=W4JJ6om8tM1CH4IzxxTR/gN+k25qFaj0o+dLFrivocM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V8Tej+zEhOLCgigoPvMlW/fYp6WIoksDsvlEzFInGn+AZAHBuQSpNDE8qw4OzWWBl 2QiT6hRiNMrXywIfNTwR0ejaUUvPtl3R6j/1r3LLCxw1skmgrLsMVVh8dCJXWUiRAf RkWtryZ4XE5JkiMLEhx3MOeTxNvLdL2FiCUkZ23mmvjW9Wy19w1eNEV3er+xksYrBN Ys8uNC+aEZLlzaJnyB3q8vOpDLNbuA/Htnl43PQWBixlFR52IIZT7l2D+IkrjUUEv9 ooFB4hzkGliiO5CFnb4YMo4FzCco417vAwgQvt2BuEZyYf8fdDTPSCIjtKf5ixt3G5 BlljiCAG1eaYQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 15/21] btrfs/ioctl: relax restrictions for BTRFS_IOC_SNAP_DESTROY_V2 with subvolids Date: Mon, 19 Jul 2021 13:10:46 +0200 Message-Id: <20210719111052.1626299-16-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2817; h=from:subject; bh=qIbCHFEYDIr911mGBYy0Iivr1qf9vv7admrv3auwOsM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd2eeKnxM3/c86XezJK90uu3yHKfT6l1TWN7cXZaU256 WlJcRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwES2STMyHK/N5spbmX/ySrbKhIkx03 SXWgdV/P/ANr0vNXSyf+sLd4a/Arymy9wW+Ad/+jpvfoueZvGFlJzkD4n6h/dXCb4yVn7CCwA= 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 --- /* v2 */ unchanged --- 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 5416b0c0ee7a..72045ae30d1c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2944,17 +2944,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; @@ -2992,6 +2982,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); /* @@ -3002,6 +2993,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 Mon Jul 19 11:10:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385565 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=-20.5 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 48115C07E9B for ; Mon, 19 Jul 2021 11:11:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2D78A6115B for ; Mon, 19 Jul 2021 11:11:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236576AbhGSKbA (ORCPT ); Mon, 19 Jul 2021 06:31:00 -0400 Received: from mail.kernel.org ([198.145.29.99]:59724 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236654AbhGSKa7 (ORCPT ); Mon, 19 Jul 2021 06:30:59 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5AD7E610FB; Mon, 19 Jul 2021 11:11:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693099; bh=CIhc9aZbuMCh85zSIZOctJcasDvhpNAFVIEeIzjvFM4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=c2QjkonLbxBHNBePVoAulYaxswlqHW7rpg2x6TzAYb9yJKpkXM1m2jYvg4QemPuxb MQjU+7rZu/G9PfE4v2+ptyNAcNgLA8BKqC7mwIv8TNDdOzrHHk9LehYUwMfbyaV4Pl d899j4l0U1uAGo/ukh++7d9euMSNZF/70xmE7lQiih5q9YoNUFzYleUFw4dmCPmI93 PR6arEl237r2+e9p3FOe4h0FRLNN6Vrt4MxbnAmTfkaeJzWAewiFWnwqTGdyfQVuQ0 mwjJf9YuvNGIYDs8CTJZ3lUE9rz45mrhBPcyY18XxsEN6R78u/GtwYDtnCegJvDgKk LS0T8KnbHMo+A== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 16/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SET_RECEIVED_SUBVOL{_32} ioctl Date: Mon, 19 Jul 2021 13:10:47 +0200 Message-Id: <20210719111052.1626299-17-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2127; h=from:subject; bh=UOz8ZUGGvks1eZvm8DVLFZP4fMG0s8EHx74ivt0aUpo=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd1+aTtPj/u71739pz7ca8w32sngtWyGxr7ZiZGZoQ3L W6X2dJSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenANzkM4wME26qlpW/f/li/Xdze74tyR Wpc3bbde+tiU3jiI+ec+/CRkaGzYcOJa8Pk3+y7+/z++u7vp9xeSbl3ZSbUu67TdO1WGYDEwA= 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 --- /* v2 */ unchanged --- 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 72045ae30d1c..d631a1cb621d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4466,6 +4466,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); @@ -4477,7 +4478,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); @@ -4582,7 +4583,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; @@ -4616,7 +4617,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 Mon Jul 19 11:10:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385567 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=-20.5 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 41AB9C12002 for ; Mon, 19 Jul 2021 11:11:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 27DCE6113C for ; Mon, 19 Jul 2021 11:11:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236678AbhGSKbD (ORCPT ); Mon, 19 Jul 2021 06:31:03 -0400 Received: from mail.kernel.org ([198.145.29.99]:59756 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236405AbhGSKbC (ORCPT ); Mon, 19 Jul 2021 06:31:02 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 13C4A600EF; Mon, 19 Jul 2021 11:11:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693102; bh=ccO7bD571pZuz4XLMjKf9TMYDBuf0k09Wn8M9r4fG/I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I+eOoaQ0KS4FLSgOyOjDeaWUaPtxpmeHt5oP0GkD6s3IEhA6zZ7fBq6SdRD7vVkOd SkREoj9vMC37iJfWuzYCN4iTkbT8jGTZZewW9o5r4Z92xieOAQokYxjMyLTLbXcKvR 9gWrZd9fC3IAlW7fTHHGdcMlClsky8DfHdkKojC9LD1BP7UJGdk8xNDZH2RXvweAJC 2q0sH6kPPwgQpOzOLJhgeQBXgITM+1H+Y8VrD5DraAyTK6+ESzYG31vdj12GZz0swq qjPL5r1ot7NN45/PSdJsnVcqP/lb0Sz92hXC1YK2aOfaIEPQjkd3VnykrtV8HIuZl2 TsQ/CwrdPK04w== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 17/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SUBVOL_SETFLAGS ioctl Date: Mon, 19 Jul 2021 13:10:48 +0200 Message-Id: <20210719111052.1626299-18-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1270; h=from:subject; bh=wBBRnXynCMgW/zQ4GNupvoxQkjtO0Uw0TI/aFXGcBJ8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd2xVZNDTiupT4MtYrlbrnHBlZ6ZqeZnPqrnaNvOVEv5 8DGpo5SFQYyLQVZMkcWh3SRcbjlPxWajTA2YOaxMIEMYuDgFYCIcnowMGzr+H1R8uXajQ1eOqc/6R+ 38qRGM50/+0+Y8erGmJsEkiZHh3uq9UwWeBGc+0rnacH7VDLWnLgrcJ9Y3b+64ILP5/85ERgA= 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 --- /* v2 */ unchanged --- 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 d631a1cb621d..73a477ead145 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1982,7 +1982,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 Mon Jul 19 11:10:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385569 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=-20.5 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 27B80C636C9 for ; Mon, 19 Jul 2021 11:11:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 10E7C6108B for ; Mon, 19 Jul 2021 11:11:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236654AbhGSKbF (ORCPT ); Mon, 19 Jul 2021 06:31:05 -0400 Received: from mail.kernel.org ([198.145.29.99]:59790 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236405AbhGSKbE (ORCPT ); Mon, 19 Jul 2021 06:31:04 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 8495F610FB; Mon, 19 Jul 2021 11:11:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693104; bh=cFQdjzcLKe2a9GkeLcL6M18xT9nUijcqOzEruQE+HvA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XV0UZuzizgwLvohINyUigaZ0n3pWtFnkR8R2u8rffcKRXj7KLjFCAQGOa5S/Vfr2A VQ+h5sxvlDDWBo1a0YuUz869U54N8339C4l7uol2+Gs6hmT+filHYoRBAExqAu5PYz 1+0xuONMNw5kip4T0UrVQzuh3gtI5ehJ+2ZCJrpY4WWpylI2eWcjLaK2qvGqB0Mkk4 Cn9DpIg+Xrn87xOK7V2FMmcIEvUomCV1dbGopif97eqhWWd+rbbQOPmNsjOyreXqj8 We3+DWZOBHJdz39r5NEliNQkCprEt61mIw6Hn+W/0akokneS9t1OkkvJ5NplCO7fV3 /irQ99mi3AW2g== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 18/21] btrfs/ioctl: allow idmapped BTRFS_IOC_INO_LOOKUP_USER ioctl Date: Mon, 19 Jul 2021 13:10:49 +0200 Message-Id: <20210719111052.1626299-19-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3763; h=from:subject; bh=ulWrQVOrk9On280Ag/BL7PrE+pRWDj+/AiV+haHRk0Q=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd3B4ruR5Tnzk18RbIsn9bvl6SwMztz76ue9eLVvu7VP +c7801HKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjARJStGhkbTiRNu//Rbd6/S2ylY/Y jiL56uUoUXC4KLC6wm1NSGTWf4zfr5YoMYB9O9vH2aQa7Fifkq+y39ZybPv+iSu/Pl1l4DBgA= 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 --- /* v2 */ unchanged --- 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 73a477ead145..c96037d15bf7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2440,7 +2440,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; @@ -2538,7 +2539,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) { @@ -2680,7 +2681,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 Mon Jul 19 11:10:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385571 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=-20.5 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 2B769C636CE for ; Mon, 19 Jul 2021 11:11:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0F0A0610F7 for ; Mon, 19 Jul 2021 11:11:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236582AbhGSKbH (ORCPT ); Mon, 19 Jul 2021 06:31:07 -0400 Received: from mail.kernel.org ([198.145.29.99]:59808 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236330AbhGSKbG (ORCPT ); Mon, 19 Jul 2021 06:31:06 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D807A6112D; Mon, 19 Jul 2021 11:11:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693106; bh=0TmMjcEfKfibdqPXhaB5QEBiiAD9Ql0JoNhIe5rmfYg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AgViBWhqosDBjg6HibSlSb8Z33IK+PtN7hfjMJRQ5KeArQyrAV97BhZkJoBIOUu4P wpVeNSZMTLfdnFODT3RJywKvR6kX6FIo8wlhsLvNm+z8tXy1E0nywRxt+9zSlXzUYw 8hFqLtgfIemocQlrq+qsadrKEVqwhV5wbvozPN582jsfAGNw0YOToJvYbVUHZXUUVB BVoZGRYEFzSLD2yxKbVFjafV//cGG7KymRDTetGGjGqHoiwi7YCZMX2sh/joqPtOt4 tjIdqGrcWNdDUGzwDMKjcm8EWsknOlJThYHlNnxyQakLBaapan1h+gG4yvaIMMUR5i iv97C6LFiOkfQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 19/21] btrfs/acl: handle idmapped mounts Date: Mon, 19 Jul 2021 13:10:50 +0200 Message-Id: <20210719111052.1626299-20-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2735; h=from:subject; bh=V86Ab3ogartsC8z1JJG1eyNEGTGDR8DjCud4mBGgT6k=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd2xs/juzw35G79MvLe0LiR2Br9E6kqFMI7fTR9erny1 5CDfnI5SFgYxLgZZMUUWh3aTcLnlPBWbjTI1YOawMoEMYeDiFICJrNBl+Ke/+D7DvxzFKdtvtFcaLJ SOtvpxV1PfI9O3Jb+ZX3BRginDf7fdzk3Rrne8TT1/+ivN09umO2Pi9l9n0r9VzZOUZNNS5AAA 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 --- /* v2 */ unchanged --- fs/btrfs/acl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index d95eb5c8cb37..c9f9789e828f 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; @@ -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 Mon Jul 19 11:10:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385573 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=-20.5 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 77E26C07E9B for ; Mon, 19 Jul 2021 11:11:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 61889610FB for ; Mon, 19 Jul 2021 11:11:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236693AbhGSKbJ (ORCPT ); Mon, 19 Jul 2021 06:31:09 -0400 Received: from mail.kernel.org ([198.145.29.99]:59826 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236330AbhGSKbJ (ORCPT ); Mon, 19 Jul 2021 06:31:09 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 362906108B; Mon, 19 Jul 2021 11:11:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693109; bh=PTaGzLX7fN0h31uKp1jMHT/xI+goUxgq9qJg6/EfR3A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bBvkRLs530p6A8j5sUUq7RZYBs0Yij/jE33x9MWkx7+6XeXFOJs5sslE8XZ4W2Ays OKlegYlHzxfyL49scGg9YWv+4A9Y2R0ct6k8yGx5Tr5bx5YZZWNDscdzE1z2i3syo1 FppiIetqjIN5Ef6b7kEfqAm1hIoCmQoATZ2x5tnUPQAmYFemrUA/pASTfE3exMT8xA IL1CeaLXGQc9CMWmBR6jAR9zEHvUMGYOqZzUdNwuCzmdnUiQPHMCy8/J5VHTjCRBN0 MagnrT2f4WJXdRxP7wTpaMkywyfiYjA1Zk2THaSJp7djwOOWo8q6ip6KdLFR3Qt6jF 1s1NgYnSmnZYw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v2 20/21] btrfs/super: allow idmapped btrfs Date: Mon, 19 Jul 2021 13:10:51 +0200 Message-Id: <20210719111052.1626299-21-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1491; h=from:subject; bh=MOSTOzkgOTVD0G5k35yE//SJXfgr9c6/6BdDJfFuedE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd1xZVVm4R6ZukNLNrrclHp991u/mVDbAz77jitXlZ3e Tq2701HKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRI24M/4yW8/e+rPtoGLjitMKUM3 GrukISRK9kvdn4SiN76slNV/UYGTY9FY6uz3D5w3yl8sDx5v9r2yZNjUr9NG0Nbwx7x/zV6qwA 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 --- /* v2 */ unchanged --- 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 Mon Jul 19 11:10:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12385575 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=-20.5 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 CAD1FC07E9D for ; Mon, 19 Jul 2021 11:11:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A0204610FB for ; Mon, 19 Jul 2021 11:11:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236510AbhGSKbN (ORCPT ); Mon, 19 Jul 2021 06:31:13 -0400 Received: from mail.kernel.org ([198.145.29.99]:59852 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236330AbhGSKbN (ORCPT ); Mon, 19 Jul 2021 06:31:13 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 8ECF0600EF; Mon, 19 Jul 2021 11:11:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626693113; bh=uSZngAfwn+8N9rDZ0xx4R4dycFDd2+L24fywkwWKXzY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hI1wwr8EPileWFpBlADzpsLQ7arnFvD5vLZmfEnJUd5BPK0nf984lP0VaPcf7kBEo xooujRBaK3rjryGlLgt78C7MOO0axSNDmKknn0T5WxG7XvBNV02KbXTM9QedI7hE/H D2UkLZximLozcPo6Y6hn9ITRQIdLKqUymxT0FLctOW4cTFP04l3D4dOWaeob1N4ru/ +a/WOR/j7oDh/haJiQG8ICgdGwSUAxrjYsvTvQQF616MRwOLOvhRmwoVmhKD/NR07D 9aLf0Jtn1Ld1hoS9u8gVOduZHMGzgbR/g+WdlR45DG11JbFvdP62A0r+fpH0T6bT7B gp8+y+nCRa7Fw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba , Al Viro Cc: linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , fstests@vger.kernel.org Subject: [PATCH v2 21/21] btrfs/242: introduce btrfs specific idmapped mounts tests Date: Mon, 19 Jul 2021 13:10:52 +0200 Message-Id: <20210719111052.1626299-22-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210719111052.1626299-1-brauner@kernel.org> References: <20210719111052.1626299-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=124071; h=from:subject; bh=wNYhBJQ30iuK8om89S8QMKDOwTAeGzHQ33sXONcjIYc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSR8jd0psH7LD4u/jmpfBIV/sF6al5w7VWC/y6rXk6sjeao2 FzqFd5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzkwllGhrtpvNr9t5my17XNLzvq23 zWSNRStuu+KdM8c7WNb06/YmRkePhbKPpo55NAMc+3hY863mdttcmRL1+esc+98eyEJ00fOAA= 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