1*b315a0a8SYosh #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")]
2*b315a0a8SYosh
3*b315a0a8SYosh use std::{env, process};
4*b315a0a8SYosh use test_programs::preview1::{assert_errno, open_scratch_directory};
5*b315a0a8SYosh
test_nofollow_errors(dir_fd: wasip1::Fd)6*b315a0a8SYosh unsafe fn test_nofollow_errors(dir_fd: wasip1::Fd) {
7*b315a0a8SYosh // Create a directory for the symlink to point to.
8*b315a0a8SYosh wasip1::path_create_directory(dir_fd, "target").expect("creating a dir");
9*b315a0a8SYosh
10*b315a0a8SYosh // Create a symlink.
11*b315a0a8SYosh wasip1::path_symlink("target", dir_fd, "symlink").expect("creating a symlink");
12*b315a0a8SYosh
13*b315a0a8SYosh // Try to open it as a directory with O_NOFOLLOW again.
14*b315a0a8SYosh assert_errno!(
15*b315a0a8SYosh wasip1::path_open(dir_fd, 0, "symlink", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)
16*b315a0a8SYosh .expect_err("opening a directory symlink as a directory should fail"),
17*b315a0a8SYosh wasip1::ERRNO_LOOP,
18*b315a0a8SYosh wasip1::ERRNO_NOTDIR
19*b315a0a8SYosh );
20*b315a0a8SYosh
21*b315a0a8SYosh // Try to open it with just O_NOFOLLOW.
22*b315a0a8SYosh assert_errno!(
23*b315a0a8SYosh wasip1::path_open(dir_fd, 0, "symlink", 0, 0, 0, 0)
24*b315a0a8SYosh .expect_err("opening a symlink with O_NOFOLLOW should fail"),
25*b315a0a8SYosh wasip1::ERRNO_LOOP,
26*b315a0a8SYosh wasip1::ERRNO_ACCES
27*b315a0a8SYosh );
28*b315a0a8SYosh
29*b315a0a8SYosh // Try to open it as a directory without O_NOFOLLOW.
30*b315a0a8SYosh let file_fd = wasip1::path_open(
31*b315a0a8SYosh dir_fd,
32*b315a0a8SYosh wasip1::LOOKUPFLAGS_SYMLINK_FOLLOW,
33*b315a0a8SYosh "symlink",
34*b315a0a8SYosh wasip1::OFLAGS_DIRECTORY,
35*b315a0a8SYosh 0,
36*b315a0a8SYosh 0,
37*b315a0a8SYosh 0,
38*b315a0a8SYosh )
39*b315a0a8SYosh .expect("opening a symlink as a directory");
40*b315a0a8SYosh assert!(
41*b315a0a8SYosh file_fd > libc::STDERR_FILENO as wasip1::Fd,
42*b315a0a8SYosh "file descriptor range check",
43*b315a0a8SYosh );
44*b315a0a8SYosh wasip1::fd_close(file_fd).expect("closing a file");
45*b315a0a8SYosh
46*b315a0a8SYosh // Replace the target directory with a file.
47*b315a0a8SYosh wasip1::path_unlink_file(dir_fd, "symlink").expect("removing a file");
48*b315a0a8SYosh wasip1::path_remove_directory(dir_fd, "target")
49*b315a0a8SYosh .expect("remove_directory on a directory should succeed");
50*b315a0a8SYosh
51*b315a0a8SYosh let file_fd = wasip1::path_open(dir_fd, 0, "target", wasip1::OFLAGS_CREAT, 0, 0, 0)
52*b315a0a8SYosh .expect("creating a file");
53*b315a0a8SYosh wasip1::fd_close(file_fd).expect("closing a file");
54*b315a0a8SYosh wasip1::path_symlink("target", dir_fd, "symlink").expect("creating a symlink");
55*b315a0a8SYosh
56*b315a0a8SYosh // Try to open it as a directory with O_NOFOLLOW again.
57*b315a0a8SYosh assert_errno!(
58*b315a0a8SYosh wasip1::path_open(dir_fd, 0, "symlink", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)
59*b315a0a8SYosh .expect_err("opening a directory symlink as a directory should fail"),
60*b315a0a8SYosh wasip1::ERRNO_LOOP,
61*b315a0a8SYosh wasip1::ERRNO_NOTDIR
62*b315a0a8SYosh );
63*b315a0a8SYosh
64*b315a0a8SYosh // Try to open it with just O_NOFOLLOW.
65*b315a0a8SYosh assert_errno!(
66*b315a0a8SYosh wasip1::path_open(dir_fd, 0, "symlink", 0, 0, 0, 0)
67*b315a0a8SYosh .expect_err("opening a symlink with NOFOLLOW should fail"),
68*b315a0a8SYosh wasip1::ERRNO_LOOP
69*b315a0a8SYosh );
70*b315a0a8SYosh
71*b315a0a8SYosh // Try to open it as a directory without O_NOFOLLOW.
72*b315a0a8SYosh assert_errno!(
73*b315a0a8SYosh wasip1::path_open(
74*b315a0a8SYosh dir_fd,
75*b315a0a8SYosh wasip1::LOOKUPFLAGS_SYMLINK_FOLLOW,
76*b315a0a8SYosh "symlink",
77*b315a0a8SYosh wasip1::OFLAGS_DIRECTORY,
78*b315a0a8SYosh 0,
79*b315a0a8SYosh 0,
80*b315a0a8SYosh 0,
81*b315a0a8SYosh )
82*b315a0a8SYosh .expect_err("opening a symlink to a file as a directory"),
83*b315a0a8SYosh wasip1::ERRNO_NOTDIR
84*b315a0a8SYosh );
85*b315a0a8SYosh
86*b315a0a8SYosh // Clean up.
87*b315a0a8SYosh wasip1::path_unlink_file(dir_fd, "target").expect("removing a file");
88*b315a0a8SYosh wasip1::path_unlink_file(dir_fd, "symlink").expect("removing a file");
89*b315a0a8SYosh }
90*b315a0a8SYosh
main()91*b315a0a8SYosh fn main() {
92*b315a0a8SYosh let mut args = env::args();
93*b315a0a8SYosh let prog = args.next().unwrap();
94*b315a0a8SYosh let arg = if let Some(arg) = args.next() {
95*b315a0a8SYosh arg
96*b315a0a8SYosh } else {
97*b315a0a8SYosh eprintln!("usage: {prog} <scratch directory>");
98*b315a0a8SYosh process::exit(1);
99*b315a0a8SYosh };
100*b315a0a8SYosh
101*b315a0a8SYosh // Open scratch directory
102*b315a0a8SYosh let dir_fd = match open_scratch_directory(&arg) {
103*b315a0a8SYosh Ok(dir_fd) => dir_fd,
104*b315a0a8SYosh Err(err) => {
105*b315a0a8SYosh eprintln!("{err}");
106*b315a0a8SYosh process::exit(1)
107*b315a0a8SYosh }
108*b315a0a8SYosh };
109*b315a0a8SYosh
110*b315a0a8SYosh // Run the tests.
111*b315a0a8SYosh unsafe { test_nofollow_errors(dir_fd) }
112*b315a0a8SYosh }
113