From nobody Wed Apr 1 09:45:02 2026 Received: from ewsoutbound.kpnmail.nl (ewsoutbound.kpnmail.nl [195.121.94.186]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50793345740 for ; Tue, 31 Mar 2026 17:20:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.121.94.186 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774977639; cv=none; b=dlgUm4GF7AKy7e7vGe49JVGzevJ41XCIZB2lYtFOX+f3HlCAMbVr7gvxlnFSx8hsXd9uHzbHMNZJ8Uy8wX36nCrD2nf1ouYQqratWEN9vICZjiYh01LZeBm0gEnIzDzR4iFTCE2of0eHSxN9qREJobrD9CdfB3SAVhgNoKDUyHY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774977639; c=relaxed/simple; bh=LXGIe2EbKEgpeH0Y3XR7VUj1ALQtEFbVCyxTICzraSw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XfNonmuaRAVI4DJuMnkz/WUtwU1osIXZlYV7ZQRuAVtwQHDZVZ6qJ7aAst8ZoXqCKvImeEi4Qz5H5dOmQ91TSupi20IiRaxE59ph2Fnl5BTGL0YAAA+CElDIVXu+VY5+AOixk+AjgDEwKOpsDQFTtS0HKPwAxmCbyuWe+2G9LgA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl; spf=pass smtp.mailfrom=xs4all.nl; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b=Z9/cUuft; arc=none smtp.client-ip=195.121.94.186 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xs4all.nl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xs4all.nl header.i=@xs4all.nl header.b="Z9/cUuft" X-KPN-MessageId: eb083a41-2d25-11f1-89dd-00505699b430 Received: from smtp.kpnmail.nl (unknown [10.31.155.7]) by ewsoutbound.so.kpn.org (Halon) with ESMTPS id eb083a41-2d25-11f1-89dd-00505699b430; Tue, 31 Mar 2026 19:20:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xs4all.nl; s=xs4all01; h=mime-version:message-id:date:subject:to:from; bh=zDP/iu2bYslX1MKM5Ro/UsGWemjCD4by8j49Ok9YqUI=; b=Z9/cUuftDVDCQkdTQLqdCiSeMZMVXZPTADgtNig+MEIP2mPBTjDplgPtxG+ceEmet8u+RdoEg3nbs UMvRPZ5ucT8tZAiss8WadzlDev5e76yOfcqTCXFrCMZZLaNtBjSBz8qOqwUnSe0Zgr688HgBvwdQ5K SlWcxFZ1rVEvkLF/k5ZbYYH/RodIHl+ApLBm0WvYxl9gjtz+2BmIlMc2kyFrpREQNG7J998rFtcXzK iBn9wrjyJdxQKxnntNmUo/XBsZzBnlHwVx9PvpTiq3XK0wNHGeaMy1RLKBSoX/xP/fZOXEbGN4pO22 xM2xlX+JALZkWtZiDwqsWpSOz2CZIoQ== X-KPN-MID: 33|sikx9ZcHd/fInEMSfVbSNHhMDyIs4oGsjHeL/W8Z7mI2Bm3I565SPcupuicIDP3 rdLtexMWR+4Zl7NdQamsboA== X-KPN-VerifiedSender: Yes X-CMASSUN: 33|nr5woH1BD8GZbCzHqoBN7y6lJ5aJDQ+LMqUCyp20FGKwuVwbmxrUdUBNnk670m1 YseKZw3G8i96sRps4/UBWfA== Received: from daedalus.home (unknown [178.227.25.158]) by smtp.xs4all.nl (Halon) with ESMTPSA id ea964dfe-2d25-11f1-86d5-005056998788; Tue, 31 Mar 2026 19:20:30 +0200 (CEST) From: Jori Koolstra To: Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Alexander Viro , Christian Brauner , Jeff Layton , Chuck Lever , Arnd Bergmann , Shuah Khan , Greg Kroah-Hartman Cc: "H . Peter Anvin" , Jan Kara , Alexander Aring , Peter Zijlstra , Oleg Nesterov , Andrey Albershteyn , Jiri Olsa , Mathieu Desnoyers , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , Namhyung Kim , Arnaldo Carvalho de Melo , Aleksa Sarai , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, cmirabil@redhat.com, Jori Koolstra , Ingo Molnar Subject: [RFC PATCH 2/2] selftest: add tests for mkdirat_fd() Date: Tue, 31 Mar 2026 19:19:59 +0200 Message-ID: <20260331172011.3512876-3-jkoolstra@xs4all.nl> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260331172011.3512876-1-jkoolstra@xs4all.nl> References: <20260331172011.3512876-1-jkoolstra@xs4all.nl> 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 some tests for the new mkdirat_fd() syscall to test compliance and to showcase its behaviour. Signed-off-by: Jori Koolstra --- tools/include/uapi/asm-generic/unistd.h | 5 +- tools/testing/selftests/filesystems/Makefile | 4 +- .../selftests/filesystems/mkdirat_fd_test.c | 139 ++++++++++++++++++ 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/filesystems/mkdirat_fd_test.c diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/a= sm-generic/unistd.h index a627acc8fb5f..5bae1029f5d9 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -863,8 +863,11 @@ __SYSCALL(__NR_listns, sys_listns) #define __NR_rseq_slice_yield 471 __SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield) =20 +#define __NR_mkdirat_fd 472 +__SYSCALL(__NR_mkdirat_fd, sys_mkdirat_fd) + #undef __NR_syscalls -#define __NR_syscalls 472 +#define __NR_syscalls 473 =20 /* * 32 bit systems traditionally used different diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/s= elftests/filesystems/Makefile index 85427d7f19b9..7357769db57a 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 =20 -CFLAGS +=3D $(KHDR_INCLUDES) -TEST_GEN_PROGS :=3D devpts_pts file_stressor anon_inode_test kernfs_test f= clog +CFLAGS +=3D $(KHDR_INCLUDES) $(TOOLS_INCLUDES) +TEST_GEN_PROGS :=3D devpts_pts file_stressor anon_inode_test kernfs_test f= clog mkdirat_fd_test TEST_GEN_PROGS_EXTENDED :=3D dnotify_test =20 include ../lib.mk diff --git a/tools/testing/selftests/filesystems/mkdirat_fd_test.c b/tools/= testing/selftests/filesystems/mkdirat_fd_test.c new file mode 100644 index 000000000000..9058be49dc7b --- /dev/null +++ b/tools/testing/selftests/filesystems/mkdirat_fd_test.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include + +#include "kselftest_harness.h" + +#ifndef MKDIRAT_FD_NEED_FD +#define MKDIRAT_FD_NEED_FD 0x01 +#endif + +#define mkdirat_fd_checked(dfd, pathname) ({ \ + struct stat __st; \ + int __fd =3D sys_mkdirat_fd(dfd, pathname, S_IRWXU, MKDIRAT_FD_NEED_FD); \ + ASSERT_GE(__fd, 0); \ + EXPECT_EQ(fstat(__fd, &__st), 0); \ + EXPECT_TRUE(S_ISDIR(__st.st_mode)); \ + __fd; \ +}) + +static inline int sys_mkdirat_fd(int dfd, const char *pathname, mode_t mod= e, + unsigned int flags) +{ + return syscall(__NR_mkdirat_fd, dfd, pathname, mode, flags); +} + +FIXTURE(mkdirat_fd) { + char dirpath[PATH_MAX]; + int dfd; +}; + +FIXTURE_SETUP(mkdirat_fd) +{ + snprintf(self->dirpath, sizeof(self->dirpath), + "/tmp/mkdirat_fd_test.%d", getpid()); + ASSERT_EQ(mkdir(self->dirpath, S_IRWXU), 0); + + self->dfd =3D open(self->dirpath, O_DIRECTORY); + ASSERT_GE(self->dfd, 0); +} + +FIXTURE_TEARDOWN(mkdirat_fd) +{ + close(self->dfd); + rmdir(self->dirpath); +} + +/* Does mkdirat_fd return a fd at all */ +TEST_F(mkdirat_fd, returns_fd) +{ + int fd =3D mkdirat_fd_checked(self->dfd, "newdir"); + EXPECT_EQ(close(fd), 0) + EXPECT_EQ(unlinkat(self->dfd, "newdir", AT_REMOVEDIR), 0); +} + +/* The fd must refer to the directory that was just created. */ +TEST_F(mkdirat_fd, fd_is_created_dir) +{ + int fd; + struct stat st_via_fd, st_via_path; + char path[PATH_MAX]; + + fd =3D mkdirat_fd_checked(self->dfd, "checkdir"); + + ASSERT_EQ(fstat(fd, &st_via_fd), 0); + + snprintf(path, sizeof(path), "%s/checkdir", self->dirpath); + ASSERT_EQ(stat(path, &st_via_path), 0); + + EXPECT_EQ(st_via_fd.st_ino, st_via_path.st_ino); + EXPECT_EQ(st_via_fd.st_dev, st_via_path.st_dev); + + EXPECT_EQ(close(fd), 0) + EXPECT_EQ(rmdir(path), 0); +} + + +/* Missing parent component must fail with ENOENT. */ +TEST_F(mkdirat_fd, enoent_missing_parent) +{ + EXPECT_EQ(sys_mkdirat_fd(self->dfd, "nonexistent/child", S_IRWXU, MKDIRAT= _FD_NEED_FD), -1); + EXPECT_EQ(errno, ENOENT); +} + +/* An invalid dfd must fail with EBADF. */ +TEST_F(mkdirat_fd, ebadf) +{ + EXPECT_EQ(sys_mkdirat_fd(-42, "badfdir", S_IRWXU, MKDIRAT_FD_NEED_FD), -1= ); + EXPECT_EQ(errno, EBADF); +} + +/* A dfd that points to a file (not a directory) must fail with ENOTDIR. */ +TEST_F(mkdirat_fd, enotdir_dfd) +{ + int file_fd; + + file_fd =3D openat(self->dfd, "file", + O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GE(file_fd, 0); + + EXPECT_EQ(sys_mkdirat_fd(file_fd, "subdir", S_IRWXU, MKDIRAT_FD_NEED_FD),= -1); + EXPECT_EQ(errno, ENOTDIR); + + EXPECT_EQ(close(file_fd), 0); + EXPECT_EQ(unlinkat(self->dfd, "file", 0), 0); +} + +/* + * The returned fd must be usable as a dfd for further *at() calls. + */ +TEST_F(mkdirat_fd, fd_usable_as_dfd) +{ + int parent_fd, child_fd; + + parent_fd =3D mkdirat_fd_checked(self->dfd, "parent"); + child_fd =3D mkdirat_fd_checked(parent_fd, "child"); + + EXPECT_EQ(close(child_fd), 0); + EXPECT_EQ(close(parent_fd), 0); + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/parent/child", self->dirpath); + EXPECT_EQ(rmdir(path), 0); + snprintf(path, sizeof(path), "%s/parent", self->dirpath); + EXPECT_EQ(rmdir(path), 0); +} + +/* Unknown flags must be rejected with EINVAL. */ +TEST_F(mkdirat_fd, einval_unknown_flags) +{ + EXPECT_EQ(sys_mkdirat_fd(self->dfd, "flagsdir", S_IRWXU, ~MKDIRAT_FD_NEED= _FD), -1); + EXPECT_EQ(errno, EINVAL); +} + +TEST_HARNESS_MAIN --=20 2.53.0