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