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. 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 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