From patchwork Tue Jul 27 10:48: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: 12402465 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D79D9C4320A for ; Tue, 27 Jul 2021 10:50:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ACCFB61220 for ; Tue, 27 Jul 2021 10:50:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236383AbhG0Kua (ORCPT ); Tue, 27 Jul 2021 06:50:30 -0400 Received: from mail.kernel.org ([198.145.29.99]:55674 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236169AbhG0Ku3 (ORCPT ); Tue, 27 Jul 2021 06:50:29 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id B218E6135A; Tue, 27 Jul 2021 10:50:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383029; bh=DXeBxntle03LxjR/sp5v3vkSocLKm4g6taMWkMmXodk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X7HJVWbvk0tBSl8ovUoqxoyqWSPboLkyGdhI4Lc7vI6ktormTVwmUu3HmUdrLJClB F1NoaV1K4m4hsQqCvMCjHQ2grR/lxzo09v0OA8P3wFWmhOqgrkNDcd/eCpu1TbDoYm OGA7XwY/Qm1yLjhg8h7yr0VRkp0G02d4n8yNW5heFOmB6R890hqLTkBeYJsEnqm3D0 O5Wf39IYzjJagm+U4kiFP53Xnyb5x+fqFTVYEstF1zcgNGfIQQvGHKQZobHZqQkh+Q gi+mLtZtIyjf5lPDroTbyrtZd79KR1WSHm5N2eE4JGyiiJQRH41bmq4dwhhtv9aC3X OTSRmELwyRluA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , Matthew Wilcox , linux-fsdevel@vger.kernel.org Subject: [PATCH v4 01/21] namei: add mapping aware lookup helper Date: Tue, 27 Jul 2021 12:48:40 +0200 Message-Id: <20210727104900.829215-2-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5026; h=from:subject; bh=oa4AcwY1bGhWgVrnncnHqGiwwPocGdhs0DBLxErCIjY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJRhpvrbeTEu9HZE7WLzV03Hw/pWTz1qLmlwcfCiINb Z7kxdpSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAExENpSRYePHRX99a1Nm+gV729smBd lfrpiocebn9E5HlalzTdIOXGNk+Jbffbfktv/Bzesk/77tO3tRO7N+ywdHUYGfrzXfqv8tYgAA 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: Matthew Wilcox (Oracle) Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner Reviewed-by: Christoph Hellwig --- /* v2 */ - Al Viro : - Add a new lookup helper instead of changing the old ones. /* v3 */ unchanged /* v4 */ - Christoph Hellwig , Matthew Wilcox (Oracle) : - Simplify the name of lookup_mapped_one_len() to either lookup_one_mapped() or lookup_one(). The *_len() prefix in all those helper apparently just exists for historical reasons because we used to have lookup_dentry(), then grew lookup_one(), and then grew lookup_one_len(). --- fs/namei.c | 43 +++++++++++++++++++++++++++++++++++++------ include/linux/namei.h | 2 ++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..902df46e7dd3 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_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_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_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2664,6 +2665,36 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) } EXPORT_SYMBOL(lookup_one_len); +/** + * lookup_one - 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_one(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_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_one); + /** * lookup_one_len_unlocked - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2683,7 +2714,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_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..df106b6d3cc5 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_one(struct user_namespace *, const char *, + struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); From patchwork Tue Jul 27 10:48: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: 12402467 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 9A11FC4338F for ; Tue, 27 Jul 2021 10:50:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7D9BB61378 for ; Tue, 27 Jul 2021 10:50:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236388AbhG0Kue (ORCPT ); Tue, 27 Jul 2021 06:50:34 -0400 Received: from mail.kernel.org ([198.145.29.99]:55708 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236169AbhG0Kuc (ORCPT ); Tue, 27 Jul 2021 06:50:32 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5FD2A61378; Tue, 27 Jul 2021 10:50:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383032; bh=3s9m2M0zO9BpXEPa838DCG0One5d1J/jUFNCpaKdbXY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IMR/2+Xv+w0Elt/y4lyVMeHQasLk4z7ia6Vc9so74XBUr5zhHVGhXIlDYdJWrs4OJ BkOb18GC97J77W+f7BJ4dlgIlaZUYJ42KvNKt/kVDf4WYJAD2u2xgCzNnM+vnFnAAs qQ/JrfofYbzPl/1DngNmGhVDG3s9rTFLn43Cp1vFiTeIkCVlez/0aZgjdTMeg6mh/Z ptMcwX0zvjY+VD3YN9wuamq79YcOw2phrGGdxcPnbdhh2YKWGeN1f7agBZDU+ECrbv MSrD8b8QfPa2X3ZupeOVpVGW0jECAuvKLI7MSU2hE5cho6AM+z2XKVu9gji6NtufWC WLFgAW/FOMNqg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 02/21] btrfs/inode: handle idmaps in btrfs_new_inode() Date: Tue, 27 Jul 2021 12:48:41 +0200 Message-Id: <20210727104900.829215-3-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4730; h=from:subject; bh=HARpXJ+DflNJss2SNmEgF1SQiC1Os3fqzbxgRo2Yak4=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzIp+qVYiWfAjkV8183M+HRi38cfbXz8tPfotY/nOsMe Tjfo6ihlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZjI+zUMv5hEmtzKN5kWH26Sdp3/7W h9QMKJ/oLls70PPPXs2vR58yOGf8Y8sf4bZmy1vJTuVumvFdb9u/tG+ikZznm7uhTazrz8zQIA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 0117d867ecf8..ae4fe9377e6f 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 Tue Jul 27 10:48: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: 12402469 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 3569BC4320A for ; Tue, 27 Jul 2021 10:50:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 11B216127B for ; Tue, 27 Jul 2021 10:50:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236392AbhG0Kuf (ORCPT ); Tue, 27 Jul 2021 06:50:35 -0400 Received: from mail.kernel.org ([198.145.29.99]:55732 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236387AbhG0Kue (ORCPT ); Tue, 27 Jul 2021 06:50:34 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E74EF611C5; Tue, 27 Jul 2021 10:50:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383034; bh=11ihsyzvFfW71UEouNoTSFiSLps04UkgtrM3/mDponQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vM+22EJkqKilYZxcIPgF0xXvHrAw8wSJNGkDLLgQDREy8iuKUunhdWDdpI6HWzg4V Ri7He06M7XJKUl5Gonniuv/QjvvPC0t1VIJ/3ZRIoMT4huSHRQWzuN3IiM6kYizc5q JfbP70S/5AAo0u6rQSVAj5+VhaIalXxuH8JvF2s5jp+GG4rB1VXL8L/VwPLT2tfiOd EqeJaE2tQhDKfg0cT9J/HLYER5qW0vPO3vWBPB6M2sTPkzLh8BSCjpTxIp2I5YOMhD TI3DbBNyek6YsVcu2+wVwSxFQPEhC9qUHaLd3IbGma6MS4Ry53oSMAGrZBLP7uBToK 260LZHOFqbQoQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 03/21] btrfs/inode: allow idmapped rename iop Date: Tue, 27 Jul 2021 12:48:42 +0200 Message-Id: <20210727104900.829215-4-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2705; h=from:subject; bh=emgPe/VOONUQcqGmZ3IuRcQkaxIqFeFuCR0vAIc/cgA=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzLp6eppFipfc89KpzkuOzg3fcmFU2b3eFce7ujfvm26 +9HjNzpKWRjEuBhkxRRZHNpNwuWW81RsNsrUgJnDygQyhIGLUwAmkrOO4X+I3ye/IOOV37ZOmTRr15 53NnfMVLet+THnxaIT0U8LpCXNGBl6d7ewzS7j35ul0Tf1S7fJ7HsVQQdWep3aLffgQQu31F1WAA== 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 ae4fe9377e6f..ddb60462f5f0 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 Tue Jul 27 10:48: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: 12402471 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 EAF1CC4338F for ; Tue, 27 Jul 2021 10:50:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CF93261378 for ; Tue, 27 Jul 2021 10:50:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236399AbhG0Kuh (ORCPT ); Tue, 27 Jul 2021 06:50:37 -0400 Received: from mail.kernel.org ([198.145.29.99]:55770 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236387AbhG0Kuh (ORCPT ); Tue, 27 Jul 2021 06:50:37 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6392B61220; Tue, 27 Jul 2021 10:50:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383037; bh=lsrOjRk2ULaBez7/7RkgcdIM+9HOAemrUoy01OqIqPk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BP7Ha7GhunPuwr72yjvVglgZDxB9TdP0lFmKTfErq9dyHoIAdqotOXLQzPWNpDL/n 4cNmESAWsXcMjfEhn99btbch37XgoFTu02SmZB//olyvU9Mc48In6/D9YDtKaBGpiI jnVTehEhgs0PAUoEv+odWEQ7Ew2lGzjYlaV/GE9wTnMRciglYd5mxqMyAL2QvnxJzI FxNiLJjAAwOkqmAI3fm3xq3Fxw4xMEh3rM62ddlPGy4ur17pQUl4W0qpUdGufnkJHl V622omYt5JY2wV/F14p5uLhwbyyqqr+JlsrP1valfrHLUX43aAMceIA2Hl0eB7sHj4 o5t1IKNAB6WEw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 04/21] btrfs/inode: allow idmapped getattr iop Date: Tue, 27 Jul 2021 12:48:43 +0200 Message-Id: <20210727104900.829215-5-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1041; h=from:subject; bh=knsOklAN7VAasC8TC+bYMG37et4GTZWW3cNbMQoEYsU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJJ6nNi/m4Xi2Kfzys3nYs/andwSkl/ysFnCmcWvtb4 38q3o6OUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAiU6oY/vDVbWhaNFFDPMpNW7dRb9 4la9cHq070BEpvl7r/eUpJQgcjw0SjpSlN7VZ3lU4d90zNeCG5fIbdcSeOq/u9bpX3LDXOYgYA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 ddb60462f5f0..e6cc2b155b7e 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 Tue Jul 27 10:48: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: 12402473 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 4372BC4338F for ; Tue, 27 Jul 2021 10:50:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 269816127A for ; Tue, 27 Jul 2021 10:50:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236371AbhG0Kul (ORCPT ); Tue, 27 Jul 2021 06:50:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:55798 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236288AbhG0Kuk (ORCPT ); Tue, 27 Jul 2021 06:50:40 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E4F956127B; Tue, 27 Jul 2021 10:50:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383040; bh=/i5vJIpPu8ukaqj/aX/I/eRmg1JJj+YZ/WL/9zSFiv8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=T4WcuiYm+sPRjqNGgLV2Ym77cM5f6/k+PlNqj9pnTtrm+X8qmT0IIBaQ1Id4jesxz ryXhEEYYXxbGQ3bDf3ZsxGbXipXK/+E6HY3wEGuOS1vBb5O/g0bE3GDaZhk93Sn8vT POM/5mkxpGKCXt7cT67u7syfioIA6wsL1SnsqiYG94XGVs1XnxG75Qu7VMRl8JCpw5 /bWX2HeNjM1Kge0fkjiP/WbhbFZzzidnwq2btnv9Mp4Lc9Z0l4R0An+JVQLkdz56Up ihl7Mo8YX72A0u5LsVztiNzDpqiXx6zDbqPY04wiZhjodGQLWjm+IkSzuxfbcVe1m/ h6bnpQx5JX0Fg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 05/21] btrfs/inode: allow idmapped mknod iop Date: Tue, 27 Jul 2021 12:48:44 +0200 Message-Id: <20210727104900.829215-6-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1090; h=from:subject; bh=LFsY+R5040o95Dc/o+RtcKiCtko40CMHwbhP8rIKUdY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJ5tff+uJIdonvnT81j1n1ou+ye7+yDtgpNr6fMF0qP 4iko7ChlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZjIjniG/7G7lqVYhr1nPGCm0XjsQt hzE4lY9hzWrLTLc47uVNX5EcTIsPH3d4sdDQnrJByvmjUosPCsSfTZZ7vAUHrxZH6pDTsk2QA= 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 e6cc2b155b7e..95068a7dd9c8 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 Tue Jul 27 10:48: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: 12402475 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 93BF3C432BE for ; Tue, 27 Jul 2021 10:50:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7DEE26152B for ; Tue, 27 Jul 2021 10:50:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236386AbhG0Kup (ORCPT ); Tue, 27 Jul 2021 06:50:45 -0400 Received: from mail.kernel.org ([198.145.29.99]:55834 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236227AbhG0Kum (ORCPT ); Tue, 27 Jul 2021 06:50:42 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 545C7611C5; Tue, 27 Jul 2021 10:50:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383043; bh=tykS0pNTe28EHku2KT+uX8JjxtrLcHfmZ/1zjva7qPE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ozxtQszXNgkYPA+hdvhTbHZu3KbkU0/xA0Vk+CZARidWMGaMahxGjP+YmxP3Wr4gU w3zdjdsAZe8oY43op3rVZFqkCdamTD5wyR3UerEziY/AZUjNiWrqFVvjC+FcvkXH5P SeZm1WQXwotiGSJ969WuDXiesXHmxWFzWYXn2fiRYiecbqYh4D4kQBMNqn4+OPa72j U3Kpo9lJEdug0fcCpV/RqF1arKFEaB8RjugHIILv4Qk/zQlVYf4E6G9Q7R7QbJrhEo hG0EnuZRumXhFXppRaaJROrwzM//LeZjb2ITz5AG7i6mqvDDeFq3oYPf4XK2QYqlDE l/kUbMXFnwWmQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 06/21] btrfs/inode: allow idmapped create iop Date: Tue, 27 Jul 2021 12:48:45 +0200 Message-Id: <20210727104900.829215-7-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1092; h=from:subject; bh=BT9Cf+qB2KXe6FKBUwilUEsXMNwVTCmukMKssLxXn/8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJ5x/7sa5u0Fb9LBU4PWJN1+/zZuXlHljrPWdlyrnrt TedJ6zpKWRjEuBhkxRRZHNpNwuWW81RsNsrUgJnDygQyhIGLUwAm8uQow39/p6Uv+Bo5L/pYFO1d7H tzbU3WIpZ8s/wnwvus/wfF8r1l+F/TFrXAS7FFfcZd1caoY1texJT5yTLvS7A8vXFJ46TlWbwA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 95068a7dd9c8..ffbb995de590 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 Tue Jul 27 10:48: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: 12402477 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 8501DC4338F for ; Tue, 27 Jul 2021 10:50:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6CD1C61378 for ; Tue, 27 Jul 2021 10:50:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236403AbhG0Kuq (ORCPT ); Tue, 27 Jul 2021 06:50:46 -0400 Received: from mail.kernel.org ([198.145.29.99]:55878 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236394AbhG0Kup (ORCPT ); Tue, 27 Jul 2021 06:50:45 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id C55976135A; Tue, 27 Jul 2021 10:50:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383045; bh=JlWbVPDY7Emg9MPciE0A72YP7Zxo6ca/9CS/YxtNWcE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ISHhUbw6ShGuzElWuOHZpW6HnG0AEjWR67LDBNRRfZhjAOQKWp0jX08kFPlKTMud8 RAMWW7hbnGAaYWqlSPwc3coDDERb5Q8z7E2fskbNP+ruf6nJ2l/eFqYi/fAD3hDge+ ZzfWZaxmZeojK2tHbY+qS67sisG2Q1+a1l/nvERAyP8euwHyCuSEvRDb1fFw4wiUsc tJ6tqZkM7fm2FEqe5egElWI9bMu4rfO+PRqmsB4wJcJKoUeyWZ9pV2MM/+JKV5Fk1f P3RohXqc2dPgb+iHhNnyom4Ikxn92wppBqSTsv4rPuVZ5pGH2kvvi8AVTl8gmduM9K esczo4hHUR1VQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 07/21] btrfs/inode: allow idmapped mkdir iop Date: Tue, 27 Jul 2021 12:48:46 +0200 Message-Id: <20210727104900.829215-8-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1079; h=from:subject; bh=PQ4iDIDeI5TxtGz3xTVWKJXns4+ldNh8A1UA4gyrxBk=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzLZ2nDfU5aWK1Ov/1m5YbP6O9HaOezFR8QSU0Nf7xB/ 8NE1rKOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAi0zUY/ukoft0/ZcrpBpv9KVEn8k uLjv15aNprcXkb5ze+owcjdXYw/C/hmLbSOfyJ66QHPS8PlKesT9ygVjTz0AkVNc0XkimCMhwA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 ffbb995de590..bb50e21a4569 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 Tue Jul 27 10:48: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: 12402479 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 4C747C4338F for ; Tue, 27 Jul 2021 10:50:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3601561220 for ; Tue, 27 Jul 2021 10:50:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236411AbhG0Kus (ORCPT ); Tue, 27 Jul 2021 06:50:48 -0400 Received: from mail.kernel.org ([198.145.29.99]:55906 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236293AbhG0Kur (ORCPT ); Tue, 27 Jul 2021 06:50:47 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 107B56127A; Tue, 27 Jul 2021 10:50:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383048; bh=yBGEZWAFbvq0+wr0cNMlYxsgRhz7Sxt0c6lFc4zuToM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sJXjAsR8uXw6tLjVLBX0edW5aN0brd6pR2TAjFbKlMLONAkdf3fe1zAd5vqQmGn07 lCJSIyhCF3qh98532sGuRV5yMPHcxy0C9N3T+3GX+NMVaGHtFR75LmCrfNVbwGabrV /GCV8o2s+dTLYbE7HADUnaYs8A/CQVtTuUQvr4xgPyrD3Vlc1INcz02ylfqHeuqOP/ essScixhfPp/4SRxA1zAOLptLAG+0FOVRVx7zZNZpB1Dxg1sxP49h7HqzD2di2vqxe dSctc/oGKUbehdIwXj2WRx4BwIq3SfytFpDHgCijSfc1rByMMSOTbZwTjCWnm91IUZ Iek4JHcPyn7Iw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 08/21] btrfs/inode: allow idmapped symlink iop Date: Tue, 27 Jul 2021 12:48:47 +0200 Message-Id: <20210727104900.829215-9-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1093; h=from:subject; bh=cMU5ah3suWizVZyyWlmmd+ojZq9IfXsaiH0ENqOcKew=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJZ5Aen3NHM08+P7Td5t//oSq+23ODIE/orhX4lLzx1 iPlec0cpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBELnsy/E+aeyV0va/mrrhnJc9Nsx oW7osuDLv9dOlW472ba2v0H11k+O93Ln+RhK2byeJfc6+Jb9W1K5GanSTJfO3omgJfk0Nz7VgB 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 bb50e21a4569..5c133280375e 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 Tue Jul 27 10:48: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: 12402481 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 0ABE2C432BE for ; Tue, 27 Jul 2021 10:50:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E700A6135A for ; Tue, 27 Jul 2021 10:50:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236419AbhG0Kuu (ORCPT ); Tue, 27 Jul 2021 06:50:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:55938 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236352AbhG0Kuu (ORCPT ); Tue, 27 Jul 2021 06:50:50 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 95ED5611C5; Tue, 27 Jul 2021 10:50:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383050; bh=RjA1AM8aoqGKhEoIRT6DcHWRTUYoXxPddzNVH9rYXTE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i0nUl/piU1/TnSB1Vlih/6XsyMvKectqsCetz2RWucLg25hIphspO1OBKsIv38jEh touJQzxmphs8TjXpyP9Ld5Pg3Y6WyEC+0vE04RrnoBeKg1ZTd+X6yCmjr60rC/3gK+ Lz752upiByo1nT1iKgvRLdIC6mv2ObIKOLOOwaufPG7JSL07E5/3VAiIkxmAIBhHZC u9kDgO3w3sEteb0ZXO0GnUuC1yZ4Ib5X6v8CX5Mzg+yZ2nicGZUt7VeKGqtIxMUmj7 tpTSf+DFaeIh5Uis3nOkBOUyfYmyCahUgz1qKDwaiNgOdhIcPIt0gYetlYzdvdlZUt q397AMHoe/Cvg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 09/21] btrfs/inode: allow idmapped tmpfile iop Date: Tue, 27 Jul 2021 12:48:48 +0200 Message-Id: <20210727104900.829215-10-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1087; h=from:subject; bh=37UYKbN/aBYXSXKbFV1SSnL0HVJkQAKLFWrm8zEmXVM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzIl1Gfbum9LuRw+Lv/y63umw6OiCSELP8433eU/66jr uydxuh2lLAxiXAyyYoosDu0m4XLLeSo2G2VqwMxhZQIZwsDFKQATkStmZOgMzMxye1dwX83xOnuV4K PvSVGcEwp/Oh18Yz9N/nXb7CKG/7GbNz6S53PNvl9Y/WkD15noI5fiLmX6e5592r7hbcn8Vh4A 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 5c133280375e..0a45e3152016 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 Tue Jul 27 10:48: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: 12402483 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 E7DE5C432BE for ; Tue, 27 Jul 2021 10:50:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C9B0E60238 for ; Tue, 27 Jul 2021 10:50:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236428AbhG0Kuy (ORCPT ); Tue, 27 Jul 2021 06:50:54 -0400 Received: from mail.kernel.org ([198.145.29.99]:55986 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236221AbhG0Kuw (ORCPT ); Tue, 27 Jul 2021 06:50:52 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 12A5261220; Tue, 27 Jul 2021 10:50:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383053; bh=MwpoHXsjwIzjqq6MD7XpqH6Fj/JBKVG/oSpwj7/Y34I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TTBQ/YBWGBu9YVPEjKT0pkYP5ywy0ISfWYCGBBe0uFmDHu457U7OLxRDnOYu1E855 /n8EfosXvcmK5kdub2Ob83YgO3+gdcVW4PaglM5jZJcpzQ396YVEEbe2yDuFq8nLcD WlyG5+BwHzcfUGaL7lA/Ycb+qE1e8jnXcRJa8rVdEO8GX6L7eGt7itFCKCmWis/uR0 tyJjMeatUqjkfWlEQ2S1D5tXECMtF/nbq1uu5WCADXzH2qYyvkFjydcD9mlWhiT/FT la/OyGVH977aa4qIEUBYZ6A2JHmTUlHaxXsBv7dQB+Mm+vElHFJ+PIZJWK1o7a8GEH du9Xsv8qEVo4g== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 10/21] btrfs/inode: allow idmapped setattr iop Date: Tue, 27 Jul 2021 12:48:49 +0200 Message-Id: <20210727104900.829215-11-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1492; h=from:subject; bh=3+zZuX1Hq6Qu7bCR0H+pKUUhTQ0C1HBYfr5glA7aA8Y=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzIlp8Dl0eSz/C+vH17pc/5vsL/di/xpEqw9SgYeRTVC j7xrOkpZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACai48vwP+Dvhd03LT5v931eym/80b TQIOiXq/1mufLTq/rXhicweDL8M0vaa3dhBsetdY3Onn3hTVulzHZxWD9f2TN17eIdvsc5eAA= 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 0a45e3152016..2d9717861a6b 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 Tue Jul 27 10:48: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: 12402485 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 F3E32C432BE for ; Tue, 27 Jul 2021 10:50:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DA9D160FED for ; Tue, 27 Jul 2021 10:50:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236314AbhG0Ku5 (ORCPT ); Tue, 27 Jul 2021 06:50:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:56062 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236431AbhG0Kuz (ORCPT ); Tue, 27 Jul 2021 06:50:55 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 9B8386135A; Tue, 27 Jul 2021 10:50:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383055; bh=W8n859/SRlGQ7exT3uSV5aE2VA+wYWi1b4nP9t7In+s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Mhchuf1og1/Tluz5Fc2+/0o4S+vSmgqSoE8JUOZLDr9ss8HCfxmRKrwKGQcQjl+ny JYgTqcKw92Bsp0qI4yy4fkOjpkFjBTMK4vS7kBti6upqy6gRMeFCp01USttWxZdE3G 47BDcOJDmQ2MwZZnDyLAQHb2opqmgofUjocg3tBBk7Q30ItppY+NoxoqTDfw8Y+Gdx FFGaOqD7unavSeuzqJbLREVRjr1PO4LuNOlzVF92tNMqzAarcOkLDcE+/3pdOaI37L Y6ER/c6jRGUIqhRouSV87d6xnLF1Gk8cy/jpnW9bvlVFryKY1B4qpiAYcoHO1oXqSD Tt9qvX6Zsg76A== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 11/21] btrfs/inode: allow idmapped permission iop Date: Tue, 27 Jul 2021 12:48:50 +0200 Message-Id: <20210727104900.829215-12-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1086; h=from:subject; bh=7QZtZ22YWM5l3X750lvv/oKj3lzulOrOb1OJQSL0Ewg=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzIl/+Wtn+br2dVvfu5w3zYhpLJsLedn6WVmvnll798d 3uLp3FHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCR5kmMDJNy/zNn+wh8PTn/3UW/8t SjqbpKqkKGt3TWyhseWcWiZsLwv3j2m+IQp4OF0qz5dRN4Vuu7b3Awmb/nefOEVdwTAme2MgEA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 2d9717861a6b..5e0b8e394ae1 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 Tue Jul 27 10:48: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: 12402487 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 9823BC43214 for ; Tue, 27 Jul 2021 10:51:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 81B7060F59 for ; Tue, 27 Jul 2021 10:51:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236449AbhG0Ku6 (ORCPT ); Tue, 27 Jul 2021 06:50:58 -0400 Received: from mail.kernel.org ([198.145.29.99]:56104 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236434AbhG0Ku6 (ORCPT ); Tue, 27 Jul 2021 06:50:58 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 2F36A600CD; Tue, 27 Jul 2021 10:50:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383058; bh=Dh7XOP/FJ/SYcaAMmFbAGhF7YtATOmtd9+KPUrsPpwM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=afHo67rLuQHhWKlvfhOxmoWMfXz9zQK0aO/ePIfSYdEi8QrdH78KgjtdWTxYQzlRl gSL3q88BCRfjjpFtibY2gwazPeKokcAf7K3RdPWn14TaXJss98liADJkTa73Y7vGbb mL60PAkn2//JfxHJmJqeDdd4rNhXIxpZjejzfCgwuz19Hcq40cDIDvrSK9+kHKroLQ TCrhHPFJppqgV9ruVoG1MVUTc1XUvGxJO4x/usZO9uYZvNo1UhMzfOM+qTZdTqviCD YgYti/YrQF1nP8dlbyTdTAwryRxRvaqD1KxM0+vXhXkiUW8eQAlc5ZR7BoS37/pM4y z8+z68sDzpWUw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 12/21] btrfs/ioctl: check whether fs{g,u}id are mapped during subvolume creation Date: Tue, 27 Jul 2021 12:48:51 +0200 Message-Id: <20210727104900.829215-13-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1734; h=from:subject; bh=BgunXVJFNVa/wuJS4n0aobSThxeR7C5BLjftXo3Q3QE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzI1wX96/pkDfD/mvjJL0ZeonVe5ODJlH9tLMa+FDufu lDw53FHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjARJ0mG/xGrBP86abRZuPYf91ofu+ LBy7SX8WW+M1edU9vxJDT38EyG/6HPNfVX8n0Wkuhzvap4+hfnvN8hm1K6pj34oPe9Jrn7BQsA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 Tue Jul 27 10:48: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: 12402489 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 7819AC4338F for ; Tue, 27 Jul 2021 10:51:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6273C60FDB for ; Tue, 27 Jul 2021 10:51:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236451AbhG0KvB (ORCPT ); Tue, 27 Jul 2021 06:51:01 -0400 Received: from mail.kernel.org ([198.145.29.99]:56192 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236425AbhG0KvA (ORCPT ); Tue, 27 Jul 2021 06:51:00 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 86F8060249; Tue, 27 Jul 2021 10:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383060; bh=uOVzXrcM4S7QODaC1Hq8HLS7sq+6Lt+/4j8G+/Tb+wQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GxJb9ybXPJCSkaUtpYPSpYJzoNpAUtF+yx2S9bh+OUfUhLS4VGN1XGIkaOEr/nA3N Hjk75maM6ywIr0tqnf+GHib+NpXPNMoqXIX6kChnx3PH/Gp3gAYdSgcCgzYaVy07NH ChBhR4mBA2HsqbF4S+PlSrA31CCP8oZr/qZJPLoJPUQ8CIiOKyrcYnuVrA+7ZNYV79 Krh9IOc7qzF5+aTMdFHozMtkJJE0LVpVxLk8TZEHEevyiJWuoprQ80hZHzZcJrC0gI rDSJT4ou2PHXu7UgYP6SMw5sQNHTuDHE0GIm0gwFvJdi/f/BVUEv4S/VMqqy1sJCnI Hn6zv5W5CH+KQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 13/21] btrfs/inode: allow idmapped BTRFS_IOC_{SNAP,SUBVOL}_CREATE{_V2} ioctl Date: Tue, 27 Jul 2021 12:48:52 +0200 Message-Id: <20210727104900.829215-14-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8252; h=from:subject; bh=Jb4kinKRMiKRA/d4DWHr6mIvAP2CszgObSFNZOVcWSM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzL18pJPDy3XHrt3++Ts0klbwjor7uR+4Mtw7pOryeNn 23zheUcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBE0v4wMnRZPdkye0cI60LhpNPvUt labiUYhy1riKkUceF3ds948oHhn6VE2nqG9xcdS43X7vSpd7qo/bzr6cxQVSP27dNNP9msYgAA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ non-semantical changes --- fs/btrfs/ctree.h | 3 ++- fs/btrfs/inode.c | 5 +++-- fs/btrfs/ioctl.c | 47 +++++++++++++++++++++++++++-------------------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e5e53e592d4f..ee1876571b3f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3145,7 +3145,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, struct extent_state **cached_state); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root); + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns); void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state, unsigned *bits); void btrfs_clear_delalloc_extent(struct inode *inode, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5e0b8e394ae1..30e22c6f83a3 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..bb4bda3bba98 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -492,8 +492,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid) return 1; } -static noinline int create_subvol(struct inode *dir, - struct dentry *dentry, +static noinline int create_subvol(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry, const char *name, int namelen, struct btrfs_qgroup_inherit *inherit) { @@ -638,7 +638,7 @@ static noinline int create_subvol(struct inode *dir, goto fail; } - ret = btrfs_create_subvol_root(trans, new_root, root); + ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns); btrfs_put_root(new_root); if (ret) { /* We potentially lose an unused inode item here */ @@ -864,15 +864,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) } /* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct inode *dir, struct dentry *child) +static inline int btrfs_may_create(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *child) { if (d_really_is_positive(child)) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) return -EOVERFLOW; - return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); } /* @@ -881,6 +882,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) * inside this filesystem so it's quite a bit simpler. */ static noinline int btrfs_mksubvol(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *snap_src, bool readonly, @@ -895,12 +897,12 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(name, parent->dentry, namelen); + dentry = lookup_one(mnt_userns, name, parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; - error = btrfs_may_create(dir, dentry); + error = btrfs_may_create(mnt_userns, dir, dentry); if (error) goto out_dput; @@ -922,7 +924,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (snap_src) error = create_snapshot(snap_src, dir, dentry, readonly, inherit); else - error = create_subvol(dir, dentry, name, namelen, inherit); + error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit); if (!error) fsnotify_mkdir(dir, dentry); @@ -936,6 +938,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, } static noinline int btrfs_mksnapshot(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *root, bool readonly, @@ -965,7 +968,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent, btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); - ret = btrfs_mksubvol(parent, name, namelen, + ret = btrfs_mksubvol(parent, mnt_userns, name, namelen, root, readonly, inherit); out: if (snapshot_force_cow) @@ -1794,6 +1797,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, } static noinline int __btrfs_ioctl_snap_create(struct file *file, + struct user_namespace *mnt_userns, const char *name, unsigned long fd, int subvol, bool readonly, struct btrfs_qgroup_inherit *inherit) @@ -1821,8 +1825,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, } if (subvol) { - ret = btrfs_mksubvol(&file->f_path, name, namelen, - NULL, readonly, inherit); + ret = btrfs_mksubvol(&file->f_path, mnt_userns, name, + namelen, NULL, readonly, inherit); } else { struct fd src = fdget(fd); struct inode *src_inode; @@ -1836,16 +1840,17 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, "Snapshot src from another FS"); ret = -EXDEV; - } else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { + } else if (!inode_owner_or_capable(mnt_userns, src_inode)) { /* * Subvolume creation is not restricted, but snapshots * are limited to own subvolumes only */ ret = -EPERM; } else { - ret = btrfs_mksnapshot(&file->f_path, name, namelen, - BTRFS_I(src_inode)->root, - readonly, inherit); + ret = btrfs_mksnapshot(&file->f_path, mnt_userns, + name, namelen, + BTRFS_I(src_inode)->root, + readonly, inherit); } fdput(src); } @@ -1869,8 +1874,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, return PTR_ERR(vol_args); vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, false, NULL); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + false, NULL); kfree(vol_args); return ret; @@ -1928,8 +1934,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file, } } - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, readonly, inherit); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + readonly, inherit); if (ret) goto free_inherit; free_inherit: From patchwork Tue Jul 27 10:48:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402491 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 6DCB2C432BE for ; Tue, 27 Jul 2021 10:51:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 54B73600CD for ; Tue, 27 Jul 2021 10:51:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236427AbhG0KvE (ORCPT ); Tue, 27 Jul 2021 06:51:04 -0400 Received: from mail.kernel.org ([198.145.29.99]:56210 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236455AbhG0KvC (ORCPT ); Tue, 27 Jul 2021 06:51:02 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 01F7860F59; Tue, 27 Jul 2021 10:51:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383063; bh=ZVdEwxARCnwrtv8KR7Uv/W3jGp+1li1RIDemiy1dWck=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gqgRAt0R2LK12Q4WIjMU5Vtmf0rsQP4VP8Wbhtl+eHxyPQq021+ypQbSKoEBu6Q7e mfkQidEYTfKCykEYUrxvgw9g90pxl8ACVaOO/7MKHcpOcttbHiE8BRkb69RYzmn1jS Erxjif4O1RAvDPy7oLw2AR/QGe6YxIVFqs3uFnkfYNOWcGJ7vtLpjtFUcVkkN55Xi6 yjC8H0Iqs0Lr0GmML6QR1+gsuOTDYtK96qV0Cb5LwaRQSfALExRjGI++Q8/GXyt3kD iyugAFcMBHs8iErtzbk79USpDUUO9FWFxBsE5mubsZJhJ7nrfeKaGdI64VvH6AuMhQ v7VrTm0eLMLYQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , Qu Wenruo Subject: [PATCH v4 14/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SNAP_DESTROY{_V2} ioctl Date: Tue, 27 Jul 2021 12:48:53 +0200 Message-Id: <20210727104900.829215-15-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8052; h=from:subject; bh=XDvcdhsUgM9IEU8Jd1xYGomj1Lq1mSRLnB//SXLofQU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzLV/kTX6v83v9+/c2jzdXUWa0aLz4t2cT7/LXx8pocw k8d2vo5SFgYxLgZZMUUWh3aTcLnlPBWbjTI1YOawMoEMYeDiFICJRN9h+O8y306/SHCvXGHkQ3XLuy se3nJW7z3M3L36opRpzIFt5lsY/vCxGIYxbF1qL/xWK3evAsfik+o+6/ZGnL/+Y80vqxvWJvwA 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. Subvolumes and snapshots can either be deleted by specifying their name or - if BTRFS_IOC_SNAP_DESTROY_V2 is used - by their subvolume or snapshot id if the BTRFS_SUBVOL_SPEC_BY_ID is set. 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. As David correctly pointed out this means that even the root or CAP_SYS_ADMIN capable user can't delete a subvolume via BTRFS_SUBVOL_SPEC_BY_ID. This is intentional. The root user is currently already subject to permission checks in btrfs_may_delete() including whether the inode's i_uid/i_gid of the directory the subvolume is located in have a mapping in the caller's idmapping. For this to fail isn't currently possible since a btrfs filesystem can't be mounted with a non-initial idmapping but it shows that even the root user would fail to delete a subvolume if the relevant inode isn't mapped in their idmapping. The idmapped mount case is the same in principle. This isn't a huge problem a root user wanting to delete arbitrary subvolumes can just always create another (even detached) mount without an idmapping attached. In addition, we will allow BTRFS_SUBVOL_SPEC_BY_ID for cases where the subvolume to delete is directly located under inode referenced by the fd passed for the ioctl() in a follow-up commit. 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 Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ - Christian Brauner : - Explain the restriction for the BTRFS_SUBVOL_SPEC_BY_ID flag in detail.k - David Sterba : - Replace the -EINVAL return value for BTRFS_SUBVOL_SPEC_BY_ID deletion requests with a more sensible one such as -EXDEV or -EOPNOTSUPP. /* v4 */ non-semantical changes --- fs/btrfs/ioctl.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index bb4bda3bba98..6295c9c6fc3e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -830,7 +830,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, * nfs_async_unlink(). */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *victim, int isdir) { int error; @@ -840,12 +841,12 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) BUG_ON(d_inode(victim->d_parent) != dir); audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(&init_user_ns, dir, d_inode(victim)) || + if (check_sticky(mnt_userns, dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim))) return -EPERM; @@ -2914,6 +2915,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, struct btrfs_root *dest = NULL; struct btrfs_ioctl_vol_args *vol_args = NULL; struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; + struct user_namespace *mnt_userns = file_mnt_user_ns(file); char *subvol_name, *subvol_name_ptr = NULL; int subvol_namelen; int err = 0; @@ -2941,6 +2943,18 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { + /* + * Deleting by subvolume id can be used to delete + * subvolumes/snapshots anywhere in the filesystem. + * Ensure that users can't abuse idmapped mounts of + * btrfs subvolumes/snapshots to perform operations in + * the whole filesystem. + */ + if (mnt_userns != &init_user_ns) { + err = -EOPNOTSUPP; + goto out; + } + if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; goto out; @@ -3025,7 +3039,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); if (err == -EINTR) goto free_subvol_name; - dentry = lookup_one_len(subvol_name, parent, subvol_namelen); + dentry = lookup_one(mnt_userns, subvol_name, parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; @@ -3067,14 +3081,14 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (root == dest) goto out_dput; - err = inode_permission(&init_user_ns, inode, + err = inode_permission(mnt_userns, inode, MAY_WRITE | MAY_EXEC); if (err) goto out_dput; } /* check if subvolume may be deleted by a user */ - err = btrfs_may_delete(dir, dentry, 1); + err = btrfs_may_delete(mnt_userns, dir, dentry, 1); if (err) goto out_dput; From patchwork Tue Jul 27 10:48:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402493 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 559BBC4338F for ; Tue, 27 Jul 2021 10:51:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3CE5161019 for ; Tue, 27 Jul 2021 10:51:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236455AbhG0KvF (ORCPT ); Tue, 27 Jul 2021 06:51:05 -0400 Received: from mail.kernel.org ([198.145.29.99]:56246 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236454AbhG0KvF (ORCPT ); Tue, 27 Jul 2021 06:51:05 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id A2E7260FDB; Tue, 27 Jul 2021 10:51:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383065; bh=6mKxMAPjsLzj7goh0VnGIg3kaeW647CzFz1ijYmFuyI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=c8mqXOrMix6NQrHTSwxtrmG/efB0xmohik11Qz+nzt7eOq0uL5u+pDvlK8rvVOONY rPYHurxteHjgn8kF1koUGq488QdOywc0WH/RA5uqQjJF6C+ggsiABJlBWhdD6kaY5y z6ZljlnDb2VfXg8ASFk3Akf5hULeWURuB0q8qMpbtQxs9jMrMma2wzKqa57FG/QhW+ 6k127qySnl/KzRGyL9CnKTUebrP14DHxZtqff3HW3KXhr8iNq2HyuXIAhrNnA3fZwf 7aP0r7EJUzbKoNYa8vuRFukfY6SokokmbjcoMliMwNKy47DDcwL4hILjdmanBqzWJM uovxxB4hQFccQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , Qu Wenruo Subject: [PATCH v4 15/21] btrfs/ioctl: relax restrictions for BTRFS_IOC_SNAP_DESTROY_V2 with subvolids Date: Tue, 27 Jul 2021 12:48:54 +0200 Message-Id: <20210727104900.829215-16-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2959; h=from:subject; bh=GVuvGzTE8hu4K3aYKWY6vqDa/KQO8VDRSKMkNDxu+eI=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzItbWZH8qbS6T93+3SdPvTGuZU36lkvm8Qpz9RtZxlz PJmaOkpZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACZyZirD/5KiGW1efKeXnc3a2p1ReH eXGkMhn1kus6dtTsj1LQf9TjD8FXPt2dd/KM/M5NSLrU/zOtnUenSuiMm8nHfiS45Y1K6FjAA= 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 Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 6295c9c6fc3e..5a8df000cc41 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2943,17 +2943,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { - /* - * Deleting by subvolume id can be used to delete - * subvolumes/snapshots anywhere in the filesystem. - * Ensure that users can't abuse idmapped mounts of - * btrfs subvolumes/snapshots to perform operations in - * the whole filesystem. - */ - if (mnt_userns != &init_user_ns) { - err = -EOPNOTSUPP; - goto out; - } + struct inode *old_dir; if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; @@ -2991,6 +2981,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = PTR_ERR(parent); goto out_drop_write; } + old_dir = dir; dir = d_inode(parent); /* @@ -3001,6 +2992,20 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, */ destroy_parent = true; + /* + * On idmapped mounts, deletion via subvolid is + * restricted to subvolumes that are immediate + * ancestors of the inode referenced by the file + * descriptor in the ioctl. Otherwise the idmapping + * could potentially be abused to delete subvolumes + * anywhere in the filesystem the user wouldn't be able + * to delete without an idmapped mount. + */ + if (old_dir != dir && mnt_userns != &init_user_ns) { + err = -EOPNOTSUPP; + goto free_parent; + } + subvol_name_ptr = btrfs_get_subvol_name_from_objectid( fs_info, vol_args2->subvolid); if (IS_ERR(subvol_name_ptr)) { From patchwork Tue Jul 27 10:48:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402495 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 8D0C4C4338F for ; Tue, 27 Jul 2021 10:51:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 770A061019 for ; Tue, 27 Jul 2021 10:51:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236457AbhG0KvI (ORCPT ); Tue, 27 Jul 2021 06:51:08 -0400 Received: from mail.kernel.org ([198.145.29.99]:56274 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236401AbhG0KvH (ORCPT ); Tue, 27 Jul 2021 06:51:07 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 18476600CD; Tue, 27 Jul 2021 10:51:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383067; bh=wlX0GfTdm4WkKZ/FF0BSt95lxb/TpGDEUvutV9t0C5E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sOGwBCoPJ6dtqdG+wczC9qEdaKNqYvBE/XmG2ZG8OfcKh3mz6aAExTDAoD7QhyGu8 bTi88tPheExsox/yJcCL0YG560N3YK41Yje0j5QogwwtTpyAqzowNjmP5NnNQd57wv soFp8II1po3brovoVQzDEs188YtfXjG/ppe01kcQG7zS7vRTZa1885xfNs/hEi9fT0 GeZhuxC81HQWqBvfnyGgmjKb21oH5zdfgv0evPbTzGHtJgwh41PjyrFz5Y8D5vNOLX YNflP1BFKbdSrzo34B9wocXouj5nbq0DRlRDa19iUPtLXB6Nvi5J/CkqVpyxF1JtL+ b/8RdFdaE//PA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 16/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SET_RECEIVED_SUBVOL{_32} ioctl Date: Tue, 27 Jul 2021 12:48:55 +0200 Message-Id: <20210727104900.829215-17-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2222; h=from:subject; bh=cm8VFf4lNW0r6dDGyPuNxLwjORqInF5A5yqLnQxEBpg=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJt15w/Amu2lqlcPnA08+lt1ewn6z2fTvrkkT7P5WJ+ 47YNER2lLAxiXAyyYoosDu0m4XLLeSo2G2VqwMxhZQIZwsDFKQATKdzHyPDz75TdD7g/dAs3F/LJJX 57HZL8w/CPzsf6DRsb0s0z/hoy/I+ZpnS6fz3fdWuOA481zRQYrlW/YKlbra+jUHDonERrAhcA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 5a8df000cc41..c330b1b252a1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4464,6 +4464,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info, } static long _btrfs_ioctl_set_received_subvol(struct file *file, + struct user_namespace *mnt_userns, struct btrfs_ioctl_received_subvol_args *sa) { struct inode *inode = file_inode(file); @@ -4475,7 +4476,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, int ret = 0; int received_uuid_changed; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(mnt_userns, inode)) return -EPERM; ret = mnt_want_write_file(file); @@ -4580,7 +4581,7 @@ static long btrfs_ioctl_set_received_subvol_32(struct file *file, args64->rtime.nsec = args32->rtime.nsec; args64->flags = args32->flags; - ret = _btrfs_ioctl_set_received_subvol(file, args64); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), args64); if (ret) goto out; @@ -4614,7 +4615,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, if (IS_ERR(sa)) return PTR_ERR(sa); - ret = _btrfs_ioctl_set_received_subvol(file, sa); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), sa); if (ret) goto out; From patchwork Tue Jul 27 10:48:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402497 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 0A636C4338F for ; Tue, 27 Jul 2021 10:51:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E94D461019 for ; Tue, 27 Jul 2021 10:51:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236461AbhG0KvK (ORCPT ); Tue, 27 Jul 2021 06:51:10 -0400 Received: from mail.kernel.org ([198.145.29.99]:56296 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236401AbhG0KvJ (ORCPT ); Tue, 27 Jul 2021 06:51:09 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 4635C60FED; Tue, 27 Jul 2021 10:51:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383070; bh=vYIYQici/uKWm8x8tPQwwBjQaY6o1hk9N+CQmtERywk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XYyZrEuKkp6qbfxDLRWoOQ4LeQ7rjVXfuuSZs2+pSshDVvLHhTeSoEzbfF7oc9UFY PaOCJZ97izPm/4DuK4kpFJLWgSV7UwUfejsGnkUKnCIeOmRXOF/mgV6YYJEpVBoeHc aK48lDoqZknbDcu/yNqAcrFR6VnF58h+9Tu3Wi3BxUtppRndTyU774pEm/tf4JDzL9 IycLG4aMllDqb/mkQXmjg1MKqgpOCzMMOofDMHFz1phlnywY3FgjOISlogY4Ze8xIZ jcX84jnDuF5EJLfpBXJZ7MtBHb8pYOXLEglKN/RAhkwcSu+rxuBtuwgb25y/8uZZr9 pfmuW7sblYcrA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 17/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SUBVOL_SETFLAGS ioctl Date: Tue, 27 Jul 2021 12:48:56 +0200 Message-Id: <20210727104900.829215-18-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1365; h=from:subject; bh=g3IM86F5Pk9+ZVwvZpOi8+/bGoYPNr5gjHAkgH0Fo1o=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzLt4nO+qasCX71g3d8iGP5XV/hft+MvHWMu7wiZaywf M+Rvd5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzE7CTDX9HirHLXi0zNv+SXlKe7yU 872tR++MUxjjfVZWJ7AlKXCDH8L/2Z/+L+F64axlWz9F8lhFsXsDreWumaJBWttUtxlyg7OwA= 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 c330b1b252a1..9858bd84a4f7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1981,7 +1981,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, u64 flags; int ret = 0; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(file_mnt_user_ns(file), inode)) return -EPERM; ret = mnt_want_write_file(file); From patchwork Tue Jul 27 10:48:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402499 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 0E833C4338F for ; Tue, 27 Jul 2021 10:51:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EA6A661019 for ; Tue, 27 Jul 2021 10:51:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236464AbhG0KvM (ORCPT ); Tue, 27 Jul 2021 06:51:12 -0400 Received: from mail.kernel.org ([198.145.29.99]:56332 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236463AbhG0KvM (ORCPT ); Tue, 27 Jul 2021 06:51:12 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 9A11F60FDB; Tue, 27 Jul 2021 10:51:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383072; bh=kElqRIJbOihM3GaF8lgnRRqbGWuGMtkra/QRuL152OA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SPnbXrSCH9ef40u/kX48iyCDHabIrjSu39ohMwMYOjXvfd9UWZomd9L0YtVE3NXVt s54rWR2qEhCf3eJ5T+JQfMXBlacNdxg0yFvTVbgeA6E237GRNgET+PTTwQSVGyeq8q dxsbHOGzvnoo8JgKm7S9F94bLnFRessivxgRhbg+OHUcUM1x5at0yVMmIapTtqyd5I BwHNXhWSF0n5dXMLpvA871wxGe5+MewnEs5Khi4tWGPnbDx/Gve3m7KxDDCrGRnQhX Iu+k8eacaYNwiMrPQi4OLLkBmCcQ71lbVTPpIAkx0n+0Oj8rnmoJR7KFZ9ahbz/i3m aNxohqVPZstwA== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 18/21] btrfs/ioctl: allow idmapped BTRFS_IOC_INO_LOOKUP_USER ioctl Date: Tue, 27 Jul 2021 12:48:57 +0200 Message-Id: <20210727104900.829215-19-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3858; h=from:subject; bh=NZzteBh+yD3m6GuYOJQ7K4duGp+WFcI1VjV5cIGiEPc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzL9f1R5FPeJL6o1N3/fLP1Yfj3tsEfpzkvVOtcViufn aXG3dZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyE+T4jw4WtleJvrM3Zux0WLmNafu 66O/OJOdUnrug8n2OY/N341RuG/6Fbj1mHuuioX2GI8hUIVry990dWo8Cdzdz8XjNnbC+bxwkA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 9858bd84a4f7..3e9bde72bee6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2439,7 +2439,8 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, return ret; } -static int btrfs_search_path_in_tree_user(struct inode *inode, +static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns, + struct inode *inode, struct btrfs_ioctl_ino_lookup_user_args *args) { struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; @@ -2537,7 +2538,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode, ret = PTR_ERR(temp_inode); goto out_put; } - ret = inode_permission(&init_user_ns, temp_inode, + ret = inode_permission(mnt_userns, temp_inode, MAY_READ | MAY_EXEC); iput(temp_inode); if (ret) { @@ -2679,7 +2680,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) return -EACCES; } - ret = btrfs_search_path_in_tree_user(inode, args); + ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args); if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) ret = -EFAULT; From patchwork Tue Jul 27 10:48:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402501 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 A0709C4338F for ; Tue, 27 Jul 2021 10:51:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8268F60249 for ; Tue, 27 Jul 2021 10:51:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236467AbhG0KvP (ORCPT ); Tue, 27 Jul 2021 06:51:15 -0400 Received: from mail.kernel.org ([198.145.29.99]:56366 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236463AbhG0KvO (ORCPT ); Tue, 27 Jul 2021 06:51:14 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E2519600CD; Tue, 27 Jul 2021 10:51:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383074; bh=74J5jE6muIiGIGr1IgvjN/INQNXH+EVr0v4jSbRm8yk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jrVFzmXLRzmaMo58e2YoTnkYpXcEBLRJkfvBezBAaks1V9jF3zgG2Si7oaLiNrVTI s/D5B9skDMbrFszfdZ/gsRsqp1nJvKDUizXLsv3Vn63dHWQ+T87nK1f8IIUidc84nt zMzPNl8mZaW6C0Mll2b+UYzNoMO+WU3tSKKZqe+Vtx7/fgd840qC4a7c9YClsJ/CPy ASjM7Won0y9kb1kj+vXbtaF84tvTp9pdFXB2BfLJTYD9SOC8kmjvFWhTyy+Yg/yp3P ik/wWctol5BAU5lqfnmG1lBR2QJ/yuBZYdnrNHr2tLo2keAW6YD1yuRJRP2K+VlQdQ MjcC0St76+Log== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 19/21] btrfs/acl: handle idmapped mounts Date: Tue, 27 Jul 2021 12:48:58 +0200 Message-Id: <20210727104900.829215-20-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2830; h=from:subject; bh=/xcihVYWIj8p9Qn0aeQ7Vcpcrd1JphDgS+/fHs1SFfE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzJ9578pu6XFuL6JW3dFL/de7num45Cac29zqc9e4zXX zgbldZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzEaSUjw1X7cpeb2+/y9AZN6ptUGu 56NWwtv7W/F6vP9ZMrnuww28jw3/XW3ECzdW+qzv9fstpkqVFJzfEHHPM1vsay372RsLnEiRMA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 Tue Jul 27 10:48:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402503 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 2BA03C4320A for ; Tue, 27 Jul 2021 10:51:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1817B61019 for ; Tue, 27 Jul 2021 10:51:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236454AbhG0KvR (ORCPT ); Tue, 27 Jul 2021 06:51:17 -0400 Received: from mail.kernel.org ([198.145.29.99]:56396 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236401AbhG0KvR (ORCPT ); Tue, 27 Jul 2021 06:51:17 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 6E62B60FED; Tue, 27 Jul 2021 10:51:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383077; bh=8/wdpW6ZRfdpLUMDczSPKfKC3lOy1ruGV34bYfkEt4k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=loc51CdlEAFymJ9/izSZXNviv6TA/CPCjFlsrwveNta0XxwuWtN1IiXJzYndEDBS3 FIl2hnnSzZgGX5l+dQakMX66v8qWqYI1RO4Er4lL08O6GDKJJtn7n1nlFVctabBwYh BE8DPoNF3Za85os7QU3hbG5DhzA/dKaH1NVHmyC+A7mc2pfQWvnNrFGTC4SMic8bRj hqm6POLVvOHoa/q/Pa5rVS1y+4dM+TF7ufWs+ckNiePvrLCm6+lVVGyT97eV7dBJ06 4peCu+2KVrxyjvocKCmNU5ZaNu/OMO3I5pZRYXcvRAijx1rCcFA4CG6KvH0W/Kb/0l LmMtNtOna2bFg== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig Subject: [PATCH v4 20/21] btrfs/super: allow idmapped btrfs Date: Tue, 27 Jul 2021 12:48:59 +0200 Message-Id: <20210727104900.829215-21-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1586; h=from:subject; bh=Ol0dO5u99pcXu3/4DfTR8pullkZ/fw2iVQq3txGXh28=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzI9W6+K+fXT5+URgp5bb5e5zRb5lyseZ+gy6Vs287+g xNz7HaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABOJPcjIcHV5WNd2u91uf849uh290H zJo9n+7F/W1Md6/U+6anHxpTgjw1yziWy84cJ3J17mTlSdtSfw0E+JDLmk0nAF+9trPf+m8gIA 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 Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ 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 Tue Jul 27 10:49:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12402505 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,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 C9551C4338F for ; Tue, 27 Jul 2021 10:51:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9D65861019 for ; Tue, 27 Jul 2021 10:51:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236470AbhG0KvW (ORCPT ); Tue, 27 Jul 2021 06:51:22 -0400 Received: from mail.kernel.org ([198.145.29.99]:56428 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236401AbhG0KvV (ORCPT ); Tue, 27 Jul 2021 06:51:21 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id F12A960249; Tue, 27 Jul 2021 10:51:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627383082; bh=XSI0rnQmsRUVBCOZ6X0wXLSx5RM5II3taZ6VvnxYfrU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eL1NjFeU82vXx5KfmUnQQltE7ZGvepDgqMCared38Li/5yio+YONuiIN9umyTE0uN RUvdsHUAn40/jt0K4rMa8t5oxIDjDSQu2RTNSP5iAOnGVLN9HcyB70l7cttSTZegMw j/Eu0MRH7B9AIR86/0LZnO3MlnvFR3VtVpow9+s5mtY9VdVP8EuaYrQDN5PU5K8sFX aEJ0HExIU25nipDRprmlnutFbf1hVVMkwKPixvRlJfz26+x+PFvoseOPY+LfbQiHsI ow/5j+hJxCXO1PlOPriF0xvki/YhTLvoZXjNkGhBrZlwWkjPmNk+5aOyfG6IcelA40 hSnEBuaTdnNkw== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , fstests@vger.kernel.org Subject: [PATCH v4 21/21] btrfs/242: introduce btrfs specific idmapped mounts tests Date: Tue, 27 Jul 2021 12:49:00 +0200 Message-Id: <20210727104900.829215-22-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210727104900.829215-1-brauner@kernel.org> References: <20210727104900.829215-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=108511; h=from:subject; bh=GXtDp1bGrymA5DVgcZkvxxTnqMVPN0TvNIiD9zvrsvk=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST8fzKdW2W5mJUKr9P3LtND/k+KuxUXeshYaDp9d9jwp9Ki T6e/o5SFQYyLQVZMkcWh3SRcbjlPxWajTA2YOaxMIEMYuDgFYCKOTowMm168W3Vy5q0F4ZnZX7hYi7 pLzzuECsl43+x88eD6jxs/PjAyfPwrv2r2woqj/9uSZdzEvp5cwlJes2Tm6pp8fj+PvUEPGQE= 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 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 --- /* v2 */ unchanged /* v3 */ - Christian Brauner : - When trying to delete a subvolume or snapshot by its id on idmapped mounts the returned error code has changed from -EINVAL to -EOPNOTSUPP. Account for that in the tests. /* v4 */ unchanged --- src/idmapped-mounts/idmapped-mounts.c | 3693 ++++++++++++++++++++++++- tests/btrfs/242 | 34 + tests/btrfs/242.out | 2 + 3 files changed, 3719 insertions(+), 10 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..c5a4aaa0 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,6 +9531,3622 @@ out: return fret; } +static int btrfs_delete_subvolume(int parent_fd, const char *name) +{ + struct btrfs_ioctl_vol_args args = {}; + size_t len; + int ret; + + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; + + 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 int btrfs_delete_subvolume_id(int parent_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(parent_fd, BTRFS_IOC_SNAP_DESTROY_V2, &args); + if (ret < 0) + return -1; + + return 0; +} + +static int btrfs_create_subvolume(int parent_fd, const char *name) +{ + struct btrfs_ioctl_vol_args_v2 args = {}; + size_t len; + int ret; + + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; + + memcpy(args.name, name, len); + args.name[len] = '\0'; + + ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args); + if (ret < 0) + return -1; + + return 0; +} + +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; + + if (flags & ~BTRFS_SUBVOL_RDONLY) + return -EINVAL; + + len = strlen(name); + if (len >= sizeof(args.name)) + return -ENAMETOOLONG; + memcpy(args.name, name, len); + args.name[len] = '\0'; + + 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; +} + +static int btrfs_get_subvolume_ro(int fd, bool *read_only_ret) +{ + uint64_t flags; + int ret; + + ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); + if (ret < 0) + return -1; + + *read_only_ret = flags & BTRFS_SUBVOL_RDONLY; + return 0; +} + +static int btrfs_set_subvolume_ro(int fd, bool read_only) +{ + uint64_t flags; + int ret; + + ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); + if (ret < 0) + return -1; + + if (read_only) + flags |= BTRFS_SUBVOL_RDONLY; + else + flags &= ~BTRFS_SUBVOL_RDONLY; + + ret = ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags); + if (ret < 0) + return -1; + + return 0; +} + +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; + + ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); + if (ret < 0) + return -1; + + *id_ret = args.treeid; + + return 0; +} + +/* + * 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; +}; + +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; + } + + top = top_stack_entry(iter); + iter->stack_len--; + parent = top_stack_entry(iter); + + 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; + + return 0; +} + +static int append_stack(struct btrfs_iter *iter, uint64_t tree_id, size_t path_len) +{ + struct btrfs_stack *entry; + + 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 != EOPNOTSUPP) + 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"); @@ -9533,20 +13160,25 @@ static void usage(void) 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}, - {NULL, 0, 0, 0}, + {"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 { @@ -9612,6 +13244,31 @@ 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; @@ -9649,7 +13306,7 @@ int main(int argc, char *argv[]) { int fret, ret; int index = 0; - bool supported = false, test_core = false, + 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) { @@ -9675,6 +13332,15 @@ int main(int argc, char *argv[]) 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: @@ -9703,6 +13369,10 @@ int main(int argc, char *argv[]) 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. @@ -9756,6 +13426,9 @@ int main(int argc, char *argv[]) !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; out: 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