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