• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023 FUJITSU LIMITED. All rights reserved.
4  * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
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;
35 static char *link_file;
36 static char *temp_link_file;
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 	test_file = tst_tmpdir_genpath("%s/test_file", MNTPOINT);
118 	link_file = tst_tmpdir_genpath("%s/link_file", MNTPOINT);
119 	temp_link_file = tst_tmpdir_genpath("%s/temp_link_file", MNTPOINT);
120 }
121 
cleanup(void)122 static void cleanup(void)
123 {
124 	if (tst_is_mounted(MNTPOINT))
125 		SAFE_UMOUNT(MNTPOINT);
126 }
127 
run_tests(bool nosymfollow)128 static void run_tests(bool nosymfollow)
129 {
130 	test_link_traversal(nosymfollow);
131 	test_readlink();
132 	test_realpath();
133 	test_cycle_link();
134 	test_statfs(nosymfollow);
135 }
136 
run(void)137 static void run(void)
138 {
139 	tst_res(TINFO, "Testing behaviour when not setting MS_NOSYMFOLLOW");
140 
141 	TST_EXP_PASS_SILENT(mount(tst_device->dev, MNTPOINT, tst_device->fs_type,
142 		0, NULL));
143 	if (!flag || !strcmp(tst_device->fs_type, "tmpfs"))
144 		setup_symlink();
145 	run_tests(false);
146 
147 	tst_res(TINFO, "Testing behaviour when setting MS_NOSYMFOLLOW");
148 	TST_EXP_PASS_SILENT(mount(tst_device->dev, MNTPOINT, tst_device->fs_type,
149 		MS_REMOUNT | MS_NOSYMFOLLOW, NULL));
150 	run_tests(true);
151 
152 	SAFE_UMOUNT(MNTPOINT);
153 }
154 
155 static struct tst_test test = {
156 	.test_all = run,
157 	.setup = setup,
158 	.cleanup = cleanup,
159 	.forks_child = 1,
160 	.needs_root = 1,
161 	.min_kver = "5.10",
162 	.format_device = 1,
163 	.mntpoint = MNTPOINT,
164 	.all_filesystems = 1,
165 	.skip_filesystems = (const char *const []){
166 		"exfat",
167 		"vfat",
168 		"ntfs",
169 		NULL
170 	},
171 };
172