From patchwork Mon Jul 26 10:27: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: 12399045 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 E8E13C4320A for ; Mon, 26 Jul 2021 10:28:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C82CA60F59 for ; Mon, 26 Jul 2021 10:28:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232331AbhGZJsZ (ORCPT ); Mon, 26 Jul 2021 05:48:25 -0400 Received: from mail.kernel.org ([198.145.29.99]:35282 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231421AbhGZJsY (ORCPT ); Mon, 26 Jul 2021 05:48:24 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 3577A60F22; Mon, 26 Jul 2021 10:28:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295333; bh=Q65wTxj8papDGcJgc1J3joFcMnqpS0z8nz0sG+q3sxg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I2Q/4Rva/RhthB6gI3WyVPrWUJVPx67RSGyIOzspJjxIpyA16Os8henvyDWoLa3Y2 QBzOm4QDF7TAzSXNw8SCdQxAEd9KBBHqsAlkfPDv1wfo5vOK9UlpaJ2X0hXVfOSfDq qJ3WjZeuaYFZ10Uw4V0FceBeGRmpGw8gMBG/r3MkgLUoh8g7/z0eOXBuqkN/UtIw7R HVuXafM6m5J7gejBj1jQvnidifG/OMxbUn7IQGF2inOBC0AOC6gYSyYWmgYz5k09eA nu3/GAXtWvSCGSe9suy/OiHuwudhSi5YWkDhbt5QmPne0dzNxuAne1HBlV1CQOUic0 wsXzJWTKthviQ== From: Christian Brauner To: Christoph Hellwig , Chris Mason , Josef Bacik , David Sterba Cc: Al Viro , linux-btrfs@vger.kernel.org, Christian Brauner , Christoph Hellwig , linux-fsdevel@vger.kernel.org Subject: [PATCH v3 01/21] namei: add mapping aware lookup helper Date: Mon, 26 Jul 2021 12:27:56 +0200 Message-Id: <20210726102816.612434-2-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4654; h=from:subject; bh=hz8lZ5Aw+HWLOcVYddCv6SoN2CluB8W6yBJZblizz5M=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNgN5p4SPBhTaJLwYlo178b658tbfY7sW7K2fcbFwWz HxD+2VHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRyysZ/rv80FPc993IZ+ehXGWZfO umjTIXtuWwhLv2/HhcyNqXv53hr0QjY6f0XN7D7mqqK9M5pxqlTsnbkD/HgfnZUr3K0rit/AA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner Various filesystems rely on the lookup_one_len() helper to lookup a single path component relative to a well-known starting point. Allow such filesystems to support idmapped mounts by adding a version of this helper to take the idmap into account when calling inode_permission(). This change is a required to let btrfs (and other filesystems) support idmapped mounts. Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- /* v2 */ - Al Viro : - Add a new lookup helper instead of changing the old ones. /* v3 */ unchanged --- fs/namei.c | 44 +++++++++++++++++++++++++++++++++++++------ include/linux/namei.h | 2 ++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..8f416698ee34 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2575,8 +2575,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, - int len, struct qstr *this) +static int lookup_one_len_common(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, int len, + struct qstr *this) { this->name = name; this->len = len; @@ -2604,7 +2605,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC); + return inode_permission(mnt_userns, base->d_inode, MAY_EXEC); } /** @@ -2628,7 +2629,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2655,7 +2656,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2664,6 +2665,37 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) } EXPORT_SYMBOL(lookup_one_len); +/** + * lookup_mapped_one_len - filesystem helper to lookup single pathname component + * @mnt_userns: user namespace of the mount the lookup is performed from + * @name: pathname component to lookup + * @base: base directory to lookup from + * @len: maximum length @len should be interpreted to + * + * Note that this routine is purely a helper for filesystem usage and should + * not be called by generic code. + * + * The caller must hold base->i_mutex. + */ +struct dentry *lookup_mapped_one_len(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, + int len) +{ + struct dentry *dentry; + struct qstr this; + int err; + + WARN_ON_ONCE(!inode_is_locked(base->d_inode)); + + err = lookup_one_len_common(mnt_userns, name, base, len, &this); + if (err) + return ERR_PTR(err); + + dentry = lookup_dcache(&this, base, 0); + return dentry ? dentry : __lookup_slow(&this, base, 0); +} +EXPORT_SYMBOL(lookup_mapped_one_len); + /** * lookup_one_len_unlocked - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2683,7 +2715,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); diff --git a/include/linux/namei.h b/include/linux/namei.h index be9a2b349ca7..fd9d22128df6 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -68,6 +68,8 @@ extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_mapped_one_len(struct user_namespace *, + const char *, struct dentry *, int); extern int follow_down_one(struct path *); extern int follow_down(struct path *); From patchwork Mon Jul 26 10:27: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: 12399047 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 CD83FC4338F for ; Mon, 26 Jul 2021 10:28:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B4E6760F5D for ; Mon, 26 Jul 2021 10:28:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232410AbhGZJs2 (ORCPT ); Mon, 26 Jul 2021 05:48:28 -0400 Received: from mail.kernel.org ([198.145.29.99]:35310 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232373AbhGZJs0 (ORCPT ); Mon, 26 Jul 2021 05:48:26 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id B7E1360F57; Mon, 26 Jul 2021 10:28:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295335; bh=cQ5K86trKiIycXN6esOezpVjNe0HcWEebhQT8/Nd24I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JFU5z8U0W4BKr/h5Ltx+AtGl1t+In9PLpixhGJ9rEI3x0stRj88SFofo4vsyqsIxJ Jy7JIV3YFc/IaROby+8TKVPa5vgU+zQZGf9krIU1qh2ios+JOGo03Lmrlfzfd17Nfz /KFe29rjjwixmrxpfdUtfdq92FUkPAbNlDES4XZWADpcJE+bVOGkbfNyjjI2oHgDyX J9jhHFjRZMWryXzcZmJUHlvHthqUpYu7AnGd/t2+Ji48D05FNpXLvFwJIqEgORLPuJ kStQGZVP/2mJcPppMeNNLfiXMsPGYnl0wLnb20WbssZiu4+cAwR6JazqUVjkV8retU L8hIIFWlYsnWQ== 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 v3 02/21] btrfs/inode: handle idmaps in btrfs_new_inode() Date: Mon, 26 Jul 2021 12:27:57 +0200 Message-Id: <20210726102816.612434-3-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4707; h=from:subject; bh=rq58S+phGawkvkIF+U33h3z9o90mx2uQ5xoIw5t9uRA=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zO8pu9SoHPiQ0Rd3v8lZdI/J/5d1d67ocVm4qaJsZeN shgndJSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyEeQojw5zp/0rd5h6x2FnvN7P9sP MG+Wuc/l9f679XY7T/6fkwcT7Df5+mde/NObPz8ib3ab5f8FwxMEL2A/MTgzfvowV+BLMdYwYA 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 --- 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 Mon Jul 26 10:27: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: 12399049 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 90009C4338F for ; Mon, 26 Jul 2021 10:29:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7835760F5B for ; Mon, 26 Jul 2021 10:29:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232490AbhGZJs3 (ORCPT ); Mon, 26 Jul 2021 05:48:29 -0400 Received: from mail.kernel.org ([198.145.29.99]:35342 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232453AbhGZJs2 (ORCPT ); Mon, 26 Jul 2021 05:48:28 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 39D0960F58; Mon, 26 Jul 2021 10:28:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295337; bh=jQFnDuoM/BnuEq8Rz4s4nc57hocd81q6QDseKfOaPjQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IaJLEvIsIiLjwYnmm/ySha+/6+SNR2Ohv/XrA7IEvSTEImmwP5K0LqbBJHjSfMUP+ D9ImdwrUg8VYrJVj13cFJgkC8BziZJoKl+r5WXEHGpQncucc8jRI4XOQYF73+72m0b oSU1yLfWA9qcRGrlSLdPN1s444lYUn1nkitUhdNvxifBAQnWg4Qmv61EdUzEXHLZAH YLSNO0Q/b3FaToTQpaZ/JhP9FC2oc1pTKMdyXSXtfZre426hN7IMvy4sfr9xE7Xg0Y IL3rWOIGwNw6fXoA1EILp7gbHBlz5UedQ6X2/pBjxGP91VbJvqj2h3NUeaRJIsrGnD CNVyiCyNVS8CQ== 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 v3 03/21] btrfs/inode: allow idmapped rename iop Date: Mon, 26 Jul 2021 12:27:58 +0200 Message-Id: <20210726102816.612434-4-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2682; h=from:subject; bh=ekR9Pl2USGKPFCFCMIDZ7co7GAwCT5FLMDb2Um5phBw=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNc8OGwqK9r4P73krKBHO99WPuTuItsXSVM2kRZfC8e EuLvKGVhEONikBVTZHFoNwmXW85TsdkoUwNmDisTyBAGLk4BmIhDEiPD9Yve17vXnI36/tLs2XJxm1 sHnf5ce+1bv+eE399oLrWb/Qz/4242/Vn/n/dYQNsC52Pvj8v9ZZlx7vuBw9lp7K/uNyzr4AcA 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 --- 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 Mon Jul 26 10:27: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: 12399051 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 DB569C432BE for ; Mon, 26 Jul 2021 10:29:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BD95260F5D for ; Mon, 26 Jul 2021 10:29:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232504AbhGZJsc (ORCPT ); Mon, 26 Jul 2021 05:48:32 -0400 Received: from mail.kernel.org ([198.145.29.99]:35362 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232453AbhGZJsb (ORCPT ); Mon, 26 Jul 2021 05:48:31 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 55D2F60F56; Mon, 26 Jul 2021 10:28:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295340; bh=1CDMCDmZRtq+M5MA+OFxWLDj01b9pbjhDSzC2XO1Yqs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o9yXa1wwK3x2aIjzK7H6dxi271R69WjqZUPQnYd9ys9Jz1fxPmJqmw2HhEZeKkj0I oUnAC9xrl2ldNFNW7iBsYKN6IyW5FBh+fZ5z7KSYVuORKBnd/BrxhboAu1q96Rx1g4 11MJlsTrfGMjiAMycTrvwBlql5zElxWslH0vpSOx2W7ZfH7/ApXtf5mSmDvcR+ri2I L54z4dWeiT29hwxGF2f0zquRzmhX56/Z9vrJMe8+r5UNMDbYY1U/Mib9b/NkxPtnbR sjJzJKtCWnSxCqn/tXu6Mw7gMzgRrfvVuNd1b+ogsxUXIKa4/ZLyenmhTTkPr/X9Sa fPtVTCq6sWLcg== 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 v3 04/21] btrfs/inode: allow idmapped getattr iop Date: Mon, 26 Jul 2021 12:27:59 +0200 Message-Id: <20210726102816.612434-5-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1018; h=from:subject; bh=l6wkerxVBoxvyXKw1y2h5evJrMKJx9iq19n2UV1jl+g=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOMruoT5fkWdtD285sJl5skHrH3bPFcLPbzwElNXotL C8W2dZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEwktJvhD88+89rP65wuP6kp4p7YV/ HcYikT57IDHobGM2alMFVvEmX475wZeY4xI0xi2fsCa+s3jA9a8/cLlH5pXbYk9mGzG4s5NwA= 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 --- 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 Mon Jul 26 10:28: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: 12399053 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 E06E2C4338F for ; Mon, 26 Jul 2021 10:29:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C7EBA60F5B for ; Mon, 26 Jul 2021 10:29:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232557AbhGZJsg (ORCPT ); Mon, 26 Jul 2021 05:48:36 -0400 Received: from mail.kernel.org ([198.145.29.99]:35390 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232250AbhGZJse (ORCPT ); Mon, 26 Jul 2021 05:48:34 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id E764A60F59; Mon, 26 Jul 2021 10:29:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295342; bh=kpx5QYBXpn0Br9PAnUYNdx1tMbbnHY1puQW3yLcxeF0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AweUurYe6GPwirSj0X4cgTO6znpCaR2RmWtROV9ncStrlK70UW7FKmol/NBYPD8XY NChQSe7LWcdxisqVb3cenS/SDFdw+1aZyT12HVCu45rfNGqiKlVIA8ofMCxYK8fcyO E/Ay3M3BrbZE/2Q8UTkm76INjNSrl94ibLgH/t6VnrIa+txUkhoUEc5aO5oyguWBKC dDhCztm6Y09p0QHWpgMFlUWYJfk0nR1QQ2JbQ4ns5+CDh6l00TK+q1L4gf52nnuQ4v YCIE9Q1I0kEgPD69mZ1XaQ7JYimU6Cs5xm3Hfad0Ay8R3QU+cmSBrZcWdWokx2wHB8 98nEJdMlo4STQ== 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 v3 05/21] btrfs/inode: allow idmapped mknod iop Date: Mon, 26 Jul 2021 12:28:00 +0200 Message-Id: <20210726102816.612434-6-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1067; h=from:subject; bh=GAvlJHhe9fXm4U2xSff3aw9XTnhJbq+w9VI6tjK3rXs=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zPKj2tvXFYp+czhd4iB6CkZfpHFDk0G/WuV6vS1D2T6 r9fqKGVhEONikBVTZHFoNwmXW85TsdkoUwNmDisTyBAGLk4BmEhXFyPDkVMiqms7p2m1/BQxVkzlev uyZ8XWih9e1tKveWIki/OUGX6zbL1grVdtc+bCgsrfszPKffWsovsfTzizImjl6Ts/QioYAQ== 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 --- 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 Mon Jul 26 10:28:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399055 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 4CEA1C4338F for ; Mon, 26 Jul 2021 10:29:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 34DA860F5C for ; Mon, 26 Jul 2021 10:29:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232594AbhGZJsh (ORCPT ); Mon, 26 Jul 2021 05:48:37 -0400 Received: from mail.kernel.org ([198.145.29.99]:35420 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232508AbhGZJsg (ORCPT ); Mon, 26 Jul 2021 05:48:36 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7BF2E60F55; Mon, 26 Jul 2021 10:29:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295345; bh=tLSen11iAR9GdQZv6PpVB/AxFlVu0VG8XYvXJXlyW74=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Cdyij6PYZ5iFWr0Ng+x5JANHLjZOtVcmle0nFhT8fGfcRtHTPRE3Nn5a6uB5INxbR j9fjkUcowhKU02aEHy90/zhdPzu52IJgc0Y/POsaB6OjCksay7WRo9FsoUT/kUBWs3 DdCeqcbaNwvbKQnmElGS+aeA2bhNEmUMhkuEcW5V/T05NrFgGpaux5xlJd8ryGXdbr zs8vIQ44R0LXJSDLGm32qzlf8o1GMi47P+mu3Himu8e9EIbqzI3ZdwP/wQzzJI1i+g xVTGeirKyXoPC2A+3fJQINitE81Pf1jKh+pNh5RH+iOpTcBn5DOKQ9VfrSPt9qXuW4 ftLzEi3QyX88g== 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 v3 06/21] btrfs/inode: allow idmapped create iop Date: Mon, 26 Jul 2021 12:28:01 +0200 Message-Id: <20210726102816.612434-7-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1069; h=from:subject; bh=Fxd4ihntjwBZvMhLCDp2LbIgtdbDP7kXZd04ZOSOtso=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNavonz5b2rk1h61Yr4nvhOOPDN+DOXyIvye7u0ltgx 7+ue1lHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRLmNGhm4Vmz3WufEFvnrhNkczO+ WDjh6pCIvpTllz4vq5Rcl8+gx/BT8scL4ul7zWLZltW1eE7MbTx9RNT7xQE9Ytl4l5bqTLDAA= 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 --- 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 Mon Jul 26 10:28:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399057 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 BF4D6C4338F for ; Mon, 26 Jul 2021 10:29:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A5EB860F5E for ; Mon, 26 Jul 2021 10:29:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232690AbhGZJsl (ORCPT ); Mon, 26 Jul 2021 05:48:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:35448 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232508AbhGZJsi (ORCPT ); Mon, 26 Jul 2021 05:48:38 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D2DF960F5A; Mon, 26 Jul 2021 10:29:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295347; bh=EmWXnuUwapnNj3nOmJY5fYmfycpsJ9kC2X5HUVUECzI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QqyytyfM6F7ll8lBOWJ5SdyVZ8P6CWlyJATRV4ytrlIklcHy3zX9F3xxIFEfAhQkX wCENLRf3U1cTNXUqU8/8DObiAxMB8ZVcuh/DHvtZczunjBrYFsMIfMNkpRAaGoKnBY QmD5CEUp4zGOmE0QFQrfgwcTS46wRW4JqenZ7JxHwxNBdVgV/60GtjlhmP2F+lYfwL ZpwwJzYwImHWRNyhfMUI1sF5NYCcgL4MqduqRDe3YpsI43DafDC1Aag473DxAJADQC vzbWLz5PdbSGpyd+Eh5/roh54xzCitiUuUfn4rHQHBu4MiT9RIXgc6gjQDmyA127Zq IBN6BRbr9PaJA== 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 v3 07/21] btrfs/inode: allow idmapped mkdir iop Date: Mon, 26 Jul 2021 12:28:02 +0200 Message-Id: <20210726102816.612434-8-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1056; h=from:subject; bh=8dKc0vp/o4w5iCOw41sKb42s/5SKTV0Dwz6fsqZNTuY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOy4Gr+Z8uvpyo28ZRey6/jamk39gkqbfxcdsvaZFkW 69x9HaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABMx+MXIMOuOpa/r94sFDDNibP4+j8 k96VIbN9Plr9Gzrr+Xt4eZNzL8M/2x51rklL+Sc35oGnxzWMtZV/iovm5bZN6hb2/2brS6ww4A 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 --- 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 Mon Jul 26 10:28:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399059 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 D0788C4338F for ; Mon, 26 Jul 2021 10:29:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B5A7760F5E for ; Mon, 26 Jul 2021 10:29:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232508AbhGZJsn (ORCPT ); Mon, 26 Jul 2021 05:48:43 -0400 Received: from mail.kernel.org ([198.145.29.99]:35486 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232334AbhGZJsl (ORCPT ); Mon, 26 Jul 2021 05:48:41 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 4EC2960F5B; Mon, 26 Jul 2021 10:29:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295350; bh=u66HxYVe+TyrQ28/KhjZbluzLoh4EA2yocxzmKJkcwg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X4sVKJ/M76Dm+ItrfFJzrBJsveNnWAGeF3/U6g6WKwu1UD3eyTaWTaUiTaqEnSgvK 2F5mtHi/RxgnZCspd0OhdEEwRDNjxwWCWe+rv/nx/tcyuM3lJu69j4tVSnJopGoS37 Ew8X61Bj89QDNcWeuPP9rB/YIXUKpBFaPH+x486eu4J2/0bCKUNx4gap/vohl9k+5l U12myPV0EKLQrOCXdrYnI6AVFWfXpF4fRDkoUZVDLLrjbaYmzcxED4OH1u/kCH16k+ TE+JCRLDpJHazYljJFykBRUX5weTop6DHDwhgub3oaVS7gwRfZEDm93hOhv4nDDN55 Btq/p/lVORMEw== 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 v3 08/21] btrfs/inode: allow idmapped symlink iop Date: Mon, 26 Jul 2021 12:28:03 +0200 Message-Id: <20210726102816.612434-9-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1070; h=from:subject; bh=tQ6M7Gi+JCLstzh1ddlt+xZ2WvCl6HfPY1NLHdvxQhw=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNOEzBYFXMvy3qJ6o9jukfWJXru/t9V/3SXy0bbd5eT s/YGdZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEykMInhn+2voun/p243FC/odwkrX9 h9L0RrYw7D1S/npWQC3i5qP8nIsJyRrZ/jx7vbquY9CWWHpC2myM79dyVXU+/uEddDy51jmAE= 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 --- 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 Mon Jul 26 10:28:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399061 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 7FFE6C432BE for ; Mon, 26 Jul 2021 10:29:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 68AA960F6D for ; Mon, 26 Jul 2021 10:29:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232450AbhGZJsq (ORCPT ); Mon, 26 Jul 2021 05:48:46 -0400 Received: from mail.kernel.org ([198.145.29.99]:35518 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232792AbhGZJsn (ORCPT ); Mon, 26 Jul 2021 05:48:43 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D77B360F5D; Mon, 26 Jul 2021 10:29:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295352; bh=ygTqKOWf4SfH9jIqd4biFcbtKPbw68yJ5OSZL7koLME=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DhYqhpPjKt1GsjpT/bj/pdX0gfnp8fMQbKi9PyEZVKVv+qIrjzEJ1vyOeQveK7mo5 oVTv8k1VPiyUBhdTsenhYIn7vBwvGyuskX3dRXLslRaMzT7m5rZqB4+KZ+P9xhw+p6 W4Fq00xg1LbELPXqoFPudxcDwET1jcT0rges6vSB1IZ/4fotK1DQtOo/3i50fbF8Xc 4nNNyC9D6BZ05/HkHF5RlZBdYtn9WMaf/AaTbZnqmWKx2uYqLk4pAayujHgRnIEJp9 wLzza6vkvhYQahVZXK7ia98GaOuQ3M9PlGSS9uq5ighs7vcXFUAOWimIXYaSh0Ukid 258zXCtMFggXg== 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 v3 09/21] btrfs/inode: allow idmapped tmpfile iop Date: Mon, 26 Jul 2021 12:28:04 +0200 Message-Id: <20210726102816.612434-10-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1064; h=from:subject; bh=t64Lw20jwnlqigBfE2nBCo7bR8oQt5qNrpJd7XIDeSs=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zM2tncJdnVpEpjck9d47UnqBL/nfF+Pbfh8csJn0aCr D7ryO0pZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACZSs5iRYfG9U82fM3LXz+bwv3AqMC FYqJqTU/fjGiXtM2y2RTdl/Bj+R1fH3PbMlFlQff1dVpfI7vrJ0/SFVvaptkguufxm/+mnbAA= 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 --- 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 Mon Jul 26 10:28:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399063 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 7449CC4338F for ; Mon, 26 Jul 2021 10:29:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 58FEA60F6D for ; Mon, 26 Jul 2021 10:29:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232828AbhGZJsr (ORCPT ); Mon, 26 Jul 2021 05:48:47 -0400 Received: from mail.kernel.org ([198.145.29.99]:35550 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232702AbhGZJsq (ORCPT ); Mon, 26 Jul 2021 05:48:46 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 5261360F5C; Mon, 26 Jul 2021 10:29:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295355; bh=ITp6zKF2ku79v6UmcBJuo2as1vyVpaYwaE3ZzHNVVDI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k3VRPffF8whPjl/lUkMsKXFgajDh3N9OBRyiVKukYHn4rbJl1LsGE+fIWxoWNw0+o GrkmwqsJ+fzp4ZyFqpl8ayWd4+vFSkej+NQlx4+d1w0uh9IHZqGfrunrlBQ50gT+56 pcPmky9+/DR9bZ3/3vEVfJCATrhzdCY1CXCJ+nUkN25VsHPFpbtgdw99kfYa+fjDU4 KplMEL/qHB68NXDWbeZU7rYhNGLf8DqiQxwI6/B/Lq2jEIbCsH9oHMG4SxQiD0uREy mSiYbsw5aaIZAMPwwfRk1Wgpp0zr89LJl2Yc5g1BOQtgkTb44IMV18vym/y1zF0sAC ZDUfK+gp44owA== 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 v3 10/21] btrfs/inode: allow idmapped setattr iop Date: Mon, 26 Jul 2021 12:28:05 +0200 Message-Id: <20210726102816.612434-11-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1469; h=from:subject; bh=HVDJ+To28YiuoIsFFLQoRX1te1c6AgCxGRFWMrI/XEM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNWnZL/Lsy28EJ549qoqZa2c20te6KFZ0YY7n9auypp y7/tHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABMRE2VkWPr8m0CZ/YR/djvu9hpEZi 5r3sFedZZ77x2dviO27nt72RkZWlzkbfgsArdeXv/HwDeNSWTWlfrv2n9tmt+tP/Nz6o4nPAA= 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 --- 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 Mon Jul 26 10:28:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399065 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 9CAE9C432BE for ; Mon, 26 Jul 2021 10:29:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8024960F22 for ; Mon, 26 Jul 2021 10:29:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232861AbhGZJst (ORCPT ); Mon, 26 Jul 2021 05:48:49 -0400 Received: from mail.kernel.org ([198.145.29.99]:35586 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232702AbhGZJss (ORCPT ); Mon, 26 Jul 2021 05:48:48 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id B6C8560F5E; Mon, 26 Jul 2021 10:29:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295357; bh=MPAYMfsjQv0SZVImWw98zWa582VM6UUm7VzLB113aAo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=O01Omb/Vn+fcUu+CfB+N0kC8kiK0jMZU6E5QBSfXOa0nsk35GNwt+At7AzCsgsguJ uH3Rwxl8ZZfT5IQcmGw1BH0uw1yzcdEyjN1nIV1f/63ZGZe7MPfPmeiqke2t2fVRVB Nv/e5YHCr1u32VrzhoLuvWnzDL4Qz68hbSpvqYd/BTG9pkhIuNZCVbXFKjdSwLYTFi M8NDEIPq6QcMG96w1h9gm3QXmAVsUsWx6XhI4i/Ow4ZqnU854szHw5jvFfHMITI5wr Zb7oJki9Mz3hf0VeJszElEpSw++37gtei0EosEqrzsh2xxW3Mt2IMaqbn7XjNiNgm2 rvt23pUGWki1w== 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 v3 11/21] btrfs/inode: allow idmapped permission iop Date: Mon, 26 Jul 2021 12:28:06 +0200 Message-Id: <20210726102816.612434-12-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1063; h=from:subject; bh=ZFYju59GbQm74bJbcLi8uQ3c4qnWX4CuILf3v68DxcI=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOetjPJgNVjzdlSuy2i85kWME4+Vnb28H9fna4NLTMO SgVO7ihlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZiI+z9Ghqf5Js+3vr39OOvG4Xe763 cxsX/SqWJi3bd1fZtT00azT3aMDL8MmJpMbsk/uFm/7cxvTfsbX2q9GJhYdwjMWyUVOOnKR04A 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 --- 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 Mon Jul 26 10:28:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399067 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 6BD4DC4338F for ; Mon, 26 Jul 2021 10:29:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4F94860F6E for ; Mon, 26 Jul 2021 10:29:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232877AbhGZJsw (ORCPT ); Mon, 26 Jul 2021 05:48:52 -0400 Received: from mail.kernel.org ([198.145.29.99]:35638 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232702AbhGZJsu (ORCPT ); Mon, 26 Jul 2021 05:48:50 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 031C160F6B; Mon, 26 Jul 2021 10:29:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295359; bh=GsGtyy96wqnROlp2/bZoRye9OVCjTkpIpqpGkIk4cCE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lViMR8Ay2QRlxKnznOClQ7bQllNwz46EXJVEvfVWkXvCpYY/AmndSMCbatAn2e0rF 3pfg4g6uRqjUZY6KXFYc9btb1g/ZrKWGgTBT6wIWUBiQNwnnGHsPqOiYlvVWh8gt4K brr0gsNDDAVdc27qqRMvDV5FzyRzEATCt08xbzzV/RnjEYF1LcOURGVwIgAmmTt6UX 0YdiOE1m1px18umYXyMkq+uyunHO/VE4aLAwO8SydeNh1ITjCHV3xjo1QvS3YMhZjT pgi0Xa+1gdEqDqHCiZOgMAR+cwyYd9T+V4fPmC8olX1K/j6FA/iqUy4V59akms53Cx Ahc1FYJKj6PEw== 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 v3 12/21] btrfs/ioctl: check whether fs{g,u}id are mapped during subvolume creation Date: Mon, 26 Jul 2021 12:28:07 +0200 Message-Id: <20210726102816.612434-13-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1711; h=from:subject; bh=xz/8Sa8HC1Rq1Hy4H8Ifh+XNuNCa36O1jKpi/ZdNlmk=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOxTl9VopScblv1LGIZX1ykwFKuj6HbItgZLh00/2l+ 4dejjlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgInsWcPI8EB3ipxIw9YQ36+JBvc3Cj zfxWbm/o/vyAGOSpcjfztjnRgZFv+Z8+hA2oKHqx5wSKQnN30XLT4Ved+zvVwu5fCXur9FnAA= 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 --- fs/btrfs/ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0ba98e08a029..7a6a886df7c4 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -870,6 +870,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; + if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + return -EOVERFLOW; return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); } From patchwork Mon Jul 26 10:28:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399069 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 21CB1C432BE for ; Mon, 26 Jul 2021 10:29:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0CF7460F6E for ; Mon, 26 Jul 2021 10:29:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232902AbhGZJsy (ORCPT ); Mon, 26 Jul 2021 05:48:54 -0400 Received: from mail.kernel.org ([198.145.29.99]:35676 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232900AbhGZJsx (ORCPT ); Mon, 26 Jul 2021 05:48:53 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 305C860F22; Mon, 26 Jul 2021 10:29:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295362; bh=3/h3UunTvPtsiyAxQ52cCP1o3Jyzr1Yu0sPuuzDtCuU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mVYA/9MoYDnAyRagNR3b0wHLsSF3bR/zDek+FjswNQx8jIVbXskMTXjjcIvXKAKiz 7eBPNQ/xP7haHPRvfrmbWwu+S1YjhBUtueoj1QncVoBDLF9d860d20CcB8W6Jxvjkd zpSOnSJY/FZhL+3pWQHdqiRopfFfutkUfwYrFRgGcgqmqJ1QfkjVyS30RfIpBqTTDV 9HBPDz69Th2DKYbxvM0ZUshNlwDvPDuoMXEABkLM6IKRdh1oRHRlIjNa8Kx7cWoKEY Kb563pI7LLadnoxCb1/4riQfnT1cP3vvFUgXT+TgufMP9GKEPwou51Z6bc5fZsUCFF JmLPKSx/z+nfA== 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 v3 13/21] btrfs/inode: allow idmapped BTRFS_IOC_{SNAP,SUBVOL}_CREATE{_V2} ioctl Date: Mon, 26 Jul 2021 12:28:08 +0200 Message-Id: <20210726102816.612434-14-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8241; h=from:subject; bh=9Wd0kCZFzhhH9Sjv9j2wc2z46o0nh4d7g45KrcsD2RI=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOJSnYUuzGXo6V58vIbNeFxt72cJsRIiz3KjQzZNVf5 v1xmRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwESK7zIyLM4Tvt0T8Gr/w6lSz39knH 3Wu8oj4Wz2/Flde9Pnb6n+0MDIsNtTRNKbR2lW2UWtM9bpUnGnIlLb9O8uMGjbvS0vPzuOEwA= 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 --- fs/btrfs/ctree.h | 3 ++- fs/btrfs/inode.c | 5 +++-- fs/btrfs/ioctl.c | 48 ++++++++++++++++++++++++++++-------------------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e5e53e592d4f..ee1876571b3f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3145,7 +3145,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, struct extent_state **cached_state); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, struct btrfs_root *new_root, - struct btrfs_root *parent_root); + struct btrfs_root *parent_root, + struct user_namespace *mnt_userns); void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state, unsigned *bits); void btrfs_clear_delalloc_extent(struct inode *inode, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 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..be52891ba571 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -492,8 +492,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid) return 1; } -static noinline int create_subvol(struct inode *dir, - struct dentry *dentry, +static noinline int create_subvol(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry, const char *name, int namelen, struct btrfs_qgroup_inherit *inherit) { @@ -638,7 +638,7 @@ static noinline int create_subvol(struct inode *dir, goto fail; } - ret = btrfs_create_subvol_root(trans, new_root, root); + ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns); btrfs_put_root(new_root); if (ret) { /* We potentially lose an unused inode item here */ @@ -864,15 +864,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) } /* copy of may_create in fs/namei.c() */ -static inline int btrfs_may_create(struct inode *dir, struct dentry *child) +static inline int btrfs_may_create(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *child) { if (d_really_is_positive(child)) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) + if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) return -EOVERFLOW; - return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); } /* @@ -881,6 +882,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) * inside this filesystem so it's quite a bit simpler. */ static noinline int btrfs_mksubvol(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *snap_src, bool readonly, @@ -895,12 +897,13 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (error == -EINTR) return error; - dentry = lookup_one_len(name, parent->dentry, namelen); + dentry = lookup_mapped_one_len(mnt_userns, name, + parent->dentry, namelen); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_unlock; - error = btrfs_may_create(dir, dentry); + error = btrfs_may_create(mnt_userns, dir, dentry); if (error) goto out_dput; @@ -922,7 +925,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, if (snap_src) error = create_snapshot(snap_src, dir, dentry, readonly, inherit); else - error = create_subvol(dir, dentry, name, namelen, inherit); + error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit); if (!error) fsnotify_mkdir(dir, dentry); @@ -936,6 +939,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, } static noinline int btrfs_mksnapshot(const struct path *parent, + struct user_namespace *mnt_userns, const char *name, int namelen, struct btrfs_root *root, bool readonly, @@ -965,7 +969,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent, btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); - ret = btrfs_mksubvol(parent, name, namelen, + ret = btrfs_mksubvol(parent, mnt_userns, name, namelen, root, readonly, inherit); out: if (snapshot_force_cow) @@ -1794,6 +1798,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, } static noinline int __btrfs_ioctl_snap_create(struct file *file, + struct user_namespace *mnt_userns, const char *name, unsigned long fd, int subvol, bool readonly, struct btrfs_qgroup_inherit *inherit) @@ -1821,8 +1826,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, } if (subvol) { - ret = btrfs_mksubvol(&file->f_path, name, namelen, - NULL, readonly, inherit); + ret = btrfs_mksubvol(&file->f_path, mnt_userns, name, + namelen, NULL, readonly, inherit); } else { struct fd src = fdget(fd); struct inode *src_inode; @@ -1836,16 +1841,17 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, "Snapshot src from another FS"); ret = -EXDEV; - } else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { + } else if (!inode_owner_or_capable(mnt_userns, src_inode)) { /* * Subvolume creation is not restricted, but snapshots * are limited to own subvolumes only */ ret = -EPERM; } else { - ret = btrfs_mksnapshot(&file->f_path, name, namelen, - BTRFS_I(src_inode)->root, - readonly, inherit); + ret = btrfs_mksnapshot(&file->f_path, mnt_userns, + name, namelen, + BTRFS_I(src_inode)->root, + readonly, inherit); } fdput(src); } @@ -1869,8 +1875,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, return PTR_ERR(vol_args); vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, false, NULL); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + false, NULL); kfree(vol_args); return ret; @@ -1928,8 +1935,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file, } } - ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, - subvol, readonly, inherit); + ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file), + vol_args->name, vol_args->fd, subvol, + readonly, inherit); if (ret) goto free_inherit; free_inherit: From patchwork Mon Jul 26 10:28:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399071 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 3F031C4338F for ; Mon, 26 Jul 2021 10:29:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 26A4F60F6C for ; Mon, 26 Jul 2021 10:29:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232507AbhGZJs4 (ORCPT ); Mon, 26 Jul 2021 05:48:56 -0400 Received: from mail.kernel.org ([198.145.29.99]:35734 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233006AbhGZJsz (ORCPT ); Mon, 26 Jul 2021 05:48:55 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 695D560F57; Mon, 26 Jul 2021 10:29:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295364; bh=yyMFOc9g6m0/zqm3+tVIqk41/E0f9Fd9n8V0G2JHjb8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YeDEqL4n2CyHsRLiWKhpuhKivnUvAXv1fdTau0YJlVLkRmDSA9SJjmmGveDo8fJ63 6B4YMI0v8iFpOBnngTcwSyU6bUgeV03c/0mwZ/far/8jmJ+AWrFTN5AvEPbXPSQUXe OquFNwWOIPlTyNTELLhfRfoIvVmA9bGUZvGYpKX/dKwPtqHiFoNO1rdnVfMyMY6VnG mu4EfD8pRP2gwpnnUPyU+5+dxuGOz2wP/9xlurQBM6Pahl8S0JzstrJF1olNUwRXD1 2q6WUPUh3RXOOYQhueGu0MNLZd1yUBLp/f2rgobKiE9G619wBGqsCZo6jntk9nQEXN oU+eqd2bXs89Q== 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 v3 14/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SNAP_DESTROY{_V2} ioctl Date: Mon, 26 Jul 2021 12:28:09 +0200 Message-Id: <20210726102816.612434-15-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8002; h=from:subject; bh=PYjV6zTHbGQznPIZyBcY3ar0pwBZk6ZljbrDpNRSx8U=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zN5vXnrteM3c6I5ZtW8fT8/7cnbtfNTktIeF74zTxCv P2b4taOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAiRQoMf4X6DvtNtsu5b/B22uovyY IXS4I/5qxxcpybPr/kw+XoinRGhkl/mEPO7Dy17cOJDftb7c5pbV3heOQ5+7HTf7g4roX4dTACAA== 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 Signed-off-by: Christian Brauner Reviewed-by: Qu Wenruo --- /* 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. --- fs/btrfs/ioctl.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index be52891ba571..488e2395034f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -830,7 +830,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, * nfs_async_unlink(). */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *victim, int isdir) { int error; @@ -840,12 +841,12 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) BUG_ON(d_inode(victim->d_parent) != dir); audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); + error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(&init_user_ns, dir, d_inode(victim)) || + if (check_sticky(mnt_userns, dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim))) return -EPERM; @@ -2915,6 +2916,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, struct btrfs_root *dest = NULL; struct btrfs_ioctl_vol_args *vol_args = NULL; struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL; + struct user_namespace *mnt_userns = file_mnt_user_ns(file); char *subvol_name, *subvol_name_ptr = NULL; int subvol_namelen; int err = 0; @@ -2942,6 +2944,18 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { + /* + * Deleting by subvolume id can be used to delete + * subvolumes/snapshots anywhere in the filesystem. + * Ensure that users can't abuse idmapped mounts of + * btrfs subvolumes/snapshots to perform operations in + * the whole filesystem. + */ + if (mnt_userns != &init_user_ns) { + err = -EOPNOTSUPP; + goto out; + } + if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; goto out; @@ -3026,7 +3040,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT); if (err == -EINTR) goto free_subvol_name; - dentry = lookup_one_len(subvol_name, parent, subvol_namelen); + dentry = lookup_mapped_one_len(mnt_userns, subvol_name, + parent, subvol_namelen); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock_dir; @@ -3068,14 +3083,14 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (root == dest) goto out_dput; - err = inode_permission(&init_user_ns, inode, + err = inode_permission(mnt_userns, inode, MAY_WRITE | MAY_EXEC); if (err) goto out_dput; } /* check if subvolume may be deleted by a user */ - err = btrfs_may_delete(dir, dentry, 1); + err = btrfs_may_delete(mnt_userns, dir, dentry, 1); if (err) goto out_dput; From patchwork Mon Jul 26 10:28:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399073 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 7C309C4338F for ; Mon, 26 Jul 2021 10:29:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6385460F8F for ; Mon, 26 Jul 2021 10:29:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233080AbhGZJs6 (ORCPT ); Mon, 26 Jul 2021 05:48:58 -0400 Received: from mail.kernel.org ([198.145.29.99]:35798 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233060AbhGZJs5 (ORCPT ); Mon, 26 Jul 2021 05:48:57 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 73F2360F6F; Mon, 26 Jul 2021 10:29:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295366; bh=l339qKbv+7vkm+hLeAZ6MnCZgWAk9HtNqHGY9Zi5wUc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tOLh0LM231z4RkCY09luCsxRQMAs/9NDcaeaJZjIFECSg7gmwDkrB2mnSquROP6vG clEykxVseAo1Qb6t7RAS6MTx7H4wuQ41tPzp44sZkOeJxGl9vlwDNRwPWt9IFlw9eE Gd2zj9h2f9SSsDi9Vttoazw0pzXfEEvi3/BAg2IYf8PoJISOUzOBEI3OQz4KDrOLoX 2YL++9T4qRjKKEv82tn1kv/9Yqok98MfeDZOaQPajOOCF+eEEISHl7Ja61cYX0Eujj 2wbToaquLWDnt94oa8QsZ4/YrQ7Le+uKC4dd7aGc6af4ustncFJGnTANxq1lvm/aVo kVhfSv0+hl8MA== 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 v3 15/21] btrfs/ioctl: relax restrictions for BTRFS_IOC_SNAP_DESTROY_V2 with subvolids Date: Mon, 26 Jul 2021 12:28:10 +0200 Message-Id: <20210726102816.612434-16-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2897; h=from:subject; bh=qRkKrST/903chvQyBuvEhRegfPOGUOR8j4rrrB7NSfU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zNdpzs1h4k7x81WP5MnsmndXU+vyUluM1d5qE5e8ott JsPTjlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgIk4dzD8r5H0EyxL2uIrP9kvTHNqV4 zuHt8D5onPnlWmBArtZfhixciwe2L42Zf/Sj/8a3T7kVSbmHSw4u+pFce3HGBib1YWfarLCAA= 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 Signed-off-by: Christian Brauner Reviewed-by: Qu Wenruo --- /* v2 */ unchanged /* v3 */ 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 488e2395034f..b9864d63ffbf 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2944,17 +2944,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, if (err) goto out; } else { - /* - * Deleting by subvolume id can be used to delete - * subvolumes/snapshots anywhere in the filesystem. - * Ensure that users can't abuse idmapped mounts of - * btrfs subvolumes/snapshots to perform operations in - * the whole filesystem. - */ - if (mnt_userns != &init_user_ns) { - err = -EOPNOTSUPP; - goto out; - } + struct inode *old_dir; if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; @@ -2992,6 +2982,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = PTR_ERR(parent); goto out_drop_write; } + old_dir = dir; dir = d_inode(parent); /* @@ -3002,6 +2993,20 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, */ destroy_parent = true; + /* + * On idmapped mounts, deletion via subvolid is + * restricted to subvolumes that are immediate + * ancestors of the inode referenced by the file + * descriptor in the ioctl. Otherwise the idmapping + * could potentially be abused to delete subvolumes + * anywhere in the filesystem the user wouldn't be able + * to delete without an idmapped mount. + */ + if (old_dir != dir && mnt_userns != &init_user_ns) { + err = -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 Mon Jul 26 10:28:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399075 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 A5B48C4338F for ; Mon, 26 Jul 2021 10:29:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8773060F70 for ; Mon, 26 Jul 2021 10:29:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233109AbhGZJtB (ORCPT ); Mon, 26 Jul 2021 05:49:01 -0400 Received: from mail.kernel.org ([198.145.29.99]:35832 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233060AbhGZJs7 (ORCPT ); Mon, 26 Jul 2021 05:48:59 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 9FA4D60F6D; Mon, 26 Jul 2021 10:29:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295368; bh=IFjkzMiUQ5ioN2dhiZdk7KTgePBKp6UnzuMZ5UkRUj8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rWV89RM48f64dYe7d+MWpJ2KXyKox0Mml5TAB3WjexB+t3J7w2WYhXw15d2R+44dH sE/3FBue6eODz6fSPJMbV8Oz4a8bjgu5TtY2C/xEG3lcWx4BKGXNY5ivYokwyaSNDF Nn/VQb3mkcS4uGer7qHu2bBMILKhBz8HY5Lzb2/PIFTiu+wCTLU/8A0PX1MbT+WEls Zoqoz+Iga/o0BaY0z8L0/BQ4Q+fNRO7TqBoOjnNY/MdJAXJqOxUjMwr0Zf1sKWPNfU GmaML0Gby/z8NiB4U6+1Rc3IKY5z0+sXlNtruse2+P/UjI1CoEhwd7sE3qow1i4Wca mD1tY9AiqF+YA== 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 v3 16/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SET_RECEIVED_SUBVOL{_32} ioctl Date: Mon, 26 Jul 2021 12:28:11 +0200 Message-Id: <20210726102816.612434-17-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2199; h=from:subject; bh=Mz0JOeEf7UECbFH5BcSEUTjC7ZP48Ll3zUev6VaOoGY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zMVs4tYcO1EoRi7VIlqbOOezk/ii9iZ6hY++1P/5eGj f5mRHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABO5JsDIsDF4Z3xRj2Xd5NeMopasok 2/fv3cGpQZnaccLRi3yfdZAsN/54PN6gGy1mU9cyyl//ZXtjqm/v21Me7fsXZefZ1JeZ/ZAA== 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 --- 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 b9864d63ffbf..983d91429099 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4466,6 +4466,7 @@ static long btrfs_ioctl_quota_rescan_wait(struct btrfs_fs_info *fs_info, } static long _btrfs_ioctl_set_received_subvol(struct file *file, + struct user_namespace *mnt_userns, struct btrfs_ioctl_received_subvol_args *sa) { struct inode *inode = file_inode(file); @@ -4477,7 +4478,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, int ret = 0; int received_uuid_changed; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(mnt_userns, inode)) return -EPERM; ret = mnt_want_write_file(file); @@ -4582,7 +4583,7 @@ static long btrfs_ioctl_set_received_subvol_32(struct file *file, args64->rtime.nsec = args32->rtime.nsec; args64->flags = args32->flags; - ret = _btrfs_ioctl_set_received_subvol(file, args64); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), args64); if (ret) goto out; @@ -4616,7 +4617,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, if (IS_ERR(sa)) return PTR_ERR(sa); - ret = _btrfs_ioctl_set_received_subvol(file, sa); + ret = _btrfs_ioctl_set_received_subvol(file, file_mnt_user_ns(file), sa); if (ret) goto out; From patchwork Mon Jul 26 10:28:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399077 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 EC4D8C4320A for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D239D60F56 for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233131AbhGZJtH (ORCPT ); Mon, 26 Jul 2021 05:49:07 -0400 Received: from mail.kernel.org ([198.145.29.99]:35880 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233115AbhGZJtB (ORCPT ); Mon, 26 Jul 2021 05:49:01 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D70C960F6E; Mon, 26 Jul 2021 10:29:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295370; bh=y6KzHjWlmnsTGV4uyTXZCdodPVX0VoMo30cGiTB2UlM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nbZ722++LAmCZmeFS9/LqU6S4k8gyEIX3PBCtaamByYONkw7LGqseMHDkfyr/LUUw Djd78xDJZEoEnddFSmjThlDFeRJ+hWoHawHne+MSOj9SSKXWdLlsc1u7DVWgt7LPTS pi3ctXOYFiNr//Sdt/GVspReCjfgfPntazI5l8/VHoOlwq+CmXsMGuqFbAUD8Aclmv tGw/iZ9+TeysK/TFT639KHcCy2qLGKS4T8yvKM0uVHPshM0NIZ7/Anjcf2sxSS8480 pAgD+uMsbf9YsOGSUxTPiLOLk+Vo8ml2OCTluqyeV5EQ/kRrWOeVswpcXYNdVEwlyc mLqYqeyA0T4XQ== 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 v3 17/21] btrfs/ioctl: allow idmapped BTRFS_IOC_SUBVOL_SETFLAGS ioctl Date: Mon, 26 Jul 2021 12:28:12 +0200 Message-Id: <20210726102816.612434-18-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1342; h=from:subject; bh=sSu4N2VK497MAT0dv+v5wLFFUYg0vay7OWqfzmUN778=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zOVWpmtnRf1ni90rUtzdonNopbQtUze97Xk/pe9FRD1 utHUUcrCIMbFICumyOLQbhIut5ynYrNRpgbMHFYmkCEMXJwCMJGrLgz/dGedO7FGcL+Oyow1BZLyYb uWcOuZ63gppf7d3ls9ofpVLSPD/rp6wfd3Z67Ze4bl0WHex28NJAS/LuNgsPZ1nznX/TcbHwA= 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 --- 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 983d91429099..e4571522427a 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1982,7 +1982,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, u64 flags; int ret = 0; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(file_mnt_user_ns(file), inode)) return -EPERM; ret = mnt_want_write_file(file); From patchwork Mon Jul 26 10:28:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399079 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 D7EA0C4338F for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BDB7E60F22 for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233132AbhGZJtI (ORCPT ); Mon, 26 Jul 2021 05:49:08 -0400 Received: from mail.kernel.org ([198.145.29.99]:35936 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233060AbhGZJtE (ORCPT ); Mon, 26 Jul 2021 05:49:04 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1D20360F58; Mon, 26 Jul 2021 10:29:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295373; bh=iWgE926CCyVowkforqFLZ2w+Vq+4GnJ51xEcGRn1WKQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gno7nhm9+lmWPnvEqsItvuYRZuydcrFfZIsNWg1ojhVNH13qgvCkjQP4ngDNFG1mz CXZ8Vwcn3DfZxuBy/4m+0tX5Fp497gyjv9w0zGlfjj/RwR+xI1g0VhyV5/I0DEwCre ljTRNy9jiQh9Wu+9r8o6tMz1iamjO+Mn5Y4LtzueeDbZf+KHfpT5cGbzOKmBm/BQxv G8TVAUf6nC2BivX0E1yfwpM53XuHVV04hCV3GdS3/O06CTJq6njBwDLR4Lj27nQbNk Nu9Eur2Gme/x41a9q0yIqSlMRMDQ5DiCYpiUEuB0eWD/RScm+rnDzDiKwf0QInDQWv /Yc7poXrLMtYw== 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 v3 18/21] btrfs/ioctl: allow idmapped BTRFS_IOC_INO_LOOKUP_USER ioctl Date: Mon, 26 Jul 2021 12:28:13 +0200 Message-Id: <20210726102816.612434-19-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3835; h=from:subject; bh=nHK9LaSXzVTOpj7E8nQPoIN2myNEl0+yllknGwd4rtQ=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zPluConvGvZ3xebXglaRzMYzLXr2rpxtdhOT+NpFzSM T7mJd5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzk/EdGhuUxn9dPVJyafHT+DU5Lm5 XLhQPlLh19Xa8QcyZ2ZZnVfn2Gf7ZMWpkX1sSHnjc2ZgnuKF7Y3Lp/xoqI/EuiDHN+POV9yQoA 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 --- 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 e4571522427a..159bff0c07c1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2440,7 +2440,8 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, return ret; } -static int btrfs_search_path_in_tree_user(struct inode *inode, +static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns, + struct inode *inode, struct btrfs_ioctl_ino_lookup_user_args *args) { struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; @@ -2538,7 +2539,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode, ret = PTR_ERR(temp_inode); goto out_put; } - ret = inode_permission(&init_user_ns, temp_inode, + ret = inode_permission(mnt_userns, temp_inode, MAY_READ | MAY_EXEC); iput(temp_inode); if (ret) { @@ -2680,7 +2681,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) return -EACCES; } - ret = btrfs_search_path_in_tree_user(inode, args); + ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args); if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) ret = -EFAULT; From patchwork Mon Jul 26 10:28:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399081 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 F137EC4320E for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E045160F22 for ; Mon, 26 Jul 2021 10:29:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233192AbhGZJtI (ORCPT ); Mon, 26 Jul 2021 05:49:08 -0400 Received: from mail.kernel.org ([198.145.29.99]:35978 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233130AbhGZJtG (ORCPT ); Mon, 26 Jul 2021 05:49:06 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7BEEA60F5C; Mon, 26 Jul 2021 10:29:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295375; bh=gjemQfdrWrDl+K8IOVzTuAj4q3jGNlfjnQZx/F7SFE8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Xw6LnILXevM6G5HNDUxMzegysmbQRDnBdlShGNeMJiHVaRtUvMNg+XlXm4Je/HKPt 3JzuNqvZTjDYfXUITUt/uPAbBBZwwYab6Ks/7rA43ajE6ci19nTBzNu9E7O7YbjjQ9 kqWLg/CAZf41r8oldRmv3KbQyv+ot/hJch5j+ZcGcVwSV4CYnIsaMnql5zk0pFv9Dv 6lJr0ZZVEfxpnMuQkCobAEWPvKFts/4EbVQsPLhMFxo0/HQdAJmGrgXY2Zzk8DJ7kA oRKJ0n53pH55jC/6fdsTWenZdpVmGiHeD4Rzq82R4pTzKLEcb/uTNtMjtHSswaDh75 p87q5DvoKZI8Q== 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 v3 19/21] btrfs/acl: handle idmapped mounts Date: Mon, 26 Jul 2021 12:28:14 +0200 Message-Id: <20210726102816.612434-20-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2807; h=from:subject; bh=1BHv/+dlej0yvpSnaL1FJS7KVyvjz8+jJgFFCTcZFhM=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zO7YCk1IUbjtvPvYOXO2aY/HFd0rXE0X39v6YVYe162 H5J3OkpZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACaiz8XIMCefg9ckzpRdbT3blvM3eA 5/fvM4m/WKRpC+yaqDkr8jVRj+x/5/pLv89E3rb06dabPdhYME9JdaRlWbzdt29Yrtl/oIXgA= 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 --- fs/btrfs/acl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index d95eb5c8cb37..c9f9789e828f 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -53,7 +53,8 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type) } static int __btrfs_set_acl(struct btrfs_trans_handle *trans, - struct inode *inode, struct posix_acl *acl, int type) + struct user_namespace *mnt_userns, + struct inode *inode, struct posix_acl *acl, int type) { int ret, size = 0; const char *name; @@ -114,12 +115,12 @@ int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, umode_t old_mode = inode->i_mode; if (type == ACL_TYPE_ACCESS && acl) { - ret = posix_acl_update_mode(&init_user_ns, inode, + ret = posix_acl_update_mode(mnt_userns, inode, &inode->i_mode, &acl); if (ret) return ret; } - ret = __btrfs_set_acl(NULL, inode, acl, type); + ret = __btrfs_set_acl(NULL, mnt_userns, inode, acl, type); if (ret) inode->i_mode = old_mode; return ret; @@ -140,14 +141,14 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans, return ret; if (default_acl) { - ret = __btrfs_set_acl(trans, inode, default_acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, default_acl, ACL_TYPE_DEFAULT); posix_acl_release(default_acl); } if (acl) { if (!ret) - ret = __btrfs_set_acl(trans, inode, acl, + ret = __btrfs_set_acl(trans, &init_user_ns, inode, acl, ACL_TYPE_ACCESS); posix_acl_release(acl); } From patchwork Mon Jul 26 10:28:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399083 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 7A018C43214 for ; Mon, 26 Jul 2021 10:29:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 64B6260F58 for ; Mon, 26 Jul 2021 10:29:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233308AbhGZJtM (ORCPT ); Mon, 26 Jul 2021 05:49:12 -0400 Received: from mail.kernel.org ([198.145.29.99]:36004 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233186AbhGZJtI (ORCPT ); Mon, 26 Jul 2021 05:49:08 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 9C6AA60F56; Mon, 26 Jul 2021 10:29:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295377; bh=viUypo+OMN3mUNmL9shbhfJJuM+fyjqfdmiq9VVckho=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YRVc8BcUskiZXISfRYc3iuHIBQHhj8DzGlEf4w2hKgyd/AS8KxtqtcCt3ZRYIYQc+ VkufH3GY5Lb+jCcaXw4MvU08kiTvXFbpUIBJCAripPmb/Gs9QfEtVo2Yyr9+WXO/Ji azjl4b6V7YWq5yX/Up7Iu8sQ1L1LFPXXl97QbrjykHOMmYYjlzATDWnmacceka4zkf ZpTN0RIwL4xRpaReCruHqu8Vv/X6JNS9Re39kXz67rrjLLwwACiMcYXNHJyIGg2Uoq eU3oYrd1VP8WLh5br16ZeFupqqCHI5miQagdAzeZqXZgCiqfhoLv60fFouzk/Gzh9q 4XDxwcPRim3aw== 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 v3 20/21] btrfs/super: allow idmapped btrfs Date: Mon, 26 Jul 2021 12:28:15 +0200 Message-Id: <20210726102816.612434-21-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1563; h=from:subject; bh=JZT0rpw1qASMxVN0tMk7Lr+GQH9r1wKgFAg3opFUEKo=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zMzvv5fPWapykKJ9u1bXz1YeKAxcOPDxztEXOY5LWpQ vVTyqaOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAinocYGRo/87Bf6zXl/mIe9329mh qj0AKukFMxIlIdkjcl97cqlTMyrJzCuSI8YuunSkdnQ1epA6J1jH0PZ1btcrtk9fS/+wczdgA= 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 --- fs/btrfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d07b18b2b250..5ba21f6b443c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2381,7 +2381,7 @@ static struct file_system_type btrfs_root_fs_type = { .name = "btrfs", .mount = btrfs_mount_root, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("btrfs"); From patchwork Mon Jul 26 10:28:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12399085 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 4EAD7C4338F for ; Mon, 26 Jul 2021 10:29:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2C0A160F57 for ; Mon, 26 Jul 2021 10:29:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233296AbhGZJtN (ORCPT ); Mon, 26 Jul 2021 05:49:13 -0400 Received: from mail.kernel.org ([198.145.29.99]:36052 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233301AbhGZJtM (ORCPT ); Mon, 26 Jul 2021 05:49:12 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 1460D60F55; Mon, 26 Jul 2021 10:29:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1627295381; bh=ReToYCrsnCgSWTrMplKLrE2gqkmqzKErsjrW+xTUPR0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dDFg3ySg5L1JG7j6GPZxeQly/lExzj9hT+9xNGE77ifNn36VWfT9vgb+Gr0U2QJjs qEvQwwJI62Fut8Qrkj6fRHQ7R51bdOPARmSBcdm+1PxY2JsDPCAmY0jyawHnoR3Pva CpCxWm7J1PtEgRY9VBNx7CNdqzjSEJdI0MVayu3yTzOmVB3OWlXx1uFWd7rc10/T/B gHrLQW1BD3JvXfqOCenfOwo51Sxa5jyrblM9uJ1CGKGMP4LV+mKv37Dxq37K/QHAZl +a6Va1v3V/j6T21vzxXQhP3YXCFDqher1XRLUGjupfVKGB+UD+EUIVKprx4Br4zr4S I6pyOGDNMRnHw== 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 v3 21/21] btrfs/242: introduce btrfs specific idmapped mounts tests Date: Mon, 26 Jul 2021 12:28:16 +0200 Message-Id: <20210726102816.612434-22-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210726102816.612434-1-brauner@kernel.org> References: <20210726102816.612434-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=108495; h=from:subject; bh=gf9TN45jPrzDKsOVXJ+IRei/n8LwQ8qGtG038Z67Vn8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMST86zP7vHLL9dhQp8CrrNoXS6PMuRc4ru9JfJx56+xS7xiT hRJxHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABP50MPIsOfvnpB55emBYeU7qnh23b RMNnZY6t/KfMFbLMvp19TetQx/uGJL9bUXOK45XRhy+/ti3qM6Pm/itwf/1gjdrtsTZyDDCAA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Christian Brauner While core vfs functionality that btrfs implements is completely covered by the generic test-suite the btrfs specific ioctls are not. This patch expands the test-suite to cover btrfs specific ioctls that are have required changes to work on idmapped mounts. We deliberately don't use the libbtrfsutil library as we need to know exactly what ioctl's are issued and we need to be in control of all privileges at all times. This test-suite currently tests: - BTRFS_IOC_{SNAP,SUBVOL}_CREATE_V2 - subvolume creation on idmapped mounts where the fsids do have a mapping in the superblock - snapshot creation on idmapped mounts where the fsids do have a mapping in the superblock - subvolume creation on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot creation on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume creation on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot creation on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume creation on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot creation on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - BTRFS_IOC_SNAP_DESTROY_V2 - subvolume deletion on idmapped mounts where the fsids do have a mapping in the superblock - snapshot deletion on idmapped mounts where the fsids do have a mapping in the superblock - subvolume deletion on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot deletion on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - unprivileged subvolume deletion on idmapped mounts where the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - unprivileged snapshot deletion on idmapped mounts where the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - subvolume deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - snapshot deletion on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock and the filesystem is mounted with "user_subvol_rm_allowed" - BTRFS_IOC_SUBVOL_SETFLAGS - subvolume flags on idmapped mounts where the fsids do have a mapping in the superblock - snapshot flags on idmapped mounts where the fsids do have a mapping in the superblock - subvolume flags on idmapped mounts where the fsids do not have a mapping in the superblock - snapshot flags on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume flags on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - snapshot flags on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume flags on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - snapshot flags on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock - BTRFS_IOC_INO_LOOKUP_USER - subvolume lookup on idmapped mounts where the fsids do have a mapping in the superblock - subvolume lookup on idmapped mounts where the fsids do not have a mapping in the superblock - subvolume lookup on idmapped mounts where the caller is located in a user namespace and the fsids do have a mapping in the superblock - subvolume lookup on idmapped mounts where the caller is located in a user namespace and the fsids do not have a mapping in the superblock Cc: Chris Mason Cc: Josef Bacik Cc: Christoph Hellwig Cc: David Sterba Cc: fstests@vger.kernel.org Cc: linux-btrfs@vger.kernel.org Signed-off-by: Christian Brauner --- /* 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. --- 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