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_read_write(dir_fd: wasip1::Fd)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 
test_file_write_and_file_pos(dir_fd: wasip1::Fd)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 
main()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