xref: /linux/tools/testing/selftests/filesystems/fclog.c (revision 26ba30221c03364d6ed9910be8da4c1fd871b07b)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Author: Aleksa Sarai <cyphar@cyphar.com>
4  * Copyright (C) 2025 SUSE LLC.
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <sched.h>
11 #include <unistd.h>
12 #include <sys/mount.h>
13 
14 #include "kselftest_harness.h"
15 
16 #define ASSERT_ERRNO(expected, _t, seen)				\
17 	__EXPECT(expected, #expected,					\
18 		({__typeof__(seen) _tmp_seen = (seen);			\
19 		  _tmp_seen >= 0 ? _tmp_seen : -errno; }), #seen, _t, 1)
20 
21 #define ASSERT_ERRNO_EQ(expected, seen) \
22 	ASSERT_ERRNO(expected, ==, seen)
23 
24 #define ASSERT_SUCCESS(seen) \
25 	ASSERT_ERRNO(0, <=, seen)
26 
27 FIXTURE(ns)
28 {
29 	int host_mntns;
30 };
31 
32 FIXTURE_SETUP(ns)
33 {
34 	/* Stash the old mntns. */
35 	self->host_mntns = open("/proc/self/ns/mnt", O_RDONLY|O_CLOEXEC);
36 	ASSERT_SUCCESS(self->host_mntns);
37 
38 	/* Create a new mount namespace and make it private. */
39 	ASSERT_SUCCESS(unshare(CLONE_NEWNS));
40 	ASSERT_SUCCESS(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL));
41 }
42 
43 FIXTURE_TEARDOWN(ns)
44 {
45 	ASSERT_SUCCESS(setns(self->host_mntns, CLONE_NEWNS));
46 	ASSERT_SUCCESS(close(self->host_mntns));
47 }
48 
49 TEST_F(ns, fscontext_log_enodata)
50 {
51 	int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
52 	ASSERT_SUCCESS(fsfd);
53 
54 	/* A brand new fscontext has no log entries. */
55 	char buf[128] = {};
56 	for (int i = 0; i < 16; i++)
57 		ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
58 
59 	ASSERT_SUCCESS(close(fsfd));
60 }
61 
62 TEST_F(ns, fscontext_log_errorfc)
63 {
64 	int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
65 	ASSERT_SUCCESS(fsfd);
66 
67 	ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
68 
69 	char buf[128] = {};
70 	ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
71 	EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
72 
73 	/* The message has been consumed. */
74 	ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
75 	ASSERT_SUCCESS(close(fsfd));
76 }
77 
78 TEST_F(ns, fscontext_log_errorfc_after_fsmount)
79 {
80 	int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
81 	ASSERT_SUCCESS(fsfd);
82 
83 	ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
84 
85 	ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
86 	int mfd = fsmount(fsfd, FSMOUNT_CLOEXEC, MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NOSUID);
87 	ASSERT_SUCCESS(mfd);
88 	ASSERT_SUCCESS(move_mount(mfd, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH));
89 
90 	/*
91 	 * The fscontext log should still contain data even after
92 	 * FSCONFIG_CMD_CREATE and fsmount().
93 	 */
94 	char buf[128] = {};
95 	ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
96 	EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
97 
98 	/* The message has been consumed. */
99 	ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
100 	ASSERT_SUCCESS(close(fsfd));
101 }
102 
103 TEST_F(ns, fscontext_log_emsgsize)
104 {
105 	int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
106 	ASSERT_SUCCESS(fsfd);
107 
108 	ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
109 
110 	char buf[128] = {};
111 	/*
112 	 * Attempting to read a message with too small a buffer should not
113 	 * result in the message getting consumed.
114 	 */
115 	ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 0));
116 	ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 1));
117 	for (int i = 0; i < 16; i++)
118 		ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 16));
119 
120 	ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
121 	EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
122 
123 	/* The message has been consumed. */
124 	ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
125 	ASSERT_SUCCESS(close(fsfd));
126 }
127 
128 TEST_HARNESS_MAIN
129