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::open_scratch_directory; 5 6 unsafe fn test_file_pread_pwrite(dir_fd: wasip1::Fd) { 7 // Create a file in the scratch directory. 8 let file_fd = wasip1::path_open( 9 dir_fd, 10 0, 11 "file", 12 wasip1::OFLAGS_CREAT, 13 wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 14 0, 15 0, 16 ) 17 .expect("opening a file"); 18 assert!( 19 file_fd > libc::STDERR_FILENO as wasip1::Fd, 20 "file descriptor range check", 21 ); 22 23 let contents = &[0u8, 1, 2, 3]; 24 let ciovec = wasip1::Ciovec { 25 buf: contents.as_ptr() as *const _, 26 buf_len: contents.len(), 27 }; 28 let mut nwritten = wasip1::fd_pwrite(file_fd, &[ciovec], 0).expect("writing bytes at offset 0"); 29 assert_eq!(nwritten, 4, "nwritten bytes check"); 30 31 let contents = &mut [0u8; 4]; 32 let iovec = wasip1::Iovec { 33 buf: contents.as_mut_ptr() as *mut _, 34 buf_len: contents.len(), 35 }; 36 let mut nread = wasip1::fd_pread(file_fd, &[iovec], 0).expect("reading bytes at offset 0"); 37 assert_eq!(nread, 4, "nread bytes check"); 38 assert_eq!(contents, &[0u8, 1, 2, 3], "written bytes equal read bytes"); 39 40 // Write all the data through multiple iovecs. 41 // 42 // Note that this needs to be done with a loop, because some 43 // platforms do not support writing multiple iovecs at once. 44 // See https://github.com/rust-lang/rust/issues/74825. 45 let contents = &[0u8, 1, 2, 3]; 46 let mut offset = 0usize; 47 loop { 48 let mut ciovecs: Vec<wasip1::Ciovec> = Vec::new(); 49 let mut remaining = contents.len() - offset; 50 if remaining > 2 { 51 ciovecs.push(wasip1::Ciovec { 52 buf: contents[offset..].as_ptr() as *const _, 53 buf_len: 2, 54 }); 55 remaining -= 2; 56 } 57 ciovecs.push(wasip1::Ciovec { 58 buf: contents[contents.len() - remaining..].as_ptr() as *const _, 59 buf_len: remaining, 60 }); 61 62 nwritten = wasip1::fd_pwrite(file_fd, ciovecs.as_slice(), offset.try_into().unwrap()) 63 .expect("writing bytes at offset 0"); 64 65 offset += nwritten; 66 if offset == contents.len() { 67 break; 68 } 69 } 70 assert_eq!(offset, 4, "nread bytes check"); 71 72 // Read all the data through multiple iovecs. 73 // 74 // Note that this needs to be done with a loop, because some 75 // platforms do not support reading multiple iovecs at once. 76 // See https://github.com/rust-lang/rust/issues/74825. 77 let contents = &mut [0u8; 4]; 78 let mut offset = 0usize; 79 loop { 80 let buffer = &mut [0u8; 4]; 81 let iovecs = &[ 82 wasip1::Iovec { 83 buf: buffer.as_mut_ptr() as *mut _, 84 buf_len: 2, 85 }, 86 wasip1::Iovec { 87 buf: buffer[2..].as_mut_ptr() as *mut _, 88 buf_len: 2, 89 }, 90 ]; 91 nread = wasip1::fd_pread(file_fd, iovecs, offset as _).expect("reading bytes at offset 0"); 92 if nread == 0 { 93 break; 94 } 95 contents[offset..offset + nread].copy_from_slice(&buffer[0..nread]); 96 offset += nread; 97 } 98 assert_eq!(offset, 4, "nread bytes check"); 99 assert_eq!(contents, &[0u8, 1, 2, 3], "file cursor was overwritten"); 100 101 let contents = &mut [0u8; 4]; 102 let iovec = wasip1::Iovec { 103 buf: contents.as_mut_ptr() as *mut _, 104 buf_len: contents.len(), 105 }; 106 nread = wasip1::fd_pread(file_fd, &[iovec], 2).expect("reading bytes at offset 2"); 107 assert_eq!(nread, 2, "nread bytes check"); 108 assert_eq!(contents, &[2u8, 3, 0, 0], "file cursor was overwritten"); 109 110 let contents = &[1u8, 0]; 111 let ciovec = wasip1::Ciovec { 112 buf: contents.as_ptr() as *const _, 113 buf_len: contents.len(), 114 }; 115 nwritten = wasip1::fd_pwrite(file_fd, &[ciovec], 2).expect("writing bytes at offset 2"); 116 assert_eq!(nwritten, 2, "nwritten bytes check"); 117 118 let contents = &mut [0u8; 4]; 119 let iovec = wasip1::Iovec { 120 buf: contents.as_mut_ptr() as *mut _, 121 buf_len: contents.len(), 122 }; 123 nread = wasip1::fd_pread(file_fd, &[iovec], 0).expect("reading bytes at offset 0"); 124 assert_eq!(nread, 4, "nread bytes check"); 125 assert_eq!(contents, &[0u8, 1, 1, 0], "file cursor was overwritten"); 126 127 wasip1::fd_close(file_fd).expect("closing a file"); 128 wasip1::path_unlink_file(dir_fd, "file").expect("removing a file"); 129 } 130 131 unsafe fn test_file_pwrite_and_file_pos(dir_fd: wasip1::Fd) { 132 let path = "file2"; 133 let file_fd = wasip1::path_open( 134 dir_fd, 135 0, 136 path, 137 wasip1::OFLAGS_CREAT, 138 wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 139 0, 140 0, 141 ) 142 .expect("opening a file"); 143 assert!( 144 file_fd > libc::STDERR_FILENO as wasip1::Fd, 145 "file descriptor range check", 146 ); 147 148 // Perform a 0-sized pwrite at an offset beyond the end of the file. Unix 149 // semantics should pop out where nothing is actually written and the size 150 // of the file isn't modified. 151 assert_eq!(wasip1::fd_tell(file_fd).unwrap(), 0); 152 let ciovec = wasip1::Ciovec { 153 buf: [].as_ptr(), 154 buf_len: 0, 155 }; 156 let n = wasip1::fd_pwrite(file_fd, &[ciovec], 50).expect("writing bytes at offset 2"); 157 assert_eq!(n, 0); 158 159 assert_eq!(wasip1::fd_tell(file_fd).unwrap(), 0); 160 let stat = wasip1::fd_filestat_get(file_fd).unwrap(); 161 assert_eq!(stat.size, 0); 162 163 // Now write a single byte and make sure it actually works 164 let buf = [0]; 165 let ciovec = wasip1::Ciovec { 166 buf: buf.as_ptr(), 167 buf_len: buf.len(), 168 }; 169 let n = wasip1::fd_pwrite(file_fd, &[ciovec], 50).expect("writing bytes at offset 50"); 170 assert_eq!(n, 1); 171 172 assert_eq!(wasip1::fd_tell(file_fd).unwrap(), 0); 173 let stat = wasip1::fd_filestat_get(file_fd).unwrap(); 174 assert_eq!(stat.size, 51); 175 176 wasip1::fd_close(file_fd).expect("closing a file"); 177 wasip1::path_unlink_file(dir_fd, path).expect("removing a file"); 178 } 179 180 unsafe fn test_file_pwrite_and_append(dir_fd: wasip1::Fd) { 181 let path = "file3"; 182 let file_fd = wasip1::path_open( 183 dir_fd, 184 0, 185 path, 186 wasip1::OFLAGS_CREAT, 187 wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 188 0, 189 wasip1::FDFLAGS_APPEND, 190 ) 191 .expect("opening a file"); 192 193 // Inherit linux semantics for `pwrite` where if the file is opened in 194 // append mode then the offset to `pwrite` is ignored. 195 let buf = [0]; 196 let ciovec = wasip1::Ciovec { 197 buf: buf.as_ptr(), 198 buf_len: buf.len(), 199 }; 200 let n = wasip1::fd_pwrite(file_fd, &[ciovec], 50).expect("writing bytes at offset 50"); 201 assert_eq!(n, 1); 202 203 let stat = wasip1::fd_filestat_get(file_fd).unwrap(); 204 assert_eq!(stat.size, 1); 205 206 let n = wasip1::fd_pwrite(file_fd, &[ciovec], 0).expect("writing bytes at offset 50"); 207 assert_eq!(n, 1); 208 209 let stat = wasip1::fd_filestat_get(file_fd).unwrap(); 210 assert_eq!(stat.size, 2); 211 212 wasip1::fd_close(file_fd).expect("closing a file"); 213 wasip1::path_unlink_file(dir_fd, path).expect("removing a file"); 214 } 215 216 fn main() { 217 let mut args = env::args(); 218 let prog = args.next().unwrap(); 219 let arg = if let Some(arg) = args.next() { 220 arg 221 } else { 222 eprintln!("usage: {prog} <scratch directory>"); 223 process::exit(1); 224 }; 225 226 // Open scratch directory 227 let dir_fd = match open_scratch_directory(&arg) { 228 Ok(dir_fd) => dir_fd, 229 Err(err) => { 230 eprintln!("{err}"); 231 process::exit(1) 232 } 233 }; 234 235 // Run the tests. 236 unsafe { 237 test_file_pread_pwrite(dir_fd); 238 test_file_pwrite_and_file_pos(dir_fd); 239 test_file_pwrite_and_append(dir_fd); 240 } 241 } 242