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