]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/wasi/fs.rs
New upstream version 1.44.1+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
402 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
48663c56 403 self.write_vectored(&[IoSlice::new(buf)])
532ac7d7
XL
404 }
405
48663c56 406 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
532ac7d7
XL
407 self.fd.write(bufs)
408 }
409
410 pub fn flush(&self) -> io::Result<()> {
411 Ok(())
412 }
413
414 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
415 self.fd.seek(pos)
416 }
417
418 pub fn duplicate(&self) -> io::Result<File> {
419 // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
420 unsupported()
421 }
422
423 pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
424 // Permissions haven't been fully figured out in wasi yet, so this is
425 // likely temporary
426 unsupported()
427 }
428
429 pub fn fd(&self) -> &WasiFd {
430 &self.fd
431 }
432
433 pub fn into_fd(self) -> WasiFd {
434 self.fd
435 }
436
437 pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
438 read_link(&self.fd, file)
439 }
440}
441
442impl FromInner<u32> for File {
443 fn from_inner(fd: u32) -> File {
60c5eb7d 444 unsafe { File { fd: WasiFd::from_raw(fd) } }
532ac7d7
XL
445 }
446}
447
448impl DirBuilder {
449 pub fn new() -> DirBuilder {
450 DirBuilder {}
451 }
452
453 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
60c5eb7d
XL
454 let (dir, file) = open_parent(p)?;
455 dir.create_directory(osstr2str(file.as_ref())?)
532ac7d7
XL
456 }
457}
458
459impl fmt::Debug for File {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60c5eb7d 461 f.debug_struct("File").field("fd", &self.fd.as_raw()).finish()
532ac7d7
XL
462 }
463}
464
465pub fn readdir(p: &Path) -> io::Result<ReadDir> {
466 let mut opts = OpenOptions::new();
467 opts.directory(true);
468 opts.read(true);
469 let dir = File::open(p, &opts)?;
470 Ok(ReadDir {
471 cookie: Some(0),
472 buf: vec![0; 128],
473 offset: 0,
474 cap: 0,
60c5eb7d 475 inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }),
532ac7d7
XL
476 })
477}
478
479pub fn unlink(p: &Path) -> io::Result<()> {
60c5eb7d
XL
480 let (dir, file) = open_parent(p)?;
481 dir.unlink_file(osstr2str(file.as_ref())?)
532ac7d7
XL
482}
483
484pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
60c5eb7d
XL
485 let (old, old_file) = open_parent(old)?;
486 let (new, new_file) = open_parent(new)?;
487 old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
532ac7d7
XL
488}
489
490pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
491 // Permissions haven't been fully figured out in wasi yet, so this is
492 // likely temporary
493 unsupported()
494}
495
496pub fn rmdir(p: &Path) -> io::Result<()> {
60c5eb7d
XL
497 let (dir, file) = open_parent(p)?;
498 dir.remove_directory(osstr2str(file.as_ref())?)
532ac7d7
XL
499}
500
501pub fn readlink(p: &Path) -> io::Result<PathBuf> {
60c5eb7d 502 let (dir, file) = open_parent(p)?;
532ac7d7
XL
503 read_link(&dir, &file)
504}
505
506fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
507 // Try to get a best effort initial capacity for the vector we're going to
508 // fill. Note that if it's not a symlink we don't use a file to avoid
509 // allocating gigabytes if you read_link a huge movie file by accident.
510 // Additionally we add 1 to the initial size so if it doesn't change until
511 // when we call `readlink` the returned length will be less than the
512 // capacity, guaranteeing that we got all the data.
513 let meta = metadata_at(fd, 0, file)?;
514 let initial_size = if meta.file_type().is_symlink() {
515 (meta.size() as usize).saturating_add(1)
516 } else {
517 1 // this'll fail in just a moment
518 };
519
520 // Now that we have an initial guess of how big to make our buffer, call
521 // `readlink` in a loop until it fails or reports it filled fewer bytes than
522 // we asked for, indicating we got everything.
60c5eb7d 523 let file = osstr2str(file.as_ref())?;
532ac7d7
XL
524 let mut destination = vec![0u8; initial_size];
525 loop {
526 let len = fd.readlink(file, &mut destination)?;
527 if len < destination.len() {
528 destination.truncate(len);
529 destination.shrink_to_fit();
530 return Ok(PathBuf::from(OsString::from_vec(destination)));
531 }
532 let amt_to_add = destination.len();
533 destination.extend(iter::repeat(0).take(amt_to_add));
534 }
535}
536
537pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
60c5eb7d
XL
538 let (dst, dst_file) = open_parent(dst)?;
539 dst.symlink(osstr2str(src.as_ref())?, osstr2str(dst_file.as_ref())?)
532ac7d7
XL
540}
541
542pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
60c5eb7d
XL
543 let (src, src_file) = open_parent(src)?;
544 let (dst, dst_file) = open_parent(dst)?;
532ac7d7 545 src.link(
60c5eb7d
XL
546 wasi::LOOKUPFLAGS_SYMLINK_FOLLOW,
547 osstr2str(src_file.as_ref())?,
532ac7d7 548 &dst,
60c5eb7d 549 osstr2str(dst_file.as_ref())?,
532ac7d7
XL
550 )
551}
552
553pub fn stat(p: &Path) -> io::Result<FileAttr> {
60c5eb7d
XL
554 let (dir, file) = open_parent(p)?;
555 metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
532ac7d7
XL
556}
557
558pub fn lstat(p: &Path) -> io::Result<FileAttr> {
60c5eb7d 559 let (dir, file) = open_parent(p)?;
532ac7d7
XL
560 metadata_at(&dir, 0, &file)
561}
562
60c5eb7d
XL
563fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
564 let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
565 Ok(FileAttr { meta })
532ac7d7
XL
566}
567
568pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
569 // This seems to not be in wasi's API yet, and we may need to end up
570 // emulating it ourselves. For now just return an error.
571 unsupported()
572}
573
574fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
575 let fd = fd.open(
576 opts.dirflags,
60c5eb7d 577 osstr2str(path.as_ref())?,
532ac7d7
XL
578 opts.oflags,
579 opts.rights_base(),
580 opts.rights_inheriting(),
581 opts.fdflags,
582 )?;
583 Ok(File { fd })
584}
585
586/// Attempts to open a bare path `p`.
587///
588/// WASI has no fundamental capability to do this. All syscalls and operations
589/// are relative to already-open file descriptors. The C library, however,
590/// manages a map of preopened file descriptors to their path, and then the C
591/// library provides an API to look at this. In other words, when you want to
592/// open a path `p`, you have to find a previously opened file descriptor in a
593/// global table and then see if `p` is relative to that file descriptor.
594///
595/// This function, if successful, will return two items:
596///
597/// * The first is a `ManuallyDrop<WasiFd>`. This represents a preopened file
598/// descriptor which we don't have ownership of, but we can use. You shouldn't
599/// actually drop the `fd`.
600///
601/// * The second is a path that should be a part of `p` and represents a
602/// relative traversal from the file descriptor specified to the desired
603/// location `p`.
604///
605/// If successful you can use the returned file descriptor to perform
606/// file-descriptor-relative operations on the path returned as well. The
607/// `rights` argument indicates what operations are desired on the returned file
608/// descriptor, and if successful the returned file descriptor should have the
609/// appropriate rights for performing `rights` actions.
610///
611/// Note that this can fail if `p` doesn't look like it can be opened relative
612/// to any preopened file descriptor.
60c5eb7d 613fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
532ac7d7
XL
614 let p = CString::new(p.as_os_str().as_bytes())?;
615 unsafe {
616 let mut ret = ptr::null();
60c5eb7d 617 let fd = __wasilibc_find_relpath(p.as_ptr(), &mut ret);
532ac7d7
XL
618 if fd == -1 {
619 let msg = format!(
620 "failed to find a preopened file descriptor \
621 through which {:?} could be opened",
622 p
623 );
624 return Err(io::Error::new(io::ErrorKind::Other, msg));
625 }
626 let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes()));
627
628 // FIXME: right now `path` is a pointer into `p`, the `CString` above.
629 // When we return `p` is deallocated and we can't use it, so we need to
630 // currently separately allocate `path`. If this becomes an issue though
631 // we should probably turn this into a closure-taking interface or take
632 // `&CString` and then pass off `&Path` tied to the same lifetime.
633 let path = path.to_path_buf();
634
635 return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path));
636 }
60c5eb7d
XL
637
638 extern "C" {
639 pub fn __wasilibc_find_relpath(
640 path: *const libc::c_char,
641 relative_path: *mut *const libc::c_char,
642 ) -> libc::c_int;
643 }
644}
645
646pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
647 f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8"))
532ac7d7 648}
74b04a01
XL
649
650pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
651 use crate::fs::File;
652
653 let mut reader = File::open(from)?;
654 let mut writer = File::create(to)?;
655
656 io::copy(&mut reader, &mut writer)
657}