1#![unstable(feature = "wasi_ext", issue = "71213")]
6
7#[allow(unused_imports)]
9use io::{Read, Write};
10
11#[cfg(target_env = "p1")]
12use crate::ffi::OsStr;
13use crate::fs::{self, File, OpenOptions};
14use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
15use crate::macros::rustdoc_skip_body;
16#[cfg(target_env = "p1")]
17use crate::os::fd::AsRawFd;
18use crate::path::Path;
19#[cfg(target_env = "p1")]
20use crate::sys::err2io;
21use crate::sys::{AsInner, AsInnerMut};
22
23pub trait FileExt {
25 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
37
38 fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
50
51 fn read_buf_at(&self, buf: BorrowedCursor<'_, u8>, offset: u64) -> io::Result<()>;
57
58 fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
86 while !buf.is_empty() {
87 match self.read_at(buf, offset) {
88 Ok(0) => break,
89 Ok(n) => {
90 let tmp = buf;
91 buf = &mut tmp[n..];
92 offset += n as u64;
93 }
94 Err(ref e) if e.is_interrupted() => {}
95 Err(e) => return Err(e),
96 }
97 }
98 if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
99 }
100
101 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
116
117 fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
132
133 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
154 while !buf.is_empty() {
155 match self.write_at(buf, offset) {
156 Ok(0) => {
157 return Err(io::Error::WRITE_ALL_EOF);
158 }
159 Ok(n) => {
160 buf = &buf[n..];
161 offset += n as u64
162 }
163 Err(ref e) if e.is_interrupted() => {}
164 Err(e) => return Err(e),
165 }
166 }
167 Ok(())
168 }
169
170 #[doc(alias = "fd_fdstat_set_flags")]
174 #[cfg(target_env = "p1")]
175 fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
176
177 #[doc(alias = "fd_fdstat_set_rights")]
181 #[cfg(target_env = "p1")]
182 fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
183
184 #[doc(alias = "fd_advise")]
188 #[cfg(target_env = "p1")]
189 fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
190
191 #[doc(alias = "fd_allocate")]
195 #[cfg(target_env = "p1")]
196 fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
197
198 #[doc(alias = "path_create_directory")]
202 #[cfg(target_env = "p1")]
203 fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
204
205 #[doc(alias = "path_unlink_file")]
209 #[cfg(target_env = "p1")]
210 fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
211
212 #[doc(alias = "path_remove_directory")]
216 #[cfg(target_env = "p1")]
217 fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
218}
219
220impl FileExt for File {
228 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
229 self.as_inner().read_at(buf, offset)
230 }
231
232 fn read_buf_at(&self, buf: BorrowedCursor<'_, u8>, offset: u64) -> io::Result<()> {
233 self.as_inner().read_buf_at(buf, offset)
234 }
235
236 #[rustdoc_skip_body]
237 fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
238 self.as_inner().read_vectored_at(bufs, offset)
239 }
240
241 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
242 self.as_inner().write_at(buf, offset)
243 }
244
245 #[rustdoc_skip_body]
246 fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
247 self.as_inner().write_vectored_at(bufs, offset)
248 }
249
250 #[cfg(target_env = "p1")]
251 fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
252 unsafe {
253 wasip1::fd_fdstat_set_flags(self.as_raw_fd() as wasip1::Fd, flags).map_err(err2io)
254 }
255 }
256
257 #[cfg(target_env = "p1")]
258 fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
259 unsafe {
260 wasip1::fd_fdstat_set_rights(self.as_raw_fd() as wasip1::Fd, rights, inheriting)
261 .map_err(err2io)
262 }
263 }
264
265 #[cfg(target_env = "p1")]
266 fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
267 let advice = match advice {
268 a if a == wasip1::ADVICE_NORMAL.raw() => wasip1::ADVICE_NORMAL,
269 a if a == wasip1::ADVICE_SEQUENTIAL.raw() => wasip1::ADVICE_SEQUENTIAL,
270 a if a == wasip1::ADVICE_RANDOM.raw() => wasip1::ADVICE_RANDOM,
271 a if a == wasip1::ADVICE_WILLNEED.raw() => wasip1::ADVICE_WILLNEED,
272 a if a == wasip1::ADVICE_DONTNEED.raw() => wasip1::ADVICE_DONTNEED,
273 a if a == wasip1::ADVICE_NOREUSE.raw() => wasip1::ADVICE_NOREUSE,
274 _ => {
275 return Err(io::const_error!(
276 io::ErrorKind::InvalidInput,
277 "invalid parameter 'advice'",
278 ));
279 }
280 };
281
282 unsafe {
283 wasip1::fd_advise(self.as_raw_fd() as wasip1::Fd, offset, len, advice).map_err(err2io)
284 }
285 }
286
287 #[cfg(target_env = "p1")]
288 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
289 unsafe { wasip1::fd_allocate(self.as_raw_fd() as wasip1::Fd, offset, len).map_err(err2io) }
290 }
291
292 #[cfg(target_env = "p1")]
293 fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
294 let path = osstr2str(dir.as_ref().as_ref())?;
295 unsafe {
296 wasip1::path_create_directory(self.as_raw_fd() as wasip1::Fd, path).map_err(err2io)
297 }
298 }
299
300 #[cfg(target_env = "p1")]
301 fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
302 let path = osstr2str(path.as_ref().as_ref())?;
303 unsafe { wasip1::path_unlink_file(self.as_raw_fd() as wasip1::Fd, path).map_err(err2io) }
304 }
305
306 #[cfg(target_env = "p1")]
307 fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
308 let path = osstr2str(path.as_ref().as_ref())?;
309 unsafe {
310 wasip1::path_remove_directory(self.as_raw_fd() as wasip1::Fd, path).map_err(err2io)
311 }
312 }
313}
314
315pub trait OpenOptionsExt {
317 fn custom_flags(&mut self, flags: i32) -> &mut Self;
319}
320
321impl OpenOptionsExt for OpenOptions {
322 #[rustdoc_skip_body]
323 fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
324 self.as_inner_mut().custom_flags(flags);
325 self
326 }
327}
328
329pub trait MetadataExt {
331 fn dev(&self) -> u64;
333 fn ino(&self) -> u64;
335 fn nlink(&self) -> u64;
337}
338
339impl MetadataExt for fs::Metadata {
340 #[rustdoc_skip_body]
341 fn dev(&self) -> u64 {
342 self.as_inner().as_inner().st_dev
343 }
344 #[rustdoc_skip_body]
345 fn ino(&self) -> u64 {
346 self.as_inner().as_inner().st_ino
347 }
348 #[rustdoc_skip_body]
349 fn nlink(&self) -> u64 {
350 self.as_inner().as_inner().st_nlink
351 }
352}
353
354pub trait FileTypeExt {
359 fn is_block_device(&self) -> bool;
361 fn is_char_device(&self) -> bool;
363 fn is_socket(&self) -> bool;
365}
366
367impl FileTypeExt for fs::FileType {
368 #[rustdoc_skip_body]
369 fn is_block_device(&self) -> bool {
370 self.as_inner().is(libc::S_IFBLK)
371 }
372 #[rustdoc_skip_body]
373 fn is_char_device(&self) -> bool {
374 self.as_inner().is(libc::S_IFCHR)
375 }
376 #[rustdoc_skip_body]
377 fn is_socket(&self) -> bool {
378 self.as_inner().is(libc::S_IFSOCK)
379 }
380}
381
382pub trait DirEntryExt {
384 fn ino(&self) -> u64;
386}
387
388impl DirEntryExt for fs::DirEntry {
389 #[rustdoc_skip_body]
390 fn ino(&self) -> u64 {
391 self.as_inner().ino()
392 }
393}
394
395#[doc(alias = "path_link")]
399#[cfg(target_env = "p1")]
400pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
401 old_fd: &File,
402 old_flags: u32,
403 old_path: P,
404 new_fd: &File,
405 new_path: U,
406) -> io::Result<()> {
407 unsafe {
408 wasip1::path_link(
409 old_fd.as_raw_fd() as wasip1::Fd,
410 old_flags,
411 osstr2str(old_path.as_ref().as_ref())?,
412 new_fd.as_raw_fd() as wasip1::Fd,
413 osstr2str(new_path.as_ref().as_ref())?,
414 )
415 .map_err(err2io)
416 }
417}
418
419#[doc(alias = "path_rename")]
423#[cfg(target_env = "p1")]
424pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
425 old_fd: &File,
426 old_path: P,
427 new_fd: &File,
428 new_path: U,
429) -> io::Result<()> {
430 unsafe {
431 wasip1::path_rename(
432 old_fd.as_raw_fd() as wasip1::Fd,
433 osstr2str(old_path.as_ref().as_ref())?,
434 new_fd.as_raw_fd() as wasip1::Fd,
435 osstr2str(new_path.as_ref().as_ref())?,
436 )
437 .map_err(err2io)
438 }
439}
440
441#[doc(alias = "path_symlink")]
445#[cfg(target_env = "p1")]
446pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
447 old_path: P,
448 fd: &File,
449 new_path: U,
450) -> io::Result<()> {
451 unsafe {
452 wasip1::path_symlink(
453 osstr2str(old_path.as_ref().as_ref())?,
454 fd.as_raw_fd() as wasip1::Fd,
455 osstr2str(new_path.as_ref().as_ref())?,
456 )
457 .map_err(err2io)
458 }
459}
460
461#[cfg_attr(not(test), rustc_diagnostic_item = "fs_symlink_path")]
466pub fn symlink_path<P: AsRef<Path>, U: AsRef<Path>>(old_path: P, new_path: U) -> io::Result<()> {
467 crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref())
468}
469
470#[cfg(target_env = "p1")]
471fn osstr2str(f: &OsStr) -> io::Result<&str> {
472 f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
473}