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, config, create_file, open_scratch_directory};
5
6 // These are all macro-rules so the panic line number shows us where
7 // things went wrong.
8
9 macro_rules! filestats_assert_eq {
10 ($left:ident, $right:ident) => {
11 assert_eq!($left.dev, $right.dev, "dev should be equal");
12 assert_eq!($left.ino, $right.ino, "ino should be equal");
13 assert_eq!($left.atim, $right.atim, "atim should be equal");
14 assert_eq!($left.ctim, $right.ctim, "ctim should be equal");
15 assert_eq!($left.mtim, $right.mtim, "mtim should be equal");
16 assert_eq!($left.size, $right.size, "size should be equal");
17 assert_eq!($left.nlink, $right.nlink, "nlink should be equal");
18 assert_eq!($left.filetype, $right.filetype, "filetype should be equal");
19 };
20 }
21
22 macro_rules! fdstats_assert_eq {
23 ($left:ident, $right:ident) => {
24 assert_eq!($left.fs_flags, $right.fs_flags, "fs_flags should be equal");
25 assert_eq!(
26 $left.fs_filetype, $right.fs_filetype,
27 "fs_filetype should be equal"
28 );
29 assert_eq!(
30 $left.fs_rights_base, $right.fs_rights_base,
31 "fs_rights_base should be equal"
32 );
33 assert_eq!(
34 $left.fs_rights_inheriting, $right.fs_rights_inheriting,
35 "fs_rights_inheriting should be equal"
36 );
37 };
38 }
39
40 macro_rules! check_rights {
41 ($orig_fd:ident, $link_fd:ident) => {
42 let orig_filestat =
43 wasip1::fd_filestat_get($orig_fd).expect("reading filestat of the source");
44 let link_filestat =
45 wasip1::fd_filestat_get($link_fd).expect("reading filestat of the link");
46 filestats_assert_eq!(orig_filestat, link_filestat);
47
48 // Compare Fdstats
49 let orig_fdstat = wasip1::fd_fdstat_get($orig_fd).expect("reading fdstat of the source");
50 let link_fdstat = wasip1::fd_fdstat_get($link_fd).expect("reading fdstat of the link");
51 fdstats_assert_eq!(orig_fdstat, link_fdstat);
52 };
53 }
54 // Extra calls of fd_close are needed for Windows, which will not remove
55 // the directory until all handles are closed.
test_path_link(dir_fd: wasip1::Fd)56 unsafe fn test_path_link(dir_fd: wasip1::Fd) {
57 // Create a file
58 let create_fd =
59 wasip1::path_open(dir_fd, 0, "file", wasip1::OFLAGS_CREAT, 0, 0, 0).expect("create file");
60 wasip1::fd_close(create_fd).unwrap();
61
62 // Open a fresh descriptor to the file. We won't have a write right that was implied by OFLAGS_CREAT
63 // above.
64 let file_fd = wasip1::path_open(dir_fd, 0, "file", 0, 0, 0, 0).expect("open file");
65
66 // Create a link in the same directory and compare rights
67 wasip1::path_link(dir_fd, 0, "file", dir_fd, "link")
68 .expect("creating a link in the same directory");
69
70 let link_fd = wasip1::path_open(dir_fd, 0, "link", 0, 0, 0, 0).expect("open link");
71
72 check_rights!(file_fd, link_fd);
73 wasip1::fd_close(link_fd).expect("Closing link_fd"); // needed for Windows
74 wasip1::path_unlink_file(dir_fd, "link").expect("removing a link");
75
76 // Create a link in a different directory and compare rights
77 wasip1::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
78 let subdir_fd = wasip1::path_open(dir_fd, 0, "subdir", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)
79 .expect("open subdir directory");
80 wasip1::path_link(dir_fd, 0, "file", subdir_fd, "link")
81 .expect("creating a link in subdirectory");
82 let link_fd = wasip1::path_open(subdir_fd, 0, "link", 0, 0, 0, 0).expect("open link in subdir");
83 check_rights!(file_fd, link_fd);
84 wasip1::fd_close(link_fd).expect("Closing link_fd"); // needed for Windows
85 wasip1::path_unlink_file(subdir_fd, "link").expect("removing a link");
86 wasip1::fd_close(subdir_fd).expect("Closing subdir_fd"); // needed for Windows
87 wasip1::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");
88
89 // Create a link to a path that already exists
90 create_file(dir_fd, "link");
91
92 assert_errno!(
93 wasip1::path_link(dir_fd, 0, "file", dir_fd, "link")
94 .expect_err("creating a link to existing path should fail"),
95 wasip1::ERRNO_EXIST
96 );
97 wasip1::path_unlink_file(dir_fd, "link").expect("removing a file");
98
99 // Create a link to itself
100 assert_errno!(
101 wasip1::path_link(dir_fd, 0, "file", dir_fd, "file")
102 .expect_err("creating a link to itself should fail"),
103 wasip1::ERRNO_EXIST
104 );
105
106 // Create a link where target is a directory
107 wasip1::path_create_directory(dir_fd, "link").expect("creating a dir");
108
109 assert_errno!(
110 wasip1::path_link(dir_fd, 0, "file", dir_fd, "link")
111 .expect_err("creating a link where target is a directory should fail"),
112 wasip1::ERRNO_EXIST
113 );
114 wasip1::path_remove_directory(dir_fd, "link").expect("removing a dir");
115
116 // Create a link to a directory
117 wasip1::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
118 let subdir_fd = wasip1::path_open(dir_fd, 0, "subdir", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)
119 .expect("open new descriptor to subdir");
120
121 assert_errno!(
122 wasip1::path_link(dir_fd, 0, "subdir", dir_fd, "link")
123 .expect_err("creating a link to a directory should fail"),
124 wasip1::ERRNO_PERM,
125 wasip1::ERRNO_ACCES
126 );
127 wasip1::fd_close(subdir_fd).expect("close subdir before deleting it");
128 wasip1::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");
129
130 // Create a link to a file with trailing slash
131 assert_errno!(
132 wasip1::path_link(dir_fd, 0, "file", dir_fd, "link/")
133 .expect_err("creating a link to a file with trailing slash should fail"),
134 wasip1::ERRNO_NOENT
135 );
136
137 if config().support_dangling_filesystem() {
138 // Create a link to a dangling symlink
139 wasip1::path_symlink("target", dir_fd, "symlink").expect("creating a dangling symlink");
140
141 // This should succeed, because we're not following symlinks
142 wasip1::path_link(dir_fd, 0, "symlink", dir_fd, "link")
143 .expect("creating a link to a dangling symlink should succeed");
144 wasip1::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
145 wasip1::path_unlink_file(dir_fd, "link").expect("removing a hardlink");
146
147 // Create a link to a symlink loop
148 wasip1::path_symlink("symlink", dir_fd, "symlink").expect("creating a symlink loop");
149
150 wasip1::path_link(dir_fd, 0, "symlink", dir_fd, "link")
151 .expect("creating a link to a symlink loop should succeed");
152 wasip1::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
153 wasip1::path_unlink_file(dir_fd, "link").expect("removing a hardlink");
154
155 // Create a link where target is a dangling symlink
156 wasip1::path_symlink("target", dir_fd, "symlink").expect("creating a dangling symlink");
157
158 assert_errno!(
159 wasip1::path_link(dir_fd, 0, "file", dir_fd, "symlink")
160 .expect_err("creating a link where target is a dangling symlink"),
161 wasip1::ERRNO_EXIST
162 );
163 wasip1::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
164
165 // Create a link where target is a dangling symlink following symlinks
166 wasip1::path_symlink("target", dir_fd, "symlink").expect("creating a dangling symlink");
167
168 // Symlink following with path_link is rejected
169 assert_errno!(
170 wasip1::path_link(
171 dir_fd,
172 wasip1::LOOKUPFLAGS_SYMLINK_FOLLOW,
173 "symlink",
174 dir_fd,
175 "link",
176 )
177 .expect_err("calling path_link with LOOKUPFLAGS_SYMLINK_FOLLOW should fail"),
178 wasip1::ERRNO_INVAL
179 );
180
181 // Clean up.
182 wasip1::path_unlink_file(dir_fd, "file").expect("removing a file");
183 }
184 }
185
main()186 fn main() {
187 let mut args = env::args();
188 let prog = args.next().unwrap();
189 let arg = if let Some(arg) = args.next() {
190 arg
191 } else {
192 eprintln!("usage: {prog} <scratch directory>");
193 process::exit(1);
194 };
195
196 // Open scratch directory
197 let dir_fd = match open_scratch_directory(&arg) {
198 Ok(dir_fd) => dir_fd,
199 Err(err) => {
200 eprintln!("{err}");
201 process::exit(1)
202 }
203 };
204
205 // Run the tests.
206 unsafe { test_path_link(dir_fd) }
207 }
208