xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/mount/mount07.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023 FUJITSU LIMITED. All rights reserved.
4  * Author: Yang Xu <[email protected]>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * It is a basic test for MS_NOSYMFOLLOW mount option and is copied
11  * from kernel selftests nosymfollow-test.c.
12  *
13  * It tests to make sure that symlink traversal fails with ELOOP when
14  * 'nosymfollow' is set, but symbolic links can still be created, and
15  * readlink(2) and realpath(3) still work properly. It also verifies
16  * that statfs(2) correctly returns ST_NOSYMFOLLOW.
17  */
18 
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <sys/mount.h>
24 #include <stdbool.h>
25 #include "tst_test.h"
26 #include "lapi/mount.h"
27 
28 #ifndef ST_NOSYMFOLLOW
29 # define ST_NOSYMFOLLOW 0x2000
30 #endif
31 
32 #define MNTPOINT "mntpoint"
33 
34 static char test_file[PATH_MAX];
35 static char link_file[PATH_MAX];
36 static char temp_link_file[PATH_MAX];
37 static int flag;
38 
setup_symlink(void)39 static void setup_symlink(void)
40 {
41 	int fd;
42 
43 	fd = SAFE_CREAT(test_file, O_RDWR);
44 	SAFE_SYMLINK(test_file, link_file);
45 	SAFE_CLOSE(fd);
46 	flag = 1;
47 }
48 
test_link_traversal(bool nosymfollow)49 static void test_link_traversal(bool nosymfollow)
50 {
51 	if (nosymfollow) {
52 		TST_EXP_FAIL2(open(link_file, 0, O_RDWR), ELOOP,
53 				"open(%s, 0, O_RDWR)", link_file);
54 	} else {
55 		TST_EXP_FD(open(link_file, 0, O_RDWR));
56 	}
57 
58 	if (TST_RET > 0)
59 		SAFE_CLOSE(TST_RET);
60 }
61 
test_readlink(void)62 static void test_readlink(void)
63 {
64 	char buf[4096];
65 
66 	memset(buf, 0, 4096);
67 	TST_EXP_POSITIVE(readlink(link_file, buf, sizeof(buf)),
68 			"readlink(%s, buf, %ld)", link_file, sizeof(buf));
69 	if (strcmp(buf, test_file) != 0) {
70 		tst_res(TFAIL, "readlink strcmp failed, %s, %s",
71 				buf, test_file);
72 	} else {
73 		tst_res(TPASS, "readlink strcmp succeeded");
74 	}
75 }
76 
test_realpath(void)77 static void test_realpath(void)
78 {
79 	TESTPTR(realpath(link_file, NULL));
80 
81 	if (!TST_RET_PTR) {
82 		tst_res(TFAIL | TERRNO, "realpath failed");
83 		return;
84 	}
85 
86 	if (strcmp(TST_RET_PTR, test_file) != 0) {
87 		tst_res(TFAIL, "realpath strcmp failed, %s, %s",
88 				(char *)TST_RET_PTR, test_file);
89 	} else {
90 		tst_res(TPASS, "realpath strcmp succeeded");
91 	}
92 }
93 
test_cycle_link(void)94 static void test_cycle_link(void)
95 {
96 	TST_EXP_PASS(symlink(test_file, temp_link_file), "symlink(%s, %s)",
97 			test_file, temp_link_file);
98 	TST_EXP_PASS(unlink(temp_link_file));
99 }
100 
test_statfs(bool nosymfollow)101 static void test_statfs(bool nosymfollow)
102 {
103 	struct statfs buf;
104 
105 	SAFE_STATFS(MNTPOINT, &buf);
106 	if (buf.f_flags & ST_NOSYMFOLLOW) {
107 		tst_res(nosymfollow ? TPASS : TFAIL, "ST_NOSYMFOLLOW set on %s",
108 				MNTPOINT);
109 	} else {
110 		tst_res(nosymfollow ? TFAIL : TPASS, "ST_NOSYMFOLLOW not set on %s",
111 				MNTPOINT);
112 	}
113 }
114 
setup(void)115 static void setup(void)
116 {
117 	char *tmpdir = tst_get_tmpdir();
118 
119 	snprintf(test_file, PATH_MAX, "%s/%s/test_file", tst_get_tmpdir(),
120 			MNTPOINT);
121 	snprintf(link_file, PATH_MAX, "%s/%s/link_file", tst_get_tmpdir(),
122 			MNTPOINT);
123 	snprintf(temp_link_file, PATH_MAX, "%s/%s/temp_link_file",
124 			tst_get_tmpdir(), MNTPOINT);
125 	free(tmpdir);
126 }
127 
cleanup(void)128 static void cleanup(void)
129 {
130 	if (tst_is_mounted(MNTPOINT))
131 		SAFE_UMOUNT(MNTPOINT);
132 }
133 
run_tests(bool nosymfollow)134 static void run_tests(bool nosymfollow)
135 {
136 	test_link_traversal(nosymfollow);
137 	test_readlink();
138 	test_realpath();
139 	test_cycle_link();
140 	test_statfs(nosymfollow);
141 }
142 
run(void)143 static void run(void)
144 {
145 	tst_res(TINFO, "Testing behaviour when not setting MS_NOSYMFOLLOW");
146 
147 	TST_EXP_PASS_SILENT(mount(tst_device->dev, MNTPOINT, tst_device->fs_type,
148 		0, NULL));
149 	if (!flag || !strcmp(tst_device->fs_type, "tmpfs"))
150 		setup_symlink();
151 	run_tests(false);
152 
153 	tst_res(TINFO, "Testing behaviour when setting MS_NOSYMFOLLOW");
154 	TST_EXP_PASS_SILENT(mount(tst_device->dev, MNTPOINT, tst_device->fs_type,
155 		MS_REMOUNT | MS_NOSYMFOLLOW, NULL));
156 	run_tests(true);
157 
158 	SAFE_UMOUNT(MNTPOINT);
159 }
160 
161 static struct tst_test test = {
162 	.test_all = run,
163 	.setup = setup,
164 	.cleanup = cleanup,
165 	.forks_child = 1,
166 	.needs_root = 1,
167 	.min_kver = "5.10",
168 	.format_device = 1,
169 	.mntpoint = MNTPOINT,
170 	.all_filesystems = 1,
171 	.skip_filesystems = (const char *const []){
172 		"exfat",
173 		"vfat",
174 		"ntfs",
175 		NULL
176 	},
177 };
178