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