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