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
test_file_pread_pwrite(dir_fd: wasip1::Fd)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
test_file_pwrite_and_file_pos(dir_fd: wasip1::Fd)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
test_file_pwrite_and_append(dir_fd: wasip1::Fd)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
main()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