1 // SPDX-License-Identifier: GPL-2.0
12 #include <sys/mount.h>
14 #include <sys/types.h>
18 #ifndef MS_NOSYMFOLLOW
19 # define MS_NOSYMFOLLOW 256 /* Do not follow symlinks */
22 #ifndef ST_NOSYMFOLLOW
23 # define ST_NOSYMFOLLOW 0x2000 /* Do not follow symlinks */
26 #define DATA "/tmp/data"
27 #define LINK "/tmp/symlink"
30 static void die(char *fmt
, ...)
35 vfprintf(stderr
, fmt
, ap
);
40 static void vmaybe_write_file(bool enoent_ok
, char *filename
, char *fmt
,
48 buf_len
= vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
50 die("vsnprintf failed: %s\n", strerror(errno
));
52 if (buf_len
>= sizeof(buf
))
53 die("vsnprintf output truncated\n");
55 fd
= open(filename
, O_WRONLY
);
57 if ((errno
== ENOENT
) && enoent_ok
)
59 die("open of %s failed: %s\n", filename
, strerror(errno
));
62 written
= write(fd
, buf
, buf_len
);
63 if (written
!= buf_len
) {
65 die("short write to %s\n", filename
);
67 die("write to %s failed: %s\n",
68 filename
, strerror(errno
));
73 die("close of %s failed: %s\n", filename
, strerror(errno
));
76 static void maybe_write_file(char *filename
, char *fmt
, ...)
81 vmaybe_write_file(true, filename
, fmt
, ap
);
85 static void write_file(char *filename
, char *fmt
, ...)
90 vmaybe_write_file(false, filename
, fmt
, ap
);
94 static void create_and_enter_ns(void)
99 if (unshare(CLONE_NEWUSER
) != 0)
100 die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno
));
102 maybe_write_file("/proc/self/setgroups", "deny");
103 write_file("/proc/self/uid_map", "0 %d 1", uid
);
104 write_file("/proc/self/gid_map", "0 %d 1", gid
);
107 die("setgid(0) failed %s\n", strerror(errno
));
109 die("setuid(0) failed %s\n", strerror(errno
));
111 if (unshare(CLONE_NEWNS
) != 0)
112 die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno
));
115 static void setup_symlink(void)
119 data
= creat(DATA
, O_RDWR
);
121 die("creat failed: %s\n", strerror(errno
));
123 err
= symlink(DATA
, LINK
);
125 die("symlink failed: %s\n", strerror(errno
));
127 if (close(data
) != 0)
128 die("close of %s failed: %s\n", DATA
, strerror(errno
));
131 static void test_link_traversal(bool nosymfollow
)
135 link
= open(LINK
, 0, O_RDWR
);
137 if ((link
!= -1 || errno
!= ELOOP
)) {
138 die("link traversal unexpected result: %d, %s\n",
139 link
, strerror(errno
));
143 die("link traversal failed: %s\n", strerror(errno
));
145 if (close(link
) != 0)
146 die("close of link failed: %s\n", strerror(errno
));
150 static void test_readlink(void)
155 bzero(buf
, sizeof(buf
));
157 ret
= readlink(LINK
, buf
, sizeof(buf
));
159 die("readlink failed: %s\n", strerror(errno
));
160 if (strcmp(buf
, DATA
) != 0)
161 die("readlink strcmp failed: '%s' '%s'\n", buf
, DATA
);
164 static void test_realpath(void)
166 char *path
= realpath(LINK
, NULL
);
169 die("realpath failed: %s\n", strerror(errno
));
170 if (strcmp(path
, DATA
) != 0)
171 die("realpath strcmp failed\n");
176 static void test_statfs(bool nosymfollow
)
181 ret
= statfs(TMP
, &buf
);
183 die("statfs failed: %s\n", strerror(errno
));
186 if ((buf
.f_flags
& ST_NOSYMFOLLOW
) == 0)
187 die("ST_NOSYMFOLLOW not set on %s\n", TMP
);
189 if ((buf
.f_flags
& ST_NOSYMFOLLOW
) != 0)
190 die("ST_NOSYMFOLLOW set on %s\n", TMP
);
194 static void run_tests(bool nosymfollow
)
196 test_link_traversal(nosymfollow
);
199 test_statfs(nosymfollow
);
202 int main(int argc
, char **argv
)
204 create_and_enter_ns();
206 if (mount("testing", TMP
, "ramfs", 0, NULL
) != 0)
207 die("mount failed: %s\n", strerror(errno
));
212 if (mount("testing", TMP
, "ramfs", MS_REMOUNT
|MS_NOSYMFOLLOW
, NULL
) != 0)
213 die("remount failed: %s\n", strerror(errno
));