From nobody Mon Dec 1 22:36:28 2025 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DCC6B26ED51 for ; Sat, 29 Nov 2025 09:15:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407721; cv=none; b=pqS4wmccvBA5tMfnbTz3m8CZ2nUfUazATFs1GuKXvC0UPDg/4sMf1uprRpBo9gHecTOXSmp4u9vdZ8ZjfbuTuad6zUjJdD50Nz0mZMykT1/PSxRKhvVpvm7e+Xvfvv0Xf50iwpyAZbm5DcEgWnsBKg7+txJA3e7Z6bAnKzf/nrg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407721; c=relaxed/simple; bh=1H3tSySb8RYZ9Xz952vACbtvWTupkLqx1w90JROwSJk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GCHc8zzoYa7SRlhzIFCt63OAeQt4Owi7ptI4Kyk3KwA4N6E7YWYGChAOG5u6MlJCsCFB49A+lJonNUqOwIIUdVt+rfeAbe4u0VB6g8EQhm8TZEidFSaDBa1TrNTq02Uo5NjnGDIBmHyI1TuNyU7KpJn/1zb9/7jIYPpJZkl724Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gTZRvdLq; arc=none smtp.client-ip=209.85.210.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gTZRvdLq" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-7aad4823079so2265690b3a.0 for ; Sat, 29 Nov 2025 01:15:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764407719; x=1765012519; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RTLbYX7bNTCm4TW5N/uHN1/H0O/wwjH+DS42MIfV7UI=; b=gTZRvdLqFAb/4YHMcqeFdjmsrcmNzl0o75pRwHqejAnlAtjEBSfJ3rdcmE0KCoAMxz 8aDfS28qehGIKRHuYCr+Q7HBiW6uRw4SaB4o+IGn7jh0DTlvLKWaE2UfjMhnaKzy8Mun 5z3I4e4j9lk1e7lpLgHEKzGTP6NOXg+6Y6D4GIS1CFY5bzTXvbnITAxX6q3Grs/VMpWI kDJaW5B1yWNj9PTLfthJUcvgWKyhSqlSiaguH4zcsOZZCknt4wJo0p1boB5w12ainCgF riPiv0yOm+1gxtUeHLxYnocVqVtzWJV/Iv5vkicp94K6cP0Fs3gJQVKHsFLk8KwBDuPU 3mCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764407719; x=1765012519; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RTLbYX7bNTCm4TW5N/uHN1/H0O/wwjH+DS42MIfV7UI=; b=OI430fFqtYSs3pYb/3Pqe+6xLenOCiZpgFiM/nLD+JgST5mAv5hg6V8gnf06a3yX5a 2AkvmPeCHXrnYDolqgJ2ETpG2+BmNxkp8nzkoG8ZjCCYWljC8afCglTeV8DKNm5PLMbR nuHm/wvmT+3FAHPALOqeAlihkGoVKczeRmF3soPJicSBbkAe6r7cKbkwZdlNn9VnVj9g 4rcxHy3CbunqK5ZGOX8LdQE+1bYvOL1SMKWTI4h8ZGcm20NU11MPvhQ0JrYiPSZkLElF 6kswZLitW9JpJGb/DvhgM3d3Ve2mICPXLD60UecC5lwA9kgceY6BF0fNN3vl2cDVCHv2 ULPQ== X-Forwarded-Encrypted: i=1; AJvYcCX4Nn3GpUQ0NBK6EzfEQVaB8SBt14sQgJ3oTiAVYNscyEARiS1lO+fJwQGhenXbq+VhDMnVLUWLQgPi2Ik=@vger.kernel.org X-Gm-Message-State: AOJu0Yzs3uhdF/WN/8ydpwSmfxNFxQIGvqzKy+zcbl02YZ5/qpuYVzXk wEK7OV06onJcuByiu3HSyaWOD9USoyguRiSE+4jc2PMnDGJLhCn+g6Qf X-Gm-Gg: ASbGnctXgGWbccQrNZpSapImXQk/hxW2F41zaVC16Bg4DgSE+UMxOBRufzCgzBSgpPa 2SG2Nyb0SAjFevq6x0w3w1CQk2DWzL1FhMRkNZkpVqbl0H0pwVS8xb0BYv5vXtiJbkRYJg7c+7B +7PcyzrGhoew11ygwFUyoIPO4TtmUCp1usd+4az80MXh29KSBEjeAhXkZ6ma0lXhrtg2RqMnS6E c70vXgUZUmWFuTmkAdXZQFgraAhxCj20hf8RRELvckzId9u8ujTbsFg3u3EKqoamMVZQAQmcTTF Ld/oV1KWbQgPB/0kDAHEkPXAKLG+xubpsySJ7Z9bPZm4otwM18RnpiU/boWnESRXZ9OBfv5I0Ie EGkmK9lnB6qjrovZcQh5ohatMAUcVdxH+bqIxY0HqpyCXCZRwz+X+dN8DIBZjZePltD9qmygQyf IhuH3jyiQ31hA= X-Google-Smtp-Source: AGHT+IFKJjzpV9EYUo0mUxqBF81hGWIOAODYyB9YBLMIQiF1odGWwtu4fNZsuEorPfh62DeARpYtCA== X-Received: by 2002:a05:6a00:2293:b0:7ab:995a:46b0 with SMTP id d2e1a72fcca58-7c58e110869mr34201519b3a.15.1764407719096; Sat, 29 Nov 2025 01:15:19 -0800 (PST) Received: from fedora ([2405:201:3017:184:2d1c:8c4c:2945:3f7c]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7d15e7db416sm7300563b3a.41.2025.11.29.01.15.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Nov 2025 01:15:18 -0800 (PST) From: Bhavik Sachdev To: Alexander Viro , Christian Brauner , Shuah Khan Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, criu@lists.linux.dev, Jan Kara , Jeff Layton , Aleksa Sarai , Miklos Szeredi , Bhavik Sachdev , Pavel Tikhomirov , Andrei Vagin , Alexander Mikhalitsyn , John Hubbard , Amir Goldstein , "Martin K . Petersen" , Andrew Donnellan Subject: [PATCH v7 1/3] statmount: permission check should return EPERM Date: Sat, 29 Nov 2025 14:41:20 +0530 Message-ID: <20251129091455.757724-2-b.sachdev1904@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251129091455.757724-1-b.sachdev1904@gmail.com> References: <20251129091455.757724-1-b.sachdev1904@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, statmount() returns ENOENT when caller is not CAP_SYS_ADMIN in the user namespace owner of target mount namespace. This should be EPERM instead. Suggested-by: Miklos Szeredi Signed-off-by: Bhavik Sachdev --- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 2bad25709b2c..ee36d67f1ac2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5795,7 +5795,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __= user *, req, =20 if (kreq.mnt_ns_id && (ns !=3D current->nsproxy->mnt_ns) && !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -ENOENT; + return -EPERM; =20 ks =3D kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); if (!ks) --=20 2.52.0 From nobody Mon Dec 1 22:36:28 2025 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 497F5245014 for ; Sat, 29 Nov 2025 09:15:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407729; cv=none; b=DqOzjvQ/T22ITw31qFstlSN1QZH4w7cYW7PVXdXBU2iwf2iTLk2cs3XLhMc8snDi4YXVgjqo5dHAct85sHBsK9hgcPAfoCc0M5TypwmcrZ4Q+vNwHTJE/AwobycES5o24/lDTKdvqfi7+cixrOZuskV45phtlFLH62v8gj989PA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407729; c=relaxed/simple; bh=IhlLSpZF72FSrdV1+YHnN2WHoXOri79HtwWzTG5DQ+A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jYavYDHtFP6NNtdHQnEIKUZz6WknII7eNbOaQmmbk+nimYFJirfsV3oZKnZ03x3GQxWbaqKpRj2ezCWGD1hdLr7rLy+qOhl3qzLnl2EHJxlWXeYM5kPi248nj8/Ww6eM3/8dA/LQU8lIBOcg8F0YtlQZR/dza7tMM5T9gBtNqLU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=E0FhOj94; arc=none smtp.client-ip=209.85.210.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="E0FhOj94" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-7aae5f2633dso3195017b3a.3 for ; Sat, 29 Nov 2025 01:15:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764407727; x=1765012527; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=pVcxx22uwcyWv5Zel36iqnOvGPO1H7KVbIWMJe+WJP8=; b=E0FhOj949Hsiqm6nPJpxDW4QqJsYQciautH01EnFNmh4bLUUEjz0vLN8Mq7FO28byM LiJrtVIZ3Sf5f6a8Mle6A0qb5nVlM6FIOpCbSZwN8CTi172u5IVmIePtSuEBgDUwUAip TehmMH48IDnoa4hVMeoASfCmjw7WkL85zfUagiZm2MmjwQdzQStYPJGXrIiBPewhlZfH Z0IwGTk/rN8HA85cfornPZcLN7ZevZju202fLGCLZJE0GIGN2TjkbLVVB5wCTdewgKOl Q4H6FtTCEhl6Fz/s9WdAYskutIDehriMLkbwfEm7b0m2DOk2PNX1rLAXjlbvpVCVEFl6 /cqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764407727; x=1765012527; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=pVcxx22uwcyWv5Zel36iqnOvGPO1H7KVbIWMJe+WJP8=; b=mmzZSHO98xdfGv3VoPm6tBEnDSU6+fSZnOpAIfPJQIktpMcJV1TyDu21oVvwGZNPZe R5q4uTitynB9LnOX3nwQZ0ky1x5mrs3EegFprrVb+rx8lVhAwQKOsbTuY1rrDK2R/13u /dDr/34kfVKul2E7nf4uEu3tUYYuVPpBYDl51452n8PG1pq9fSR5yeWv9RRKEG0c1g0B Eo/Q2Xp+ZlvB4GbeY0kTOzxK/2U/YEGaTT4otMqrsZuOLcBuHY7Iv38+PVkC3JPf3WLV 1OqriG+KHZPR37PE3rb1Vr/OdXxZ2VbJ5E7FIoT93grDdDpndP/WcObqN/yflGlC9YRP qqDg== X-Forwarded-Encrypted: i=1; AJvYcCXHb31dZSVduXxZqmpmpM2WXEsVgIPKu+7UbYikRk9stMbcPqfsREFDDQ2DfUPUzz4AmY1kPWp5YDrPUYA=@vger.kernel.org X-Gm-Message-State: AOJu0Ywxt8/B8NsdppjY9N20Lhsbx21UorsHZ2uB9BFNrcokS1PI0UJK kB+0UKd5ybBv2H9ooYjQkr7+sCVyfp+ivLRhnLzeLNpM4URpXYIn/R2f X-Gm-Gg: ASbGncuIAP7iFV1ow+/nqdsn9aIPty4wDtofwZbUPhPsP5e0+xSMfrbsKpwBGXNl3zg 4AaN01ByVGK+2nNL3br7lvwHELytMsNwbWmoQdmL/aC4oRyiZX2ezlTCnZ5HcbPN0nmuUSYxdlP 07d+QAwJf9/cMvCBOSigB+bSqbL5iRoUO8FNJxM2qciBTeZih8uFk3fOfEeIjCYW/9hWKgKr1kn GCckhvkx474r8jVsH6qjyPnK/nlv9ZH/0AH87n5fVIFrynwK3hyACM8gnPVg1TuDoOvsZv4jxol /MHD4ljXadmUEB4VvwUNxNVh7iGgUxJUBce1nXqcew42tyiAXFsLGLgh/oiESACb/DTiiYWLz/k ieV8qsz4qfViJJtB+7EDdWUGxycqf9m1MMQfawlM3Yy6n9lQ5GKrE2Y+TIdzziPYtUUONczA7mY 7gSp7P/y3tGno= X-Google-Smtp-Source: AGHT+IFy53cLbESiXML1MJ4hrBJswXxa/BoGl5z6KQzHvzuNLRuKNd9j4dRr8DLhGkrFztEpABmyuA== X-Received: by 2002:a05:6a21:999a:b0:361:2fff:7b19 with SMTP id adf61e73a8af0-3614ee0a140mr34179763637.52.1764407726448; Sat, 29 Nov 2025 01:15:26 -0800 (PST) Received: from fedora ([2405:201:3017:184:2d1c:8c4c:2945:3f7c]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7d15e7db416sm7300563b3a.41.2025.11.29.01.15.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Nov 2025 01:15:25 -0800 (PST) From: Bhavik Sachdev To: Alexander Viro , Christian Brauner , Shuah Khan Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, criu@lists.linux.dev, Jan Kara , Jeff Layton , Aleksa Sarai , Miklos Szeredi , Bhavik Sachdev , Pavel Tikhomirov , Andrei Vagin , Alexander Mikhalitsyn , John Hubbard , Amir Goldstein , "Martin K . Petersen" , Andrew Donnellan Subject: [PATCH v7 2/3] statmount: accept fd as a parameter Date: Sat, 29 Nov 2025 14:41:21 +0530 Message-ID: <20251129091455.757724-3-b.sachdev1904@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251129091455.757724-1-b.sachdev1904@gmail.com> References: <20251129091455.757724-1-b.sachdev1904@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Extend `struct mnt_id_req` to take in a fd and introduce STATMOUNT_BY_FD flag. When a valid fd is provided and STATMOUNT_BY_FD is set, statmount will return mountinfo about the mount the fd is on. This even works for "unmounted" mounts (mounts that have been umounted using umount2(mnt, MNT_DETACH)), if you have access to a file descriptor on that mount. These "umounted" mounts will have no mountpoint and no valid mount namespace. Hence, we unset the STATMOUNT_MNT_POINT and STATMOUNT_MNT_NS_ID in statmount.mask for "unmounted" mounts. In case of STATMOUNT_BY_FD, given that we already have access to an fd on the mount, accessing mount information without a capability check seems fine because of the following reasons: - All fs related information is available via fstatfs() without any capability check. - Mount information is also available via /proc/pid/mountinfo (without any capability check). - Given that we have access to a fd on the mount which tells us that we had access to the mount at some point (or someone that had access gave us the fd). So, we should be able to access mount info. Co-developed-by: Pavel Tikhomirov Signed-off-by: Pavel Tikhomirov Signed-off-by: Bhavik Sachdev --- fs/namespace.c | 102 ++++++++++++++++++++++++------------- include/uapi/linux/mount.h | 10 +++- 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index ee36d67f1ac2..73ffa1fbdad7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5563,31 +5563,49 @@ static int grab_requested_root(struct mnt_namespace= *ns, struct path *root) =20 /* locks: namespace_shared */ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, - struct mnt_namespace *ns) + struct file *mnt_file, struct mnt_namespace *ns) { - struct mount *m; int err; =20 - /* Has the namespace already been emptied? */ - if (mnt_ns_id && mnt_ns_empty(ns)) - return -ENOENT; + if (mnt_file) { + WARN_ON_ONCE(ns !=3D NULL); =20 - s->mnt =3D lookup_mnt_in_ns(mnt_id, ns); - if (!s->mnt) - return -ENOENT; + s->mnt =3D mnt_file->f_path.mnt; + ns =3D real_mount(s->mnt)->mnt_ns; + if (!ns) + /* + * We can't set mount point and mnt_ns_id since we don't have a + * ns for the mount. This can happen if the mount is unmounted + * with MNT_DETACH. + */ + s->mask &=3D ~(STATMOUNT_MNT_POINT | STATMOUNT_MNT_NS_ID); + } else { + /* Has the namespace already been emptied? */ + if (mnt_ns_id && mnt_ns_empty(ns)) + return -ENOENT; =20 - err =3D grab_requested_root(ns, &s->root); - if (err) - return err; + s->mnt =3D lookup_mnt_in_ns(mnt_id, ns); + if (!s->mnt) + return -ENOENT; + } =20 - /* - * Don't trigger audit denials. We just want to determine what - * mounts to show users. - */ - m =3D real_mount(s->mnt); - if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; + if (ns) { + err =3D grab_requested_root(ns, &s->root); + if (err) + return err; + + if (!mnt_file) { + struct mount *m; + /* + * Don't trigger audit denials. We just want to determine what + * mounts to show users. + */ + m =3D real_mount(s->mnt); + if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } + } =20 err =3D security_sb_statfs(s->mnt->mnt_root); if (err) @@ -5709,7 +5727,7 @@ static int prepare_kstatmount(struct kstatmount *ks, = struct mnt_id_req *kreq, } =20 static int copy_mnt_id_req(const struct mnt_id_req __user *req, - struct mnt_id_req *kreq) + struct mnt_id_req *kreq, unsigned int flags) { int ret; size_t usize; @@ -5727,11 +5745,17 @@ static int copy_mnt_id_req(const struct mnt_id_req = __user *req, ret =3D copy_struct_from_user(kreq, sizeof(*kreq), req, usize); if (ret) return ret; - if (kreq->mnt_ns_fd !=3D 0 && kreq->mnt_ns_id) - return -EINVAL; - /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ - if (kreq->mnt_id <=3D MNT_UNIQUE_ID_OFFSET) - return -EINVAL; + + if (flags & STATMOUNT_BY_FD) { + if (kreq->mnt_id || kreq->mnt_ns_id) + return -EINVAL; + } else { + if (kreq->mnt_ns_fd !=3D 0 && kreq->mnt_ns_id) + return -EINVAL; + /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ + if (kreq->mnt_id <=3D MNT_UNIQUE_ID_OFFSET) + return -EINVAL; + } return 0; } =20 @@ -5777,25 +5801,33 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req = __user *, req, { struct mnt_namespace *ns __free(mnt_ns_release) =3D NULL; struct kstatmount *ks __free(kfree) =3D NULL; + struct file *mnt_file __free(fput) =3D NULL; struct mnt_id_req kreq; /* We currently support retrieval of 3 strings. */ size_t seq_size =3D 3 * PATH_MAX; int ret; =20 - if (flags) + if (flags & ~STATMOUNT_BY_FD) return -EINVAL; =20 - ret =3D copy_mnt_id_req(req, &kreq); + ret =3D copy_mnt_id_req(req, &kreq, flags); if (ret) return ret; =20 - ns =3D grab_requested_mnt_ns(&kreq); - if (IS_ERR(ns)) - return PTR_ERR(ns); + if (flags & STATMOUNT_BY_FD) { + mnt_file =3D fget_raw(kreq.mnt_fd); + if (!mnt_file) + return -EBADF; + /* do_statmount sets ns in case of STATMOUNT_BY_FD */ + } else { + ns =3D grab_requested_mnt_ns(&kreq); + if (IS_ERR(ns)) + return PTR_ERR(ns); =20 - if (kreq.mnt_ns_id && (ns !=3D current->nsproxy->mnt_ns) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; + if (kreq.mnt_ns_id && (ns !=3D current->nsproxy->mnt_ns) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } =20 ks =3D kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); if (!ks) @@ -5807,7 +5839,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __= user *, req, return ret; =20 scoped_guard(namespace_shared) - ret =3D do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns); + ret =3D do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, mnt_file, ns); =20 if (!ret) ret =3D copy_statmount_to_user(ks); @@ -5947,7 +5979,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __= user *, req, if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids))) return -EFAULT; =20 - ret =3D copy_mnt_id_req(req, &kreq); + ret =3D copy_mnt_id_req(req, &kreq, 0); if (ret) return ret; =20 diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h index 5d3f8c9e3a62..18c624405268 100644 --- a/include/uapi/linux/mount.h +++ b/include/uapi/linux/mount.h @@ -197,7 +197,10 @@ struct statmount { */ struct mnt_id_req { __u32 size; - __u32 mnt_ns_fd; + union { + __u32 mnt_ns_fd; + __u32 mnt_fd; + }; __u64 mnt_id; __u64 param; __u64 mnt_ns_id; @@ -232,4 +235,9 @@ struct mnt_id_req { #define LSMT_ROOT 0xffffffffffffffff /* root mount */ #define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */ =20 +/* + * @flag bits for statmount(2) + */ +#define STATMOUNT_BY_FD 0x00000001U /* want mountinfo for given fd */ + #endif /* _UAPI_LINUX_MOUNT_H */ --=20 2.52.0 From nobody Mon Dec 1 22:36:28 2025 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95BDD274B5F for ; Sat, 29 Nov 2025 09:15:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407737; cv=none; b=QSMM4Cg6JuCcvQIHND6zi0zToG0V3ISeXDpiEYOvt8kjYwrnbokGvDfIvUuMOd8HQ3O4J9GiOxpLL+L/3ZUL8d555N2xaN6FivHYtUQiAQd07AnNp5aj98Hnp2d03XczwMpRMv9AUf3XDnfkWdgqvssgjmaKYejFEHBtlR18AiA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407737; c=relaxed/simple; bh=g44NCATS1TCM4N3MVgj2aM+W4i89Z9A+wULVk7cS86E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=thhpOXEEb/jeoxcvDJFlNEWOkVqdndDBbEMd5ZgjV7f2xw20GzuhWUEwwhPGBRevlryOYdrCHRZuDf+ypRdB3oJSalPQPJUBwKXL4k5liqhxnjLQZfvw2DjonrZHlIWNFgGGN9E5pz4ZItYZ+VpLsgN1zPLAjXJkmKkmLJjt7Jk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GtCGuny0; arc=none smtp.client-ip=209.85.210.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GtCGuny0" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-7ba49f92362so1561847b3a.1 for ; Sat, 29 Nov 2025 01:15:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764407734; x=1765012534; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8dR6VknWnQZF4bSIzafZo2V382x8cKwTyyx/xFmReEY=; b=GtCGuny0JKPxy1lgupIwYdq780gZdI1gIqBA7ZYsW5vGohRQ2yxZlxtwfPvKUPRVUe aG5RfvHUe/kXgLzRTHTT6Z5OqoxrjmZEfjQZODiqj1Ll4da6XglLyXg+A69kl+QcMk1p nE52RahsgFr1vKAPZkAVjeEZpF0gN1zgDiZNd4blvcA2WG+Qy6Ap/pk/Mu5h1HB8JG3o 4doO5lZl23X3wo9DxleVnYu0F92ee4UHKd5CQwAOTHqpW1iFOg2nlWbxV5w1r4hyFlVU fqG/bCdqHNSqnE557D8QNiaWRi3+B2gxlpXazwKHHlH7OQNf/z9N8DNYcxHFJo2OzAjx BYvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764407734; x=1765012534; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=8dR6VknWnQZF4bSIzafZo2V382x8cKwTyyx/xFmReEY=; b=s6E2cew0/+tAzj2j+nxhq8LpbNoZemq7NykyaWk5/rq2K6jRMc6QUVFjI5zZNAi7Y4 z79bbZz9fyTR6+mOwtQZLaGaYV8H5Jki5EYM8feA5PUU6Ahf0dF1I7N7WmckgdR8r3vx z4NzIj76WF+Fz+41Rhs3CYkMS8Wf3ikEYf8rBalzZ6tTccDjKh2aJpZNDLRXcIVeMWMG NX9hKpRMKrMNorOQenMh+ivmHDrbXXjciaw4zu+C3mLpW826rj2WXGs9hPqseosPPsT3 oEZNLqpc/0187lF8jpc/EHBwo286vs9jQ2gcKtzklgLB1Sx44TyHilCgh5y7JrmowIU4 pY3g== X-Forwarded-Encrypted: i=1; AJvYcCX6Tm77Y+pBiUrvMchIxCljMLv94IPFY+qPbVtrGj1n37zN6t3/hfgDSC5g1yfo3nQPBi++uYWdV6KFlgc=@vger.kernel.org X-Gm-Message-State: AOJu0YzoMgUpaz0UOhRY3SuJaE9GfLEqPHI4bwHnNpgOZ9t3ORzaHWYZ J6JB7x24GtC5jOvzNakj1fgkIXdfBz6Z7ixyqI4iXY9LXe+IktXwDoSY X-Gm-Gg: ASbGnct+MxMDdl1CxT4foHr1Tq8uoNglV20ySTJ85rueirKSH0kggKWXmhOehddae+x jRtlZ+JvWi7O1ChfZxz68d9O8jeUgLDT+VerYU22Y8w4WUsKnGmowEfqFXJFwlASPz96QTpReds HfkLo8mWcRRNd3Gw0J/fL9Q3i8GRZgtTWWW/ER1Q5kdOSS7CcccxIcX/ajnvJo+4ZRvEyds7kGa 0Y5IuxtWC1k1vDjyCCo+XiUNjRW6cSM9M88dOMELFhuqGpwYvnh316inVuaX7ABrO8qUU1sHxaR TgDD/g+fTg64zevN2/jJ0pnoFfcV3uC92qONwMNIhut8ELEVCTNLWFU08opwaCaBJGkz9k4+3kg zSzNURLpcsggMz1IPiAkfymZVLGbTcZtJTwcHOV9h9++e7c82p/kMUGVbxCw1KpvFGAByXw4pMn RV6Ti1r+rqPDo= X-Google-Smtp-Source: AGHT+IHruZIAOHm6EMOKF1APwodVcDGXlfmCY/BJnPMVAX/ATWPHaJ22ObFNln7/rm4RyHPip0Lemg== X-Received: by 2002:a05:6a00:cc8:b0:77d:98ee:e1c5 with SMTP id d2e1a72fcca58-7c42066e5f7mr37864729b3a.15.1764407733645; Sat, 29 Nov 2025 01:15:33 -0800 (PST) Received: from fedora ([2405:201:3017:184:2d1c:8c4c:2945:3f7c]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7d15e7db416sm7300563b3a.41.2025.11.29.01.15.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Nov 2025 01:15:33 -0800 (PST) From: Bhavik Sachdev To: Alexander Viro , Christian Brauner , Shuah Khan Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, criu@lists.linux.dev, Jan Kara , Jeff Layton , Aleksa Sarai , Miklos Szeredi , Bhavik Sachdev , Pavel Tikhomirov , Andrei Vagin , Alexander Mikhalitsyn , John Hubbard , Amir Goldstein , "Martin K . Petersen" , Andrew Donnellan Subject: [PATCH v7 3/3] selftests: statmount: tests for STATMOUNT_BY_FD Date: Sat, 29 Nov 2025 14:41:22 +0530 Message-ID: <20251129091455.757724-4-b.sachdev1904@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251129091455.757724-1-b.sachdev1904@gmail.com> References: <20251129091455.757724-1-b.sachdev1904@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add tests for STATMOUNT_BY_FD flag, which adds support for passing a file descriptors to statmount(). The fd can also be on a "unmounted" mount (mount unmounted with MNT_DETACH), we also include tests for that. Co-developed-by: Andrei Vagin Signed-off-by: Andrei Vagin Signed-off-by: Bhavik Sachdev --- .../filesystems/statmount/statmount.h | 15 +- .../filesystems/statmount/statmount_test.c | 261 +++++++++++++++++- .../filesystems/statmount/statmount_test_ns.c | 101 ++++++- 3 files changed, 354 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/filesystems/statmount/statmount.h b/to= ols/testing/selftests/filesystems/statmount/statmount.h index 99e5ad082fb1..e1cba4bfd8d9 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount.h +++ b/tools/testing/selftests/filesystems/statmount/statmount.h @@ -43,19 +43,24 @@ #endif #endif =20 -static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t = mask, - struct statmount *buf, size_t bufsize, +static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint32_t = fd, + uint64_t mask, struct statmount *buf, size_t bufsize, unsigned int flags) { struct mnt_id_req req =3D { .size =3D MNT_ID_REQ_SIZE_VER0, - .mnt_id =3D mnt_id, .param =3D mask, }; =20 - if (mnt_ns_id) { + if (flags & STATMOUNT_BY_FD) { req.size =3D MNT_ID_REQ_SIZE_VER1; - req.mnt_ns_id =3D mnt_ns_id; + req.mnt_fd =3D fd; + } else { + req.mnt_id =3D mnt_id; + if (mnt_ns_id) { + req.size =3D MNT_ID_REQ_SIZE_VER1; + req.mnt_ns_id =3D mnt_ns_id; + } } =20 return syscall(__NR_statmount, &req, buf, bufsize, flags); diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test.c= b/tools/testing/selftests/filesystems/statmount/statmount_test.c index f048042e53e9..4790a349806e 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount_test.c +++ b/tools/testing/selftests/filesystems/statmount/statmount_test.c @@ -33,15 +33,24 @@ static const char *const known_fs[] =3D { "sysv", "tmpfs", "tracefs", "ubifs", "udf", "ufs", "v7", "vboxsf", "vfat", "virtiofs", "vxfs", "xenfs", "xfs", "zonefs", NULL }; =20 -static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, u= nsigned int flags) +static struct statmount *statmount_alloc(uint64_t mnt_id, int fd, uint64_t= mask, unsigned int flags) { size_t bufsize =3D 1 << 15; - struct statmount *buf =3D NULL, *tmp =3D alloca(bufsize); + struct statmount *buf =3D NULL, *tmp =3D NULL; int tofree =3D 0; int ret; =20 + if (flags & STATMOUNT_BY_FD && fd < 0) + return NULL; + + tmp =3D alloca(bufsize); + for (;;) { - ret =3D statmount(mnt_id, 0, mask, tmp, bufsize, flags); + if (flags & STATMOUNT_BY_FD) + ret =3D statmount(0, 0, (uint32_t) fd, mask, tmp, bufsize, flags); + else + ret =3D statmount(mnt_id, 0, 0, mask, tmp, bufsize, flags); + if (ret !=3D -1) break; if (tofree) @@ -237,7 +246,7 @@ static void test_statmount_zero_mask(void) struct statmount sm; int ret; =20 - ret =3D statmount(root_id, 0, 0, &sm, sizeof(sm), 0); + ret =3D statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0); if (ret =3D=3D -1) { ksft_test_result_fail("statmount zero mask: %s\n", strerror(errno)); @@ -263,7 +272,7 @@ static void test_statmount_mnt_basic(void) int ret; uint64_t mask =3D STATMOUNT_MNT_BASIC; =20 - ret =3D statmount(root_id, 0, mask, &sm, sizeof(sm), 0); + ret =3D statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0); if (ret =3D=3D -1) { ksft_test_result_fail("statmount mnt basic: %s\n", strerror(errno)); @@ -323,7 +332,7 @@ static void test_statmount_sb_basic(void) struct statx sx; struct statfs sf; =20 - ret =3D statmount(root_id, 0, mask, &sm, sizeof(sm), 0); + ret =3D statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0); if (ret =3D=3D -1) { ksft_test_result_fail("statmount sb basic: %s\n", strerror(errno)); @@ -375,7 +384,7 @@ static void test_statmount_mnt_point(void) { struct statmount *sm; =20 - sm =3D statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0); + sm =3D statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0); if (!sm) { ksft_test_result_fail("statmount mount point: %s\n", strerror(errno)); @@ -405,7 +414,7 @@ static void test_statmount_mnt_root(void) assert(last_dir); last_dir++; =20 - sm =3D statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0); + sm =3D statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT, 0); if (!sm) { ksft_test_result_fail("statmount mount root: %s\n", strerror(errno)); @@ -438,7 +447,7 @@ static void test_statmount_fs_type(void) const char *fs_type; const char *const *s; =20 - sm =3D statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0); + sm =3D statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0); if (!sm) { ksft_test_result_fail("statmount fs type: %s\n", strerror(errno)); @@ -467,7 +476,7 @@ static void test_statmount_mnt_opts(void) char *line =3D NULL; size_t len =3D 0; =20 - sm =3D statmount_alloc(root_id, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS, + sm =3D statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OP= TS, 0); if (!sm) { ksft_test_result_fail("statmount mnt opts: %s\n", @@ -557,7 +566,7 @@ static void test_statmount_string(uint64_t mask, size_t= off, const char *name) uint32_t start, i; int ret; =20 - sm =3D statmount_alloc(root_id, mask, 0); + sm =3D statmount_alloc(root_id, 0, mask, 0); if (!sm) { ksft_test_result_fail("statmount %s: %s\n", name, strerror(errno)); @@ -586,14 +595,14 @@ static void test_statmount_string(uint64_t mask, size= _t off, const char *name) exactsize =3D sm->size; shortsize =3D sizeof(*sm) + i; =20 - ret =3D statmount(root_id, 0, mask, sm, exactsize, 0); + ret =3D statmount(root_id, 0, 0, mask, sm, exactsize, 0); if (ret =3D=3D -1) { ksft_test_result_fail("statmount exact size: %s\n", strerror(errno)); goto out; } errno =3D 0; - ret =3D statmount(root_id, 0, mask, sm, shortsize, 0); + ret =3D statmount(root_id, 0, 0, mask, sm, shortsize, 0); if (ret !=3D -1 || errno !=3D EOVERFLOW) { ksft_test_result_fail("should have failed with EOVERFLOW: %s\n", strerror(errno)); @@ -658,6 +667,226 @@ static void test_listmount_tree(void) ksft_test_result_pass("listmount tree\n"); } =20 +static void test_statmount_by_fd(void) +{ + struct statmount *sm =3D NULL; + char tmpdir[] =3D "/statmount.fd.XXXXXX"; + const char root[] =3D "/test"; + char subdir[PATH_MAX], tmproot[PATH_MAX]; + int fd; + + if (!mkdtemp(tmpdir)) { + ksft_perror("mkdtemp"); + return; + } + + if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) { + ksft_perror("mount"); + rmdir(tmpdir); + return; + } + + snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root); + snprintf(tmproot, PATH_MAX, "%s/%s", tmpdir, "chroot"); + + if (mkdir(subdir, 0755)) { + ksft_perror("mkdir"); + goto err_tmpdir; + } + + if (mount(subdir, subdir, NULL, MS_BIND, 0)) { + ksft_perror("mount"); + goto err_subdir; + } + + if (mkdir(tmproot, 0755)) { + ksft_perror("mkdir"); + goto err_subdir; + } + + fd =3D open(subdir, O_PATH); + if (fd < 0) { + ksft_perror("open"); + goto err_tmproot; + } + + if (chroot(tmproot)) { + ksft_perror("chroot"); + goto err_fd; + } + + sm =3D statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, S= TATMOUNT_BY_FD); + if (!sm) { + ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno)); + goto err_chroot; + } + + if (sm->size < sizeof(*sm)) { + ksft_test_result_fail("unexpected size: %u < %u\n", + sm->size, (uint32_t) sizeof(*sm)); + goto err_chroot; + } + + if (sm->mask & STATMOUNT_MNT_POINT) { + ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in statmount= \n"); + goto err_chroot; + } + + if (!(sm->mask & STATMOUNT_MNT_ROOT)) { + ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n"); + goto err_chroot; + } + + if (strcmp(root, sm->str + sm->mnt_root) !=3D 0) { + ksft_test_result_fail("statmount returned incorrect mnt_root," + "statmount mnt_root: %s !=3D %s\n", + sm->str + sm->mnt_root, root); + goto err_chroot; + } + + if (chroot(".")) { + ksft_perror("chroot"); + goto out; + } + + free(sm); + sm =3D statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, S= TATMOUNT_BY_FD); + if (!sm) { + ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno)); + goto err_fd; + } + + if (sm->size < sizeof(*sm)) { + ksft_test_result_fail("unexpected size: %u < %u\n", + sm->size, (uint32_t) sizeof(*sm)); + goto out; + } + + if (!(sm->mask & STATMOUNT_MNT_POINT)) { + ksft_test_result_fail("STATMOUNT_MNT_POINT not set in statmount\n"); + goto out; + } + + if (!(sm->mask & STATMOUNT_MNT_ROOT)) { + ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n"); + goto out; + } + + if (strcmp(subdir, sm->str + sm->mnt_point) !=3D 0) { + ksft_test_result_fail("statmount returned incorrect mnt_point," + "statmount mnt_point: %s !=3D %s\n", sm->str + sm->mnt_point, subdir); + goto out; + } + + if (strcmp(root, sm->str + sm->mnt_root) !=3D 0) { + ksft_test_result_fail("statmount returned incorrect mnt_root," + "statmount mnt_root: %s !=3D %s\n", sm->str + sm->mnt_root, root); + goto out; + } + + ksft_test_result_pass("statmount by fd\n"); + goto out; +err_chroot: + chroot("."); +out: + free(sm); +err_fd: + close(fd); +err_tmproot: + rmdir(tmproot); +err_subdir: + umount2(subdir, MNT_DETACH); + rmdir(subdir); +err_tmpdir: + umount2(tmpdir, MNT_DETACH); + rmdir(tmpdir); +} + +static void test_statmount_by_fd_unmounted(void) +{ + const char root[] =3D "/test.unmounted"; + char tmpdir[] =3D "/statmount.fd.XXXXXX"; + char subdir[PATH_MAX]; + int fd; + struct statmount *sm =3D NULL; + + if (!mkdtemp(tmpdir)) { + ksft_perror("mkdtemp"); + return; + } + + if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) { + ksft_perror("mount"); + rmdir(tmpdir); + return; + } + + snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root); + + if (mkdir(subdir, 0755)) { + ksft_perror("mkdir"); + goto err_tmpdir; + } + + if (mount(subdir, subdir, 0, MS_BIND, NULL)) { + ksft_perror("mount"); + goto err_subdir; + } + + fd =3D open(subdir, O_PATH); + if (fd < 0) { + ksft_perror("open"); + goto err_subdir; + } + + if (umount2(tmpdir, MNT_DETACH)) { + ksft_perror("umount2"); + goto err_fd; + } + + sm =3D statmount_alloc(0, fd, STATMOUNT_MNT_POINT | STATMOUNT_MNT_ROOT, S= TATMOUNT_BY_FD); + if (!sm) { + ksft_test_result_fail("statmount by fd unmounted: %s\n", + strerror(errno)); + goto err_sm; + } + + if (sm->size < sizeof(*sm)) { + ksft_test_result_fail("unexpected size: %u < %u\n", + sm->size, (uint32_t) sizeof(*sm)); + goto err_sm; + } + + if (sm->mask & STATMOUNT_MNT_POINT) { + ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in mask\n"); + goto err_sm; + } + + if (!(sm->mask & STATMOUNT_MNT_ROOT)) { + ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in mask\n"); + goto err_sm; + } + + if (strcmp(sm->str + sm->mnt_root, root) !=3D 0) { + ksft_test_result_fail("statmount returned incorrect mnt_root," + "statmount mnt_root: %s !=3D %s\n", + sm->str + sm->mnt_root, root); + goto err_sm; + } + + ksft_test_result_pass("statmount by fd on unmounted mount\n"); +err_sm: + free(sm); +err_fd: + close(fd); +err_subdir: + umount2(subdir, MNT_DETACH); + rmdir(subdir); +err_tmpdir: + umount2(tmpdir, MNT_DETACH); + rmdir(tmpdir); +} + #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t)) =20 int main(void) @@ -669,14 +898,14 @@ int main(void) =20 ksft_print_header(); =20 - ret =3D statmount(0, 0, 0, NULL, 0, 0); + ret =3D statmount(0, 0, 0, 0, NULL, 0, 0); assert(ret =3D=3D -1); if (errno =3D=3D ENOSYS) ksft_exit_skip("statmount() syscall not supported\n"); =20 setup_namespace(); =20 - ksft_set_plan(15); + ksft_set_plan(17); test_listmount_empty_root(); test_statmount_zero_mask(); test_statmount_mnt_basic(); @@ -693,6 +922,8 @@ int main(void) test_statmount_string(all_mask, str_off(fs_type), "fs type & all"); =20 test_listmount_tree(); + test_statmount_by_fd_unmounted(); + test_statmount_by_fd(); =20 =20 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test_n= s.c b/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c index 605a3fa16bf7..6449b50dde0c 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c +++ b/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c @@ -102,7 +102,7 @@ static int _test_statmount_mnt_ns_id(void) if (!root_id) return NSID_ERROR; =20 - ret =3D statmount(root_id, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0); + ret =3D statmount(root_id, 0, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0); if (ret =3D=3D -1) { ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno)); return NSID_ERROR; @@ -128,6 +128,98 @@ static int _test_statmount_mnt_ns_id(void) return NSID_PASS; } =20 +static int _test_statmount_mnt_ns_id_by_fd(void) +{ + struct statmount sm; + uint64_t mnt_ns_id; + int ret, fd, mounted =3D 1, status =3D NSID_ERROR; + char mnt[] =3D "/statmount.fd.XXXXXX"; + + ret =3D get_mnt_ns_id("/proc/self/ns/mnt", &mnt_ns_id); + if (ret !=3D NSID_PASS) + return ret; + + if (!mkdtemp(mnt)) { + ksft_print_msg("statmount by fd mnt ns id mkdtemp: %s\n", strerror(errno= )); + return NSID_ERROR; + } + + if (mount(mnt, mnt, NULL, MS_BIND, 0)) { + ksft_print_msg("statmount by fd mnt ns id mount: %s\n", strerror(errno)); + status =3D NSID_ERROR; + goto err; + } + + fd =3D open(mnt, O_PATH); + if (fd < 0) { + ksft_print_msg("statmount by fd mnt ns id open: %s\n", strerror(errno)); + goto err; + } + + ret =3D statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOU= NT_BY_FD); + if (ret =3D=3D -1) { + ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno)); + status =3D NSID_ERROR; + goto out; + } + + if (sm.size !=3D sizeof(sm)) { + ksft_print_msg("unexpected size: %u !=3D %u\n", sm.size, + (uint32_t)sizeof(sm)); + status =3D NSID_FAIL; + goto out; + } + if (sm.mask !=3D STATMOUNT_MNT_NS_ID) { + ksft_print_msg("statmount mnt ns id unavailable\n"); + status =3D NSID_SKIP; + goto out; + } + + if (sm.mnt_ns_id !=3D mnt_ns_id) { + ksft_print_msg("unexpected mnt ns ID: 0x%llx !=3D 0x%llx\n", + (unsigned long long)sm.mnt_ns_id, + (unsigned long long)mnt_ns_id); + status =3D NSID_FAIL; + goto out; + } + + mounted =3D 0; + if (umount2(mnt, MNT_DETACH)) { + ksft_print_msg("statmount by fd mnt ns id umount2: %s\n", strerror(errno= )); + goto out; + } + + ret =3D statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOU= NT_BY_FD); + if (ret =3D=3D -1) { + ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno)); + status =3D NSID_ERROR; + goto out; + } + + if (sm.size !=3D sizeof(sm)) { + ksft_print_msg("unexpected size: %u !=3D %u\n", sm.size, + (uint32_t)sizeof(sm)); + status =3D NSID_FAIL; + goto out; + } + + if (sm.mask =3D=3D STATMOUNT_MNT_NS_ID) { + ksft_print_msg("unexpected STATMOUNT_MNT_NS_ID in mask\n"); + status =3D NSID_FAIL; + goto out; + } + + status =3D NSID_PASS; +out: + close(fd); + if (mounted) + umount2(mnt, MNT_DETACH); +err: + rmdir(mnt); + return status; +} + + static void test_statmount_mnt_ns_id(void) { pid_t pid; @@ -148,6 +240,9 @@ static void test_statmount_mnt_ns_id(void) if (ret !=3D NSID_PASS) exit(ret); ret =3D _test_statmount_mnt_ns_id(); + if (ret !=3D NSID_PASS) + exit(ret); + ret =3D _test_statmount_mnt_ns_id_by_fd(); exit(ret); } =20 @@ -179,7 +274,7 @@ static int validate_external_listmount(pid_t pid, uint6= 4_t child_nr_mounts) for (int i =3D 0; i < nr_mounts; i++) { struct statmount sm; =20 - ret =3D statmount(list[i], mnt_ns_id, STATMOUNT_MNT_NS_ID, &sm, + ret =3D statmount(list[i], mnt_ns_id, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0); if (ret < 0) { ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno)); @@ -275,7 +370,7 @@ int main(void) int ret; =20 ksft_print_header(); - ret =3D statmount(0, 0, 0, NULL, 0, 0); + ret =3D statmount(0, 0, 0, 0, NULL, 0, 0); assert(ret =3D=3D -1); if (errno =3D=3D ENOSYS) ksft_exit_skip("statmount() syscall not supported\n"); --=20 2.52.0