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