xref: /openbmc/linux/tools/testing/selftests/fchmodat2/fchmodat2_test.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1*4859c257SAlexey Gladkov // SPDX-License-Identifier: GPL-2.0-or-later
2*4859c257SAlexey Gladkov 
3*4859c257SAlexey Gladkov #define _GNU_SOURCE
4*4859c257SAlexey Gladkov #include <fcntl.h>
5*4859c257SAlexey Gladkov #include <sys/stat.h>
6*4859c257SAlexey Gladkov #include <sys/types.h>
7*4859c257SAlexey Gladkov #include <syscall.h>
8*4859c257SAlexey Gladkov #include <unistd.h>
9*4859c257SAlexey Gladkov 
10*4859c257SAlexey Gladkov #include "../kselftest.h"
11*4859c257SAlexey Gladkov 
sys_fchmodat2(int dfd,const char * filename,mode_t mode,int flags)12*4859c257SAlexey Gladkov int sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags)
13*4859c257SAlexey Gladkov {
14*4859c257SAlexey Gladkov 	int ret = syscall(__NR_fchmodat2, dfd, filename, mode, flags);
15*4859c257SAlexey Gladkov 
16*4859c257SAlexey Gladkov 	return ret >= 0 ? ret : -errno;
17*4859c257SAlexey Gladkov }
18*4859c257SAlexey Gladkov 
setup_testdir(void)19*4859c257SAlexey Gladkov int setup_testdir(void)
20*4859c257SAlexey Gladkov {
21*4859c257SAlexey Gladkov 	int dfd, ret;
22*4859c257SAlexey Gladkov 	char dirname[] = "/tmp/ksft-fchmodat2.XXXXXX";
23*4859c257SAlexey Gladkov 
24*4859c257SAlexey Gladkov 	/* Make the top-level directory. */
25*4859c257SAlexey Gladkov 	if (!mkdtemp(dirname))
26*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: failed to create tmpdir\n", __func__);
27*4859c257SAlexey Gladkov 
28*4859c257SAlexey Gladkov 	dfd = open(dirname, O_PATH | O_DIRECTORY);
29*4859c257SAlexey Gladkov 	if (dfd < 0)
30*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: failed to open tmpdir\n", __func__);
31*4859c257SAlexey Gladkov 
32*4859c257SAlexey Gladkov 	ret = openat(dfd, "regfile", O_CREAT | O_WRONLY | O_TRUNC, 0644);
33*4859c257SAlexey Gladkov 	if (ret < 0)
34*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: failed to create file in tmpdir\n",
35*4859c257SAlexey Gladkov 				__func__);
36*4859c257SAlexey Gladkov 	close(ret);
37*4859c257SAlexey Gladkov 
38*4859c257SAlexey Gladkov 	ret = symlinkat("regfile", dfd, "symlink");
39*4859c257SAlexey Gladkov 	if (ret < 0)
40*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: failed to create symlink in tmpdir\n",
41*4859c257SAlexey Gladkov 				__func__);
42*4859c257SAlexey Gladkov 
43*4859c257SAlexey Gladkov 	return dfd;
44*4859c257SAlexey Gladkov }
45*4859c257SAlexey Gladkov 
expect_mode(int dfd,const char * filename,mode_t expect_mode)46*4859c257SAlexey Gladkov int expect_mode(int dfd, const char *filename, mode_t expect_mode)
47*4859c257SAlexey Gladkov {
48*4859c257SAlexey Gladkov 	struct stat st;
49*4859c257SAlexey Gladkov 	int ret = fstatat(dfd, filename, &st, AT_SYMLINK_NOFOLLOW);
50*4859c257SAlexey Gladkov 
51*4859c257SAlexey Gladkov 	if (ret)
52*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: %s: fstatat failed\n",
53*4859c257SAlexey Gladkov 				__func__, filename);
54*4859c257SAlexey Gladkov 
55*4859c257SAlexey Gladkov 	return (st.st_mode == expect_mode);
56*4859c257SAlexey Gladkov }
57*4859c257SAlexey Gladkov 
test_regfile(void)58*4859c257SAlexey Gladkov void test_regfile(void)
59*4859c257SAlexey Gladkov {
60*4859c257SAlexey Gladkov 	int dfd, ret;
61*4859c257SAlexey Gladkov 
62*4859c257SAlexey Gladkov 	dfd = setup_testdir();
63*4859c257SAlexey Gladkov 
64*4859c257SAlexey Gladkov 	ret = sys_fchmodat2(dfd, "regfile", 0640, 0);
65*4859c257SAlexey Gladkov 
66*4859c257SAlexey Gladkov 	if (ret < 0)
67*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
68*4859c257SAlexey Gladkov 
69*4859c257SAlexey Gladkov 	if (!expect_mode(dfd, "regfile", 0100640))
70*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
71*4859c257SAlexey Gladkov 				__func__);
72*4859c257SAlexey Gladkov 
73*4859c257SAlexey Gladkov 	ret = sys_fchmodat2(dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW);
74*4859c257SAlexey Gladkov 
75*4859c257SAlexey Gladkov 	if (ret < 0)
76*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: fchmodat2(AT_SYMLINK_NOFOLLOW) failed\n",
77*4859c257SAlexey Gladkov 				__func__);
78*4859c257SAlexey Gladkov 
79*4859c257SAlexey Gladkov 	if (!expect_mode(dfd, "regfile", 0100600))
80*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
81*4859c257SAlexey Gladkov 				__func__);
82*4859c257SAlexey Gladkov 
83*4859c257SAlexey Gladkov 	ksft_test_result_pass("fchmodat2(regfile)\n");
84*4859c257SAlexey Gladkov }
85*4859c257SAlexey Gladkov 
test_symlink(void)86*4859c257SAlexey Gladkov void test_symlink(void)
87*4859c257SAlexey Gladkov {
88*4859c257SAlexey Gladkov 	int dfd, ret;
89*4859c257SAlexey Gladkov 
90*4859c257SAlexey Gladkov 	dfd = setup_testdir();
91*4859c257SAlexey Gladkov 
92*4859c257SAlexey Gladkov 	ret = sys_fchmodat2(dfd, "symlink", 0640, 0);
93*4859c257SAlexey Gladkov 
94*4859c257SAlexey Gladkov 	if (ret < 0)
95*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
96*4859c257SAlexey Gladkov 
97*4859c257SAlexey Gladkov 	if (!expect_mode(dfd, "regfile", 0100640))
98*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
99*4859c257SAlexey Gladkov 				__func__);
100*4859c257SAlexey Gladkov 
101*4859c257SAlexey Gladkov 	if (!expect_mode(dfd, "symlink", 0120777))
102*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2\n",
103*4859c257SAlexey Gladkov 				__func__);
104*4859c257SAlexey Gladkov 
105*4859c257SAlexey Gladkov 	ret = sys_fchmodat2(dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW);
106*4859c257SAlexey Gladkov 
107*4859c257SAlexey Gladkov 	/*
108*4859c257SAlexey Gladkov 	 * On certain filesystems (xfs or btrfs), chmod operation fails. So we
109*4859c257SAlexey Gladkov 	 * first check the symlink target but if the operation fails we mark the
110*4859c257SAlexey Gladkov 	 * test as skipped.
111*4859c257SAlexey Gladkov 	 *
112*4859c257SAlexey Gladkov 	 * https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html
113*4859c257SAlexey Gladkov 	 */
114*4859c257SAlexey Gladkov 	if (ret == 0 && !expect_mode(dfd, "symlink", 0120600))
115*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n",
116*4859c257SAlexey Gladkov 				__func__);
117*4859c257SAlexey Gladkov 
118*4859c257SAlexey Gladkov 	if (!expect_mode(dfd, "regfile", 0100640))
119*4859c257SAlexey Gladkov 		ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
120*4859c257SAlexey Gladkov 				__func__);
121*4859c257SAlexey Gladkov 
122*4859c257SAlexey Gladkov 	if (ret != 0)
123*4859c257SAlexey Gladkov 		ksft_test_result_skip("fchmodat2(symlink)\n");
124*4859c257SAlexey Gladkov 	else
125*4859c257SAlexey Gladkov 		ksft_test_result_pass("fchmodat2(symlink)\n");
126*4859c257SAlexey Gladkov }
127*4859c257SAlexey Gladkov 
128*4859c257SAlexey Gladkov #define NUM_TESTS 2
129*4859c257SAlexey Gladkov 
main(int argc,char ** argv)130*4859c257SAlexey Gladkov int main(int argc, char **argv)
131*4859c257SAlexey Gladkov {
132*4859c257SAlexey Gladkov 	ksft_print_header();
133*4859c257SAlexey Gladkov 	ksft_set_plan(NUM_TESTS);
134*4859c257SAlexey Gladkov 
135*4859c257SAlexey Gladkov 	test_regfile();
136*4859c257SAlexey Gladkov 	test_symlink();
137*4859c257SAlexey Gladkov 
138*4859c257SAlexey Gladkov 	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
139*4859c257SAlexey Gladkov 		ksft_exit_fail();
140*4859c257SAlexey Gladkov 	else
141*4859c257SAlexey Gladkov 		ksft_exit_pass();
142*4859c257SAlexey Gladkov }
143