]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/wasi/fs.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / library / std / src / sys / wasi / fs.rs
CommitLineData
1b1a35ee
XL
1#![deny(unsafe_op_in_unsafe_fn)]
2
532ac7d7
XL
3use crate::ffi::{CStr, CString, OsStr, OsString};
4use crate::fmt;
48663c56 5use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
532ac7d7
XL
6use crate::iter;
7use crate::mem::{self, ManuallyDrop};
8use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
9use crate::path::{Path, PathBuf};
10use crate::ptr;
11use crate::sync::Arc;
e1599b0c 12use crate::sys::fd::WasiFd;
532ac7d7
XL
13use crate::sys::time::SystemTime;
14use crate::sys::unsupported;
15use crate::sys_common::FromInner;
16
532ac7d7
XL
17pub use crate::sys_common::fs::remove_dir_all;
18
19pub struct File {
20 fd: WasiFd,
21}
22
23#[derive(Clone)]
24pub struct FileAttr {
60c5eb7d 25 meta: wasi::Filestat,
532ac7d7
XL
26}
27
28pub struct ReadDir {
29 inner: Arc<ReadDirInner>,
60c5eb7d 30 cookie: Option<wasi::Dircookie>,
532ac7d7
XL
31 buf: Vec<u8>,
32 offset: usize,
33 cap: usize,
34}
35
36struct ReadDirInner {
37 root: PathBuf,
38 dir: File,
39}
40
41pub struct DirEntry {
e1599b0c 42 meta: wasi::Dirent,
532ac7d7
XL
43 name: Vec<u8>,
44 inner: Arc<ReadDirInner>,
45}
46
47#[derive(Clone, Debug, Default)]
48pub struct OpenOptions {
49 read: bool,
50 write: bool,
3dfed10e 51 append: bool,
60c5eb7d
XL
52 dirflags: wasi::Lookupflags,
53 fdflags: wasi::Fdflags,
54 oflags: wasi::Oflags,
e1599b0c
XL
55 rights_base: Option<wasi::Rights>,
56 rights_inheriting: Option<wasi::Rights>,
532ac7d7
XL
57}
58
59#[derive(Clone, PartialEq, Eq, Debug)]
60pub struct FilePermissions {
61 readonly: bool,
62}
63
64#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
65pub struct FileType {
60c5eb7d 66 bits: wasi::Filetype,
532ac7d7
XL
67}
68
69#[derive(Debug)]
70pub struct DirBuilder {}
71
72impl FileAttr {
532ac7d7 73 pub fn size(&self) -> u64 {
60c5eb7d 74 self.meta.size
532ac7d7
XL
75 }
76
77 pub fn perm(&self) -> FilePermissions {
78 // not currently implemented in wasi yet
79 FilePermissions { readonly: false }
80 }
81
82 pub fn file_type(&self) -> FileType {
60c5eb7d 83 FileType { bits: self.meta.filetype }
532ac7d7
XL
84 }
85
86 pub fn modified(&self) -> io::Result<SystemTime> {
60c5eb7d 87 Ok(SystemTime::from_wasi_timestamp(self.meta.mtim))
532ac7d7
XL
88 }
89
90 pub fn accessed(&self) -> io::Result<SystemTime> {
60c5eb7d 91 Ok(SystemTime::from_wasi_timestamp(self.meta.atim))
532ac7d7
XL
92 }
93
94 pub fn created(&self) -> io::Result<SystemTime> {
60c5eb7d 95 Ok(SystemTime::from_wasi_timestamp(self.meta.ctim))
532ac7d7
XL
96 }
97
60c5eb7d 98 pub fn as_wasi(&self) -> &wasi::Filestat {
532ac7d7
XL
99 &self.meta
100 }
101}
102
103impl FilePermissions {
104 pub fn readonly(&self) -> bool {
105 self.readonly
106 }
107
108 pub fn set_readonly(&mut self, readonly: bool) {
109 self.readonly = readonly;
110 }
111}
112
113impl FileType {
114 pub fn is_dir(&self) -> bool {
e1599b0c 115 self.bits == wasi::FILETYPE_DIRECTORY
532ac7d7
XL
116 }
117
118 pub fn is_file(&self) -> bool {
e1599b0c 119 self.bits == wasi::FILETYPE_REGULAR_FILE
532ac7d7
XL
120 }
121
122 pub fn is_symlink(&self) -> bool {
e1599b0c 123 self.bits == wasi::FILETYPE_SYMBOLIC_LINK
532ac7d7
XL
124 }
125
60c5eb7d 126 pub fn bits(&self) -> wasi::Filetype {
532ac7d7
XL
127 self.bits
128 }
129}
130
131impl fmt::Debug for ReadDir {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.debug_struct("ReadDir").finish()
134 }
135}
136
137impl Iterator for ReadDir {
138 type Item = io::Result<DirEntry>;
139
140 fn next(&mut self) -> Option<io::Result<DirEntry>> {
141 loop {
142 // If we've reached the capacity of our buffer then we need to read
143 // some more from the OS, otherwise we pick up at our old offset.
144 let offset = if self.offset == self.cap {
145 let cookie = self.cookie.take()?;
146 match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
147 Ok(bytes) => self.cap = bytes,
148 Err(e) => return Some(Err(e)),
149 }
150 self.offset = 0;
151 self.cookie = Some(cookie);
152
153 // If we didn't actually read anything, this is in theory the
154 // end of the directory.
155 if self.cap == 0 {
156 self.cookie = None;
157 return None;
158 }
159
160 0
161 } else {
162 self.offset
163 };
164 let data = &self.buf[offset..self.cap];
165
166 // If we're not able to read a directory entry then that means it
167 // must have been truncated at the end of the buffer, so reset our
168 // offset so we can go back and reread into the buffer, picking up
169 // where we last left off.
e1599b0c 170 let dirent_size = mem::size_of::<wasi::Dirent>();
532ac7d7
XL
171 if data.len() < dirent_size {
172 assert!(self.cookie.is_some());
173 assert!(self.buf.len() >= dirent_size);
174 self.offset = self.cap;
175 continue;
176 }
177 let (dirent, data) = data.split_at(dirent_size);
60c5eb7d 178 let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
532ac7d7
XL
179
180 // If the file name was truncated, then we need to reinvoke
181 // `readdir` so we truncate our buffer to start over and reread this
182 // descriptor. Note that if our offset is 0 that means the file name
183 // is massive and we need a bigger buffer.
184 if data.len() < dirent.d_namlen as usize {
185 if offset == 0 {
186 let amt_to_add = self.buf.capacity();
187 self.buf.extend(iter::repeat(0).take(amt_to_add));
188 }
189 assert!(self.cookie.is_some());
190 self.offset = self.cap;
191 continue;
192 }
193 self.cookie = Some(dirent.d_next);
194 self.offset = offset + dirent_size + dirent.d_namlen as usize;
195
196 let name = &data[..(dirent.d_namlen as usize)];
197
198 // These names are skipped on all other platforms, so let's skip
199 // them here too
200 if name == b"." || name == b".." {
201 continue;
202 }
203
204 return Some(Ok(DirEntry {
205 meta: dirent,
206 name: name.to_vec(),
207 inner: self.inner.clone(),
208 }));
209 }
210 }
211}
212
213impl DirEntry {
214 pub fn path(&self) -> PathBuf {
215 let name = OsStr::from_bytes(&self.name);
216 self.inner.root.join(name)
217 }
218
219 pub fn file_name(&self) -> OsString {
220 OsString::from_vec(self.name.clone())
221 }
222
223 pub fn metadata(&self) -> io::Result<FileAttr> {
60c5eb7d 224 metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref())
532ac7d7
XL
225 }
226
227 pub fn file_type(&self) -> io::Result<FileType> {
60c5eb7d 228 Ok(FileType { bits: self.meta.d_type })
532ac7d7
XL
229 }
230
e1599b0c 231 pub fn ino(&self) -> wasi::Inode {
532ac7d7
XL
232 self.meta.d_ino
233 }
234}
235
236impl OpenOptions {
237 pub fn new() -> OpenOptions {
238 let mut base = OpenOptions::default();
60c5eb7d 239 base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
532ac7d7
XL
240 return base;
241 }
242
243 pub fn read(&mut self, read: bool) {
244 self.read = read;
245 }
246
247 pub fn write(&mut self, write: bool) {
248 self.write = write;
249 }
250
251 pub fn truncate(&mut self, truncate: bool) {
60c5eb7d 252 self.oflag(wasi::OFLAGS_TRUNC, truncate);
532ac7d7
XL
253 }
254
255 pub fn create(&mut self, create: bool) {
60c5eb7d 256 self.oflag(wasi::OFLAGS_CREAT, create);
532ac7d7
XL
257 }
258
259 pub fn create_new(&mut self, create_new: bool) {
60c5eb7d
XL
260 self.oflag(wasi::OFLAGS_EXCL, create_new);
261 self.oflag(wasi::OFLAGS_CREAT, create_new);
532ac7d7
XL
262 }
263
264 pub fn directory(&mut self, directory: bool) {
60c5eb7d 265 self.oflag(wasi::OFLAGS_DIRECTORY, directory);
532ac7d7
XL
266 }
267
60c5eb7d 268 fn oflag(&mut self, bit: wasi::Oflags, set: bool) {
532ac7d7
XL
269 if set {
270 self.oflags |= bit;
271 } else {
272 self.oflags &= !bit;
273 }
274 }
275
3dfed10e
XL
276 pub fn append(&mut self, append: bool) {
277 self.append = append;
278 self.fdflag(wasi::FDFLAGS_APPEND, append);
532ac7d7
XL
279 }
280
281 pub fn dsync(&mut self, set: bool) {
60c5eb7d 282 self.fdflag(wasi::FDFLAGS_DSYNC, set);
532ac7d7
XL
283 }
284
285 pub fn nonblock(&mut self, set: bool) {
60c5eb7d 286 self.fdflag(wasi::FDFLAGS_NONBLOCK, set);
532ac7d7
XL
287 }
288
289 pub fn rsync(&mut self, set: bool) {
60c5eb7d 290 self.fdflag(wasi::FDFLAGS_RSYNC, set);
532ac7d7
XL
291 }
292
293 pub fn sync(&mut self, set: bool) {
60c5eb7d 294 self.fdflag(wasi::FDFLAGS_SYNC, set);
532ac7d7
XL
295 }
296
60c5eb7d 297 fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) {
532ac7d7
XL
298 if set {
299 self.fdflags |= bit;
300 } else {
301 self.fdflags &= !bit;
302 }
303 }
304
e1599b0c 305 pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
532ac7d7
XL
306 self.rights_base = Some(rights);
307 }
308
e1599b0c 309 pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
532ac7d7
XL
310 self.rights_inheriting = Some(rights);
311 }
312
e1599b0c 313 fn rights_base(&self) -> wasi::Rights {
532ac7d7
XL
314 if let Some(rights) = self.rights_base {
315 return rights;
316 }
317
318 // If rights haven't otherwise been specified try to pick a reasonable
319 // set. This can always be overridden by users via extension traits, and
320 // implementations may give us fewer rights silently than we ask for. So
321 // given that, just look at `read` and `write` and bucket permissions
322 // based on that.
323 let mut base = 0;
324 if self.read {
60c5eb7d
XL
325 base |= wasi::RIGHTS_FD_READ;
326 base |= wasi::RIGHTS_FD_READDIR;
532ac7d7 327 }
3dfed10e 328 if self.write || self.append {
60c5eb7d
XL
329 base |= wasi::RIGHTS_FD_WRITE;
330 base |= wasi::RIGHTS_FD_DATASYNC;
331 base |= wasi::RIGHTS_FD_ALLOCATE;
332 base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE;
532ac7d7
XL
333 }
334
335 // FIXME: some of these should probably be read-only or write-only...
60c5eb7d
XL
336 base |= wasi::RIGHTS_FD_ADVISE;
337 base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS;
3dfed10e 338 base |= wasi::RIGHTS_FD_FILESTAT_GET;
60c5eb7d
XL
339 base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES;
340 base |= wasi::RIGHTS_FD_SEEK;
341 base |= wasi::RIGHTS_FD_SYNC;
342 base |= wasi::RIGHTS_FD_TELL;
343 base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY;
344 base |= wasi::RIGHTS_PATH_CREATE_FILE;
345 base |= wasi::RIGHTS_PATH_FILESTAT_GET;
346 base |= wasi::RIGHTS_PATH_LINK_SOURCE;
347 base |= wasi::RIGHTS_PATH_LINK_TARGET;
348 base |= wasi::RIGHTS_PATH_OPEN;
349 base |= wasi::RIGHTS_PATH_READLINK;
350 base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY;
351 base |= wasi::RIGHTS_PATH_RENAME_SOURCE;
352 base |= wasi::RIGHTS_PATH_RENAME_TARGET;
353 base |= wasi::RIGHTS_PATH_SYMLINK;
354 base |= wasi::RIGHTS_PATH_UNLINK_FILE;
355 base |= wasi::RIGHTS_POLL_FD_READWRITE;
532ac7d7
XL
356
357 return base;
358 }
359
e1599b0c 360 fn rights_inheriting(&self) -> wasi::Rights {
532ac7d7
XL
361 self.rights_inheriting.unwrap_or_else(|| self.rights_base())
362 }
363
60c5eb7d 364 pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) {
532ac7d7
XL
365 self.dirflags = flags;
366 }
367}
368
369impl File {
370 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
60c5eb7d 371 let (dir, file) = open_parent(path)?;
532ac7d7
XL
372 open_at(&dir, &file, opts)
373 }
374
375 pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
376 open_at(&self.fd, path, opts)
377 }
378
379 pub fn file_attr(&self) -> io::Result<FileAttr> {
e1599b0c 380 self.fd.filestat_get().map(|meta| FileAttr { meta })
532ac7d7
XL
381 }
382
60c5eb7d 383 pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
532ac7d7
XL
384 metadata_at(&self.fd, flags, path)
385 }
386
387 pub fn fsync(&self) -> io::Result<()> {
388 self.fd.sync()
389 }
390
391 pub fn datasync(&self) -> io::Result<()> {
392 self.fd.datasync()
393 }
394
395 pub fn truncate(&self, size: u64) -> io::Result<()> {
396 self.fd.filestat_set_size(size)
397 }
398
399 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
48663c56 400 self.read_vectored(&mut [IoSliceMut::new(buf)])
532ac7d7
XL
401 }
402
48663c56 403 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
532ac7d7
XL
404 self.fd.read(bufs)
405 }
406
f9f354fc
XL
407 #[inline]
408 pub fn is_read_vectored(&self) -> bool {
409 true
410 }
411
532ac7d7 412 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
48663c56 413 self.write_vectored(&[IoSlice::new(buf)])
532ac7d7
XL
414 }
415
48663c56 416 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
532ac7d7
XL
417 self.fd.write(bufs)
418 }
419
f9f354fc
XL
420 #[inline]
421 pub fn is_write_vectored(&self) -> bool {
422 true
423 }
424
532ac7d7
XL
425 pub fn flush(&self) -> io::Result<()> {
426 Ok(())
427 }
428
429 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
430 self.fd.seek(pos)
431 }
432
433 pub fn duplicate(&self) -> io::Result<File> {
434 // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
435 unsupported()
436 }
437
438 pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
439 // Permissions haven't been fully figured out in wasi yet, so this is
440 // likely temporary
441 unsupported()
442 }
443
444 pub fn fd(&self) -> &WasiFd {
445 &self.fd
446 }
447
448 pub fn into_fd(self) -> WasiFd {
449 self.fd
450 }
451
452 pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
453 read_link(&self.fd, file)
454 }
455}
456
457impl FromInner<u32> for File {
458 fn from_inner(fd: u32) -> File {
60c5eb7d 459 unsafe { File { fd: WasiFd::from_raw(fd) } }
532ac7d7
XL
460 }
461}
462
463impl DirBuilder {
464 pub fn new() -> DirBuilder {
465 DirBuilder {}
466 }
467
468 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
60c5eb7d
XL
469 let (dir, file) = open_parent(p)?;
470 dir.create_directory(osstr2str(file.as_ref())?)
532ac7d7
XL
471 }
472}
473
474impl fmt::Debug for File {
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60c5eb7d 476 f.debug_struct("File").field("fd", &self.fd.as_raw()).finish()
532ac7d7
XL
477 }
478}
479
480pub fn readdir(p: &Path) -> io::Result<ReadDir> {
481 let mut opts = OpenOptions::new();
482 opts.directory(true);
483 opts.read(true);
484 let dir = File::open(p, &opts)?;
485 Ok(ReadDir {
486 cookie: Some(0),
487 buf: vec![0; 128],
488 offset: 0,
489 cap: 0,
60c5eb7d 490 inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }),
532ac7d7
XL
491 })
492}
493
494pub fn unlink(p: &Path) -> io::Result<()> {
60c5eb7d
XL
495 let (dir, file) = open_parent(p)?;
496 dir.unlink_file(osstr2str(file.as_ref())?)
532ac7d7
XL
497}
498
499pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
60c5eb7d
XL
500 let (old, old_file) = open_parent(old)?;
501 let (new, new_file) = open_parent(new)?;
502 old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
532ac7d7
XL
503}
504
505pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
506 // Permissions haven't been fully figured out in wasi yet, so this is
507 // likely temporary
508 unsupported()
509}
510
511pub fn rmdir(p: &Path) -> io::Result<()> {
60c5eb7d
XL
512 let (dir, file) = open_parent(p)?;
513 dir.remove_directory(osstr2str(file.as_ref())?)
532ac7d7
XL
514}
515
516pub fn readlink(p: &Path) -> io::Result<PathBuf> {
60c5eb7d 517 let (dir, file) = open_parent(p)?;
532ac7d7
XL
518 read_link(&dir, &file)
519}
520
521fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
522 // Try to get a best effort initial capacity for the vector we're going to
523 // fill. Note that if it's not a symlink we don't use a file to avoid
524 // allocating gigabytes if you read_link a huge movie file by accident.
525 // Additionally we add 1 to the initial size so if it doesn't change until
526 // when we call `readlink` the returned length will be less than the
527 // capacity, guaranteeing that we got all the data.
528 let meta = metadata_at(fd, 0, file)?;
529 let initial_size = if meta.file_type().is_symlink() {
530 (meta.size() as usize).saturating_add(1)
531 } else {
532 1 // this'll fail in just a moment
533 };
534
535 // Now that we have an initial guess of how big to make our buffer, call
536 // `readlink` in a loop until it fails or reports it filled fewer bytes than
537 // we asked for, indicating we got everything.
60c5eb7d 538 let file = osstr2str(file.as_ref())?;
532ac7d7
XL
539 let mut destination = vec![0u8; initial_size];
540 loop {
541 let len = fd.readlink(file, &mut destination)?;
542 if len < destination.len() {
543 destination.truncate(len);
544 destination.shrink_to_fit();
545 return Ok(PathBuf::from(OsString::from_vec(destination)));
546 }
547 let amt_to_add = destination.len();
548 destination.extend(iter::repeat(0).take(amt_to_add));
549 }
550}
551
fc512014
XL
552pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
553 let (link, link_file) = open_parent(link)?;
554 link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?)
532ac7d7
XL
555}
556
fc512014
XL
557pub fn link(original: &Path, link: &Path) -> io::Result<()> {
558 let (original, original_file) = open_parent(original)?;
559 let (link, link_file) = open_parent(link)?;
560 original.link(
60c5eb7d 561 wasi::LOOKUPFLAGS_SYMLINK_FOLLOW,
fc512014
XL
562 osstr2str(original_file.as_ref())?,
563 &link,
564 osstr2str(link_file.as_ref())?,
532ac7d7
XL
565 )
566}
567
568pub fn stat(p: &Path) -> io::Result<FileAttr> {
60c5eb7d
XL
569 let (dir, file) = open_parent(p)?;
570 metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
532ac7d7
XL
571}
572
573pub fn lstat(p: &Path) -> io::Result<FileAttr> {
60c5eb7d 574 let (dir, file) = open_parent(p)?;
532ac7d7
XL
575 metadata_at(&dir, 0, &file)
576}
577
60c5eb7d
XL
578fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
579 let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
580 Ok(FileAttr { meta })
532ac7d7
XL
581}
582
583pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
584 // This seems to not be in wasi's API yet, and we may need to end up
585 // emulating it ourselves. For now just return an error.
586 unsupported()
587}
588
589fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
590 let fd = fd.open(
591 opts.dirflags,
60c5eb7d 592 osstr2str(path.as_ref())?,
532ac7d7
XL
593 opts.oflags,
594 opts.rights_base(),
595 opts.rights_inheriting(),
596 opts.fdflags,
597 )?;
598 Ok(File { fd })
599}
600
601/// Attempts to open a bare path `p`.
602///
603/// WASI has no fundamental capability to do this. All syscalls and operations
604/// are relative to already-open file descriptors. The C library, however,
3dfed10e 605/// manages a map of pre-opened file descriptors to their path, and then the C
532ac7d7
XL
606/// library provides an API to look at this. In other words, when you want to
607/// open a path `p`, you have to find a previously opened file descriptor in a
608/// global table and then see if `p` is relative to that file descriptor.
609///
610/// This function, if successful, will return two items:
611///
3dfed10e 612/// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
532ac7d7
XL
613/// descriptor which we don't have ownership of, but we can use. You shouldn't
614/// actually drop the `fd`.
615///
616/// * The second is a path that should be a part of `p` and represents a
617/// relative traversal from the file descriptor specified to the desired
618/// location `p`.
619///
620/// If successful you can use the returned file descriptor to perform
621/// file-descriptor-relative operations on the path returned as well. The
622/// `rights` argument indicates what operations are desired on the returned file
623/// descriptor, and if successful the returned file descriptor should have the
624/// appropriate rights for performing `rights` actions.
625///
626/// Note that this can fail if `p` doesn't look like it can be opened relative
3dfed10e 627/// to any pre-opened file descriptor.
60c5eb7d 628fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
532ac7d7
XL
629 let p = CString::new(p.as_os_str().as_bytes())?;
630 unsafe {
631 let mut ret = ptr::null();
60c5eb7d 632 let fd = __wasilibc_find_relpath(p.as_ptr(), &mut ret);
532ac7d7
XL
633 if fd == -1 {
634 let msg = format!(
3dfed10e 635 "failed to find a pre-opened file descriptor \
532ac7d7
XL
636 through which {:?} could be opened",
637 p
638 );
639 return Err(io::Error::new(io::ErrorKind::Other, msg));
640 }
641 let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes()));
642
643 // FIXME: right now `path` is a pointer into `p`, the `CString` above.
644 // When we return `p` is deallocated and we can't use it, so we need to
645 // currently separately allocate `path`. If this becomes an issue though
646 // we should probably turn this into a closure-taking interface or take
647 // `&CString` and then pass off `&Path` tied to the same lifetime.
648 let path = path.to_path_buf();
649
650 return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path));
651 }
60c5eb7d
XL
652
653 extern "C" {
654 pub fn __wasilibc_find_relpath(
655 path: *const libc::c_char,
656 relative_path: *mut *const libc::c_char,
657 ) -> libc::c_int;
658 }
659}
660
661pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
662 f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8"))
532ac7d7 663}
74b04a01
XL
664
665pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
666 use crate::fs::File;
667
668 let mut reader = File::open(from)?;
669 let mut writer = File::create(to)?;
670
671 io::copy(&mut reader, &mut writer)
672}