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