1use crate::{
2 DirEntry, FileType, FsError, Metadata, OpenOptions, OpenOptionsConfig, ReadDir, Result,
3 VirtualFile,
4};
5use bytes::{Buf, Bytes};
6use futures::future::BoxFuture;
7use std::convert::TryInto;
8use std::fs;
9use std::io::{self, Seek};
10use std::path::{Component, Path, PathBuf};
11use std::pin::Pin;
12use std::sync::Arc;
13use std::task::{Context, Poll};
14use std::time::{SystemTime, UNIX_EPOCH};
15use tokio::fs as tfs;
16use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
17use tokio::runtime::Handle;
18
19#[derive(Debug, Clone)]
20pub struct FileSystem {
21 handle: Handle,
22 root: PathBuf,
23}
24
25#[allow(dead_code)]
26fn default_handle() -> Handle {
27 Handle::current()
28}
29
30pub fn canonicalize(path: &Path) -> Result<PathBuf> {
31 if !path.exists() {
32 return Err(FsError::InvalidInput);
33 }
34 dunce::canonicalize(path).map_err(Into::into)
35}
36
37pub fn normalize_path(path: &Path) -> PathBuf {
40 let mut components = path.components().peekable();
41 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
42 components.next();
43 PathBuf::from(c.as_os_str())
44 } else {
45 PathBuf::new()
46 };
47
48 for component in components {
49 match component {
50 Component::Prefix(..) => unreachable!(),
51 Component::RootDir => {
52 ret.push(component.as_os_str());
53 }
54 Component::CurDir => {}
55 Component::ParentDir => {
56 ret.pop();
57 }
58 Component::Normal(c) => {
59 ret.push(c);
60 }
61 }
62 }
63 ret
64}
65
66fn path_suffix_to_guest_absolute(stripped: &Path) -> PathBuf {
67 let mut stripped = stripped.to_string_lossy().into_owned();
68 if std::path::MAIN_SEPARATOR == '\\' {
69 stripped = stripped.replace('\\', "/");
70 }
71
72 PathBuf::from(format!("/{}", stripped.trim_start_matches('/')))
73}
74
75fn strip_host_root(root: &Path, target: &Path) -> Option<PathBuf> {
76 target
77 .strip_prefix(root)
78 .ok()
79 .map(path_suffix_to_guest_absolute)
80}
81
82fn host_root_relative_target(root: &Path, target: PathBuf) -> PathBuf {
83 if root == Path::new("/") || !target.is_absolute() {
84 return target;
85 }
86
87 if let Some(target) = strip_host_root(root, &target) {
88 return target;
89 }
90
91 if let Ok(canonical_target) = canonicalize(&target)
92 && let Some(target) = strip_host_root(root, &canonical_target)
93 {
94 return target;
95 }
96
97 target
98}
99
100impl FileSystem {
101 pub fn new(handle: Handle, root: impl Into<PathBuf>) -> Result<Self> {
102 let root = canonicalize(&root.into())?;
103
104 Ok(FileSystem { handle, root })
105 }
106
107 fn prepare_path(&self, path: &Path) -> Result<PathBuf> {
108 let path = normalize_path(path);
109
110 if matches!(path.components().next(), Some(Component::Prefix(..))) {
111 return Err(FsError::InvalidInput);
112 }
113
114 if self.root != Path::new("/") && path.starts_with(&self.root) {
115 return Err(FsError::InvalidInput);
116 }
117
118 let path = path.strip_prefix("/").unwrap_or(&path);
119 let path = self.root.join(path);
120
121 debug_assert!(path.starts_with(&self.root));
122 Ok(path)
123 }
124}
125
126impl crate::FileSystem for FileSystem {
127 fn readlink(&self, path: &Path) -> Result<PathBuf> {
128 let path = self.prepare_path(path)?;
129
130 let target = fs::read_link(path)?;
131 Ok(host_root_relative_target(&self.root, target))
132 }
133
134 fn read_dir(&self, path: &Path) -> Result<ReadDir> {
135 let path = self.prepare_path(path)?;
136
137 let read_dir = fs::read_dir(path)?;
138 let mut data = read_dir
139 .map(|entry| {
140 let entry = entry?;
141
142 let path = entry
143 .path()
144 .strip_prefix(&self.root)
145 .map_err(|_| FsError::InvalidData)?
146 .to_owned();
147 let path = Path::new("/").join(path);
148
149 let metadata = fs::symlink_metadata(entry.path())?;
150
151 Ok(DirEntry {
152 path,
153 metadata: Ok(metadata.try_into()?),
154 })
155 })
156 .collect::<std::result::Result<Vec<DirEntry>, io::Error>>()
157 .map_err::<FsError, _>(Into::into)?;
158 data.sort_by(|a, b| a.path.file_name().cmp(&b.path.file_name()));
159 Ok(ReadDir::new(data))
160 }
161
162 fn create_dir(&self, path: &Path) -> Result<()> {
163 let path = self.prepare_path(path)?;
164
165 if path.parent().is_none() {
166 return Err(FsError::BaseNotDirectory);
167 }
168
169 fs::create_dir(path).map_err(Into::into)
170 }
171
172 fn remove_dir(&self, path: &Path) -> Result<()> {
173 let path = self.prepare_path(path)?;
174
175 if path.parent().is_none() {
176 return Err(FsError::BaseNotDirectory);
177 }
178
179 if path.is_dir()
182 && fs::read_dir(&path)
183 .map(|mut s| s.next().is_some())
184 .unwrap_or(false)
185 {
186 return Err(FsError::DirectoryNotEmpty);
187 }
188 fs::remove_dir(path).map_err(Into::into)
189 }
190
191 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
192 Box::pin(async move {
193 use filetime::{FileTime, set_file_mtime};
194 let norm_from = normalize_path(from);
195 let norm_to = normalize_path(to);
196
197 if norm_from.parent().is_none() {
198 return Err(FsError::BaseNotDirectory);
199 }
200 if norm_to.parent().is_none() {
201 return Err(FsError::BaseNotDirectory);
202 }
203
204 let from = self.prepare_path(from)?;
205 let to = self.prepare_path(to)?;
206
207 if !from.exists() {
208 return Err(FsError::EntryNotFound);
209 }
210 let from_parent = from.parent().unwrap();
211 let to_parent = to.parent().unwrap();
212 if !from_parent.exists() {
213 return Err(FsError::EntryNotFound);
214 }
215 if !to_parent.exists() {
216 return Err(FsError::EntryNotFound);
217 }
218 let result = if from_parent != to_parent {
219 let _ = std::fs::create_dir_all(to_parent);
220 if from.is_dir() {
221 fs_extra::move_items(
222 &[&from],
223 &to,
224 &fs_extra::dir::CopyOptions {
225 copy_inside: true,
226 ..Default::default()
227 },
228 )
229 .map(|_| ())
230 .map_err(|_| FsError::UnknownError)?;
231 let _ = fs_extra::remove_items(&[&from]);
232 Ok(())
233 } else {
234 fs::copy(&from, &to).map(|_| ()).map_err(FsError::from)?;
235 fs::remove_file(&from).map(|_| ()).map_err(Into::into)
236 }
237 } else {
238 fs::rename(&from, &to).map_err(Into::into)
239 };
240 let _ = set_file_mtime(&to, FileTime::now()).map(|_| ());
241 result
242 })
243 }
244
245 fn remove_file(&self, path: &Path) -> Result<()> {
246 let path = self.prepare_path(path)?;
247
248 if path.parent().is_none() {
249 return Err(FsError::BaseNotDirectory);
250 }
251
252 fs::remove_file(path).map_err(Into::into)
253 }
254
255 fn new_open_options(&self) -> OpenOptions<'_> {
256 OpenOptions::new(self)
257 }
258
259 fn metadata(&self, path: &Path) -> Result<Metadata> {
260 let path = self.prepare_path(path)?;
261
262 fs::metadata(path)
263 .and_then(TryInto::try_into)
264 .map_err(Into::into)
265 }
266
267 fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
268 let path = self.prepare_path(path)?;
269
270 fs::symlink_metadata(path)
271 .and_then(TryInto::try_into)
272 .map_err(Into::into)
273 }
274}
275
276impl TryInto<Metadata> for std::fs::Metadata {
277 type Error = io::Error;
278
279 fn try_into(self) -> std::result::Result<Metadata, Self::Error> {
280 let filetype = self.file_type();
281 let (char_device, block_device, socket, fifo) = {
282 #[cfg(unix)]
283 {
284 use std::os::unix::fs::FileTypeExt;
285 (
286 filetype.is_char_device(),
287 filetype.is_block_device(),
288 filetype.is_socket(),
289 filetype.is_fifo(),
290 )
291 }
292 #[cfg(not(unix))]
293 {
294 (false, false, false, false)
295 }
296 };
297
298 Ok(Metadata {
299 ft: FileType {
300 dir: filetype.is_dir(),
301 file: filetype.is_file(),
302 symlink: filetype.is_symlink(),
303 char_device,
304 block_device,
305 socket,
306 fifo,
307 },
308 accessed: self
309 .accessed()
310 .and_then(|time| time.duration_since(UNIX_EPOCH).map_err(io::Error::other))
311 .map_or(0, |time| time.as_nanos() as u64),
312 created: self
313 .created()
314 .and_then(|time| time.duration_since(UNIX_EPOCH).map_err(io::Error::other))
315 .map_or(0, |time| time.as_nanos() as u64),
316 modified: self
317 .modified()
318 .and_then(|time| time.duration_since(UNIX_EPOCH).map_err(io::Error::other))
319 .map_or(0, |time| time.as_nanos() as u64),
320 len: self.len(),
321 })
322 }
323}
324
325impl crate::FileOpener for FileSystem {
326 fn open(
327 &self,
328 path: &Path,
329 conf: &OpenOptionsConfig,
330 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
331 let path = self.prepare_path(path)?;
332
333 let read = conf.read();
335 let write = conf.write();
336
337 let append = if conf.truncate { false } else { conf.append() };
343
344 let mut oo = fs::OpenOptions::new();
345 oo.read(conf.read())
346 .write(conf.write())
347 .create_new(conf.create_new())
348 .create(conf.create())
349 .append(append)
350 .truncate(conf.truncate())
351 .open(&path)
352 .map_err(Into::into)
353 .map(|file| {
354 Box::new(File::new(
355 self.handle.clone(),
356 file,
357 path.to_owned(),
358 read,
359 write,
360 append,
361 )) as Box<dyn VirtualFile + Send + Sync + 'static>
362 })
363 }
364}
365
366#[derive(Debug)]
368pub struct File {
369 handle: Handle,
370 inner: tfs::File,
371 inner_std: fs::File,
372 pub host_path: PathBuf,
373}
374
375impl File {
376 const READ: u16 = 1;
377 const WRITE: u16 = 2;
378 const APPEND: u16 = 4;
379
380 pub fn new(
382 handle: Handle,
383 file: fs::File,
384 host_path: PathBuf,
385 read: bool,
386 write: bool,
387 append: bool,
388 ) -> Self {
389 let mut _flags = 0;
390
391 if read {
392 _flags |= Self::READ;
393 }
394
395 if write {
396 _flags |= Self::WRITE;
397 }
398
399 if append {
400 _flags |= Self::APPEND;
401 }
402
403 let async_file = tfs::File::from_std(file.try_clone().unwrap());
404 Self {
405 handle,
406 inner_std: file,
407 inner: async_file,
408 host_path,
409 }
410 }
411
412 fn metadata(&self) -> std::fs::Metadata {
413 self.inner_std.metadata().unwrap()
415 }
416}
417
418#[async_trait::async_trait]
419impl VirtualFile for File {
420 fn last_accessed(&self) -> u64 {
421 self.metadata()
422 .accessed()
423 .ok()
424 .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
425 .map(|ct| ct.as_nanos() as u64)
426 .unwrap_or(0)
427 }
428
429 fn last_modified(&self) -> u64 {
430 self.metadata()
431 .modified()
432 .ok()
433 .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
434 .map(|ct| ct.as_nanos() as u64)
435 .unwrap_or(0)
436 }
437
438 fn created_time(&self) -> u64 {
439 self.metadata()
440 .created()
441 .ok()
442 .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
443 .map(|ct| ct.as_nanos() as u64)
444 .unwrap_or(0)
445 }
446
447 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
448 let atime = atime.map(|t| filetime::FileTime::from_unix_time(t as i64, 0));
449 let mtime = mtime.map(|t| filetime::FileTime::from_unix_time(t as i64, 0));
450
451 filetime::set_file_handle_times(&self.inner_std, atime, mtime)
452 .map_err(|_| crate::FsError::IOError)
453 }
454
455 fn size(&self) -> u64 {
456 self.metadata().len()
457 }
458
459 fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
460 fs::File::set_len(&self.inner_std, new_size).map_err(Into::into)
461 }
462
463 fn unlink(&mut self) -> Result<()> {
464 fs::remove_file(&self.host_path).map_err(Into::into)
465 }
466
467 fn get_special_fd(&self) -> Option<u32> {
468 None
469 }
470
471 fn poll_read_ready(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
472 let cursor = match self.inner_std.stream_position() {
473 Ok(a) => a,
474 Err(err) => return Poll::Ready(Err(err)),
475 };
476 let end = match self.inner_std.seek(io::SeekFrom::End(0)) {
477 Ok(a) => a,
478 Err(err) => return Poll::Ready(Err(err)),
479 };
480 let _ = self.inner_std.seek(io::SeekFrom::Start(cursor));
481
482 let remaining = end - cursor;
483 Poll::Ready(Ok(remaining as usize))
484 }
485
486 fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
487 Poll::Ready(Ok(8192))
488 }
489}
490
491impl AsyncRead for File {
492 fn poll_read(
493 mut self: Pin<&mut Self>,
494 cx: &mut Context<'_>,
495 buf: &mut tokio::io::ReadBuf<'_>,
496 ) -> Poll<io::Result<()>> {
497 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
498 let inner = Pin::new(&mut self.inner);
499 inner.poll_read(cx, buf)
500 }
501}
502
503impl AsyncWrite for File {
504 fn poll_write(
505 mut self: Pin<&mut Self>,
506 cx: &mut Context<'_>,
507 buf: &[u8],
508 ) -> Poll<io::Result<usize>> {
509 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
510 let inner = Pin::new(&mut self.inner);
511 inner.poll_write(cx, buf)
512 }
513
514 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
515 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
516 let inner = Pin::new(&mut self.inner);
517 inner.poll_flush(cx)
518 }
519
520 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
521 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
522 let inner = Pin::new(&mut self.inner);
523 inner.poll_shutdown(cx)
524 }
525
526 fn poll_write_vectored(
527 mut self: Pin<&mut Self>,
528 cx: &mut Context<'_>,
529 bufs: &[io::IoSlice<'_>],
530 ) -> Poll<io::Result<usize>> {
531 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
532 let inner = Pin::new(&mut self.inner);
533 inner.poll_write_vectored(cx, bufs)
534 }
535
536 fn is_write_vectored(&self) -> bool {
537 self.inner.is_write_vectored()
538 }
539}
540
541impl AsyncSeek for File {
542 fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
543 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
544 let inner = Pin::new(&mut self.inner);
545 inner.start_seek(position)
546 }
547
548 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
549 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
550 let inner = Pin::new(&mut self.inner);
551 inner.poll_complete(cx)
552 }
553}
554
555impl Drop for File {
556 fn drop(&mut self) {
557 tracing::trace!(?self.host_path, "Closing host file");
558 }
559}
560
561#[derive(Debug)]
563pub struct Stdout {
564 handle: Handle,
565 inner: tokio::io::Stdout,
566}
567#[allow(dead_code)]
568fn default_stdout() -> tokio::io::Stdout {
569 tokio::io::stdout()
570}
571impl Default for Stdout {
572 fn default() -> Self {
573 Self {
574 handle: Handle::current(),
575 inner: tokio::io::stdout(),
576 }
577 }
578}
579
580const DEFAULT_BUF_SIZE_HINT: usize = 8 * 1024;
588
589#[async_trait::async_trait]
590impl VirtualFile for Stdout {
591 fn last_accessed(&self) -> u64 {
592 0
593 }
594
595 fn last_modified(&self) -> u64 {
596 0
597 }
598
599 fn created_time(&self) -> u64 {
600 0
601 }
602
603 fn size(&self) -> u64 {
604 0
605 }
606
607 fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
608 Ok(())
609 }
610
611 fn unlink(&mut self) -> Result<()> {
612 Ok(())
613 }
614
615 fn get_special_fd(&self) -> Option<u32> {
616 Some(1)
617 }
618
619 fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
620 Poll::Ready(Ok(0))
621 }
622
623 fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
624 Poll::Ready(Ok(DEFAULT_BUF_SIZE_HINT))
625 }
626}
627
628impl AsyncRead for Stdout {
629 fn poll_read(
630 self: Pin<&mut Self>,
631 _cx: &mut Context<'_>,
632 _buf: &mut tokio::io::ReadBuf<'_>,
633 ) -> Poll<io::Result<()>> {
634 Poll::Ready(Err(io::Error::other("can not read from stdout")))
635 }
636}
637
638impl AsyncWrite for Stdout {
639 fn poll_write(
640 mut self: Pin<&mut Self>,
641 cx: &mut Context<'_>,
642 buf: &[u8],
643 ) -> Poll<io::Result<usize>> {
644 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
645 let inner = Pin::new(&mut self.inner);
646 inner.poll_write(cx, buf)
647 }
648
649 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
650 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
651 let inner = Pin::new(&mut self.inner);
652 inner.poll_flush(cx)
653 }
654
655 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
656 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
657 let inner = Pin::new(&mut self.inner);
658 inner.poll_shutdown(cx)
659 }
660
661 fn poll_write_vectored(
662 mut self: Pin<&mut Self>,
663 cx: &mut Context<'_>,
664 bufs: &[io::IoSlice<'_>],
665 ) -> Poll<io::Result<usize>> {
666 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
667 let inner = Pin::new(&mut self.inner);
668 inner.poll_write_vectored(cx, bufs)
669 }
670
671 fn is_write_vectored(&self) -> bool {
672 self.inner.is_write_vectored()
673 }
674}
675
676impl AsyncSeek for Stdout {
677 fn start_seek(self: Pin<&mut Self>, _position: io::SeekFrom) -> io::Result<()> {
678 Err(io::Error::other("can not seek stdout"))
679 }
680
681 fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
682 Poll::Ready(Err(io::Error::other("can not seek stdout")))
683 }
684}
685
686#[derive(Debug)]
688pub struct Stderr {
689 handle: Handle,
690 inner: tokio::io::Stderr,
691}
692#[allow(dead_code)]
693fn default_stderr() -> tokio::io::Stderr {
694 tokio::io::stderr()
695}
696impl Default for Stderr {
697 fn default() -> Self {
698 Self {
699 handle: Handle::current(),
700 inner: tokio::io::stderr(),
701 }
702 }
703}
704
705impl AsyncRead for Stderr {
706 fn poll_read(
707 self: Pin<&mut Self>,
708 _cx: &mut Context<'_>,
709 _buf: &mut tokio::io::ReadBuf<'_>,
710 ) -> Poll<io::Result<()>> {
711 Poll::Ready(Err(io::Error::other("can not read from stderr")))
712 }
713}
714
715impl AsyncWrite for Stderr {
716 fn poll_write(
717 mut self: Pin<&mut Self>,
718 cx: &mut Context<'_>,
719 buf: &[u8],
720 ) -> Poll<io::Result<usize>> {
721 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
722 let inner = Pin::new(&mut self.inner);
723 inner.poll_write(cx, buf)
724 }
725
726 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
727 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
728 let inner = Pin::new(&mut self.inner);
729 inner.poll_flush(cx)
730 }
731
732 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
733 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
734 let inner = Pin::new(&mut self.inner);
735 inner.poll_shutdown(cx)
736 }
737
738 fn poll_write_vectored(
739 mut self: Pin<&mut Self>,
740 cx: &mut Context<'_>,
741 bufs: &[io::IoSlice<'_>],
742 ) -> Poll<io::Result<usize>> {
743 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
744 let inner = Pin::new(&mut self.inner);
745 inner.poll_write_vectored(cx, bufs)
746 }
747
748 fn is_write_vectored(&self) -> bool {
749 self.inner.is_write_vectored()
750 }
751}
752
753impl AsyncSeek for Stderr {
754 fn start_seek(self: Pin<&mut Self>, _position: io::SeekFrom) -> io::Result<()> {
755 Err(io::Error::other("can not seek stderr"))
756 }
757
758 fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
759 Poll::Ready(Err(io::Error::other("can not seek stderr")))
760 }
761}
762
763#[async_trait::async_trait]
764impl VirtualFile for Stderr {
765 fn last_accessed(&self) -> u64 {
766 0
767 }
768
769 fn last_modified(&self) -> u64 {
770 0
771 }
772
773 fn created_time(&self) -> u64 {
774 0
775 }
776
777 fn size(&self) -> u64 {
778 0
779 }
780
781 fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
782 Ok(())
783 }
784
785 fn unlink(&mut self) -> Result<()> {
786 Ok(())
787 }
788
789 fn get_special_fd(&self) -> Option<u32> {
790 Some(2)
791 }
792
793 fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
794 Poll::Ready(Ok(0))
795 }
796
797 fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
798 Poll::Ready(Ok(8192))
799 }
800}
801
802#[derive(Debug)]
804pub struct Stdin {
805 read_buffer: Arc<std::sync::Mutex<Option<Bytes>>>,
806 handle: Handle,
807 inner: tokio::io::Stdin,
808}
809#[allow(dead_code)]
810fn default_stdin() -> tokio::io::Stdin {
811 tokio::io::stdin()
812}
813impl Default for Stdin {
814 fn default() -> Self {
815 Self {
816 handle: Handle::current(),
817 read_buffer: Arc::new(std::sync::Mutex::new(None)),
818 inner: tokio::io::stdin(),
819 }
820 }
821}
822
823impl AsyncRead for Stdin {
824 fn poll_read(
825 mut self: Pin<&mut Self>,
826 cx: &mut Context<'_>,
827 buf: &mut tokio::io::ReadBuf<'_>,
828 ) -> Poll<io::Result<()>> {
829 let max_size = buf.remaining();
830 {
831 let mut read_buffer = self.read_buffer.lock().unwrap();
832 if let Some(read_buffer) = read_buffer.as_mut() {
833 let buf_len = read_buffer.len();
834 if buf_len > 0 {
835 let read = buf_len.min(max_size);
836 buf.put_slice(&read_buffer[..read]);
837 read_buffer.advance(read);
838 return Poll::Ready(Ok(()));
839 }
840 }
841 }
842
843 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
844 let inner = Pin::new(&mut self.inner);
845 inner.poll_read(cx, buf)
846 }
847}
848
849impl AsyncWrite for Stdin {
850 fn poll_write(
851 self: Pin<&mut Self>,
852 _cx: &mut Context<'_>,
853 _buf: &[u8],
854 ) -> Poll<io::Result<usize>> {
855 Poll::Ready(Err(io::Error::other("can not wrote to stdin")))
856 }
857
858 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
859 Poll::Ready(Err(io::Error::other("can not flush stdin")))
860 }
861
862 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
863 Poll::Ready(Err(io::Error::other("can not wrote to stdin")))
864 }
865
866 fn poll_write_vectored(
867 self: Pin<&mut Self>,
868 _cx: &mut Context<'_>,
869 _bufs: &[io::IoSlice<'_>],
870 ) -> Poll<io::Result<usize>> {
871 Poll::Ready(Err(io::Error::other("can not wrote to stdin")))
872 }
873}
874
875impl AsyncSeek for Stdin {
876 fn start_seek(self: Pin<&mut Self>, _position: io::SeekFrom) -> io::Result<()> {
877 Err(io::Error::other("can not seek stdin"))
878 }
879
880 fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
881 Poll::Ready(Err(io::Error::other("can not seek stdin")))
882 }
883}
884
885#[async_trait::async_trait]
886impl VirtualFile for Stdin {
887 fn last_accessed(&self) -> u64 {
888 0
889 }
890 fn last_modified(&self) -> u64 {
891 0
892 }
893 fn created_time(&self) -> u64 {
894 0
895 }
896 fn size(&self) -> u64 {
897 0
898 }
899 fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
900 Ok(())
901 }
902 fn unlink(&mut self) -> Result<()> {
903 Ok(())
904 }
905 fn get_special_fd(&self) -> Option<u32> {
906 Some(0)
907 }
908 fn poll_read_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
909 {
910 let read_buffer = self.read_buffer.lock().unwrap();
911 if let Some(read_buffer) = read_buffer.as_ref() {
912 let buf_len = read_buffer.len();
913 if buf_len > 0 {
914 return Poll::Ready(Ok(buf_len));
915 }
916 }
917 }
918
919 let _guard = Handle::try_current().map_err(|_| self.handle.enter());
920 let inner = Pin::new(&mut self.inner);
921
922 let mut buf = [0u8; 8192];
923 let mut read_buf = ReadBuf::new(&mut buf[..]);
924 match inner.poll_read(cx, &mut read_buf) {
925 Poll::Pending => Poll::Pending,
926 Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
927 Poll::Ready(Ok(())) => {
928 let buf = read_buf.filled();
929 let buf_len = buf.len();
930
931 let mut read_buffer = self.read_buffer.lock().unwrap();
932 read_buffer.replace(Bytes::from(buf.to_vec()));
933 Poll::Ready(Ok(buf_len))
934 }
935 }
936 }
937 fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
938 Poll::Ready(Ok(0))
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use tempfile::TempDir;
945 use tokio::runtime::Handle;
946
947 use super::FileSystem;
948 use crate::FileSystem as FileSystemTrait;
949 use crate::FsError;
950 use std::path::Path;
951
952 #[tokio::test]
953 async fn test_new_filesystem() {
954 let temp = TempDir::new().unwrap();
955 std::fs::write(temp.path().join("foo2.txt"), b"").unwrap();
956
957 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
958 assert!(
959 fs.read_dir(Path::new("/")).is_ok(),
960 "NativeFS can read root"
961 );
962 assert!(
963 fs.new_open_options()
964 .read(true)
965 .open(Path::new("/foo2.txt"))
966 .is_ok(),
967 "created foo2.txt"
968 );
969 }
970
971 #[tokio::test]
972 async fn test_create_dir() {
973 let temp: TempDir = TempDir::new().unwrap();
974 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
975
976 assert_eq!(
977 fs.create_dir(Path::new("../")),
978 Err(FsError::AlreadyExists),
979 "creating a directory out of bounds",
980 );
981
982 assert_eq!(
983 fs.create_dir(Path::new("/foo")),
984 Ok(()),
985 "creating a directory",
986 );
987
988 assert!(
989 temp.path().join("foo").exists(),
990 "foo dir exists in host_fs"
991 );
992
993 let cur_dir = read_dir_names(&fs, "/");
994
995 if !cur_dir.contains(&"foo".to_string()) {
996 panic!("cur_dir does not contain foo: {cur_dir:#?}");
997 }
998
999 assert!(
1000 cur_dir.contains(&"foo".to_string()),
1001 "the root is updated and well-defined"
1002 );
1003
1004 assert_eq!(
1005 fs.create_dir(Path::new("foo/bar")),
1006 Ok(()),
1007 "creating a sub-directory",
1008 );
1009
1010 assert!(
1011 temp.path().join("foo").join("bar").exists(),
1012 "foo dir exists in host_fs"
1013 );
1014
1015 let foo_dir = read_dir_names(&fs, Path::new("/foo"));
1016
1017 assert!(
1018 foo_dir.contains(&"bar".to_string()),
1019 "the foo directory is updated and well-defined"
1020 );
1021
1022 let bar_dir = read_dir_names(&fs, Path::new("/foo/bar"));
1023
1024 assert!(
1025 bar_dir.is_empty(),
1026 "the foo directory is updated and well-defined"
1027 );
1028 }
1029
1030 #[tokio::test]
1031 async fn test_remove_dir() {
1032 let temp: TempDir = TempDir::new().unwrap();
1033 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
1034
1035 assert_eq!(
1036 fs.remove_dir(Path::new("/foo")),
1037 Err(FsError::EntryNotFound),
1038 "cannot remove a directory that doesn't exist",
1039 );
1040
1041 assert_eq!(
1042 fs.create_dir(Path::new("foo")),
1043 Ok(()),
1044 "creating a directory",
1045 );
1046
1047 assert_eq!(
1048 fs.create_dir(Path::new("foo/bar")),
1049 Ok(()),
1050 "creating a sub-directory",
1051 );
1052
1053 assert!(temp.path().join("foo/bar").exists(), "./foo/bar exists");
1054
1055 assert_eq!(
1056 fs.remove_dir(Path::new("foo")),
1057 Err(FsError::DirectoryNotEmpty),
1058 "removing a directory that has children",
1059 );
1060
1061 assert_eq!(
1062 fs.remove_dir(Path::new("foo/bar")),
1063 Ok(()),
1064 "removing a sub-directory",
1065 );
1066
1067 assert_eq!(
1068 fs.remove_dir(Path::new("foo")),
1069 Ok(()),
1070 "removing a directory",
1071 );
1072
1073 let cur_dir = read_dir_names(&fs, "/");
1074
1075 assert!(
1076 !cur_dir.contains(&"foo".to_string()),
1077 "the foo directory still exists"
1078 );
1079 }
1080
1081 fn read_dir_names(fs: &FileSystem, path: impl AsRef<Path>) -> Vec<String> {
1082 fs.read_dir(path.as_ref())
1083 .unwrap()
1084 .filter_map(|entry| Some(entry.ok()?.file_name().to_str()?.to_string()))
1085 .collect::<Vec<_>>()
1086 }
1087
1088 #[tokio::test]
1089 async fn test_rename() {
1090 let temp: TempDir = TempDir::new().unwrap();
1091 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
1092 std::fs::create_dir_all(temp.path().join("foo").join("qux")).unwrap();
1093 let foo = Path::new("foo");
1094 let bar = Path::new("bar");
1095 let foo_realpath = temp.path().join(foo);
1096 let bar_realpath = temp.path().join(bar);
1097
1098 assert_eq!(
1099 fs.rename(Path::new("/"), Path::new("/bar")).await,
1100 Err(FsError::BaseNotDirectory),
1101 "renaming a directory that has no parent",
1102 );
1103 assert_eq!(
1104 fs.rename(Path::new("/foo"), Path::new("/")).await,
1105 Err(FsError::BaseNotDirectory),
1106 "renaming to a directory that has no parent",
1107 );
1108
1109 assert_eq!(
1110 fs.rename(foo, &foo.join("bar").join("baz"),).await,
1111 Err(FsError::EntryNotFound),
1112 "renaming to a directory that has parent that doesn't exist",
1113 );
1114
1115 #[cfg(not(target_os = "windows"))]
1117 assert_eq!(fs.create_dir(bar), Ok(()));
1118
1119 assert_eq!(
1120 fs.rename(foo, bar).await,
1121 Ok(()),
1122 "renaming to a directory that has parent that exists",
1123 );
1124
1125 assert!(
1126 fs.new_open_options()
1127 .write(true)
1128 .create_new(true)
1129 .open(bar.join("hello1.txt"))
1130 .is_ok(),
1131 "creating a new file (`hello1.txt`)",
1132 );
1133 assert!(
1134 fs.new_open_options()
1135 .write(true)
1136 .create_new(true)
1137 .open(bar.join("hello2.txt"))
1138 .is_ok(),
1139 "creating a new file (`hello2.txt`)",
1140 );
1141
1142 let cur_dir = read_dir_names(&fs, Path::new("/"));
1143
1144 assert!(
1145 !cur_dir.contains(&"foo".to_string()),
1146 "the foo directory still exists"
1147 );
1148
1149 assert!(
1150 cur_dir.contains(&"bar".to_string()),
1151 "the bar directory still exists"
1152 );
1153
1154 let bar_dir = read_dir_names(&fs, bar);
1155
1156 if !bar_dir.contains(&"qux".to_string()) {
1157 println!("qux does not exist: {bar_dir:?}")
1158 }
1159
1160 let qux_dir = read_dir_names(&fs, bar.join("qux"));
1161
1162 assert!(qux_dir.is_empty(), "the qux directory is empty");
1163
1164 assert!(
1165 bar_realpath.join("hello1.txt").exists(),
1166 "the /bar/hello1.txt file exists"
1167 );
1168
1169 assert!(
1170 bar_realpath.join("hello2.txt").exists(),
1171 "the /bar/hello2.txt file exists"
1172 );
1173
1174 assert_eq!(fs.create_dir(foo), Ok(()), "create ./foo again");
1175
1176 assert_eq!(
1177 fs.rename(&bar.join("hello2.txt"), &foo.join("world2.txt"))
1178 .await,
1179 Ok(()),
1180 "renaming (and moving) a file",
1181 );
1182
1183 assert_eq!(
1184 fs.rename(foo, &bar.join("baz")).await,
1185 Ok(()),
1186 "renaming a directory",
1187 );
1188
1189 assert_eq!(
1190 fs.rename(&bar.join("hello1.txt"), &bar.join("world1.txt"))
1191 .await,
1192 Ok(()),
1193 "renaming a file (in the same directory)",
1194 );
1195
1196 assert!(bar_realpath.exists(), "./bar exists");
1197 assert!(bar_realpath.join("baz").exists(), "./bar/baz exists");
1198 assert!(!foo_realpath.exists(), "foo does not exist anymore");
1199 assert!(
1200 bar_realpath.join("baz/world2.txt").exists(),
1201 "/bar/baz/world2.txt exists"
1202 );
1203 assert!(
1204 bar_realpath.join("world1.txt").exists(),
1205 "/bar/world1.txt (ex hello1.txt) exists"
1206 );
1207 assert!(
1208 !bar_realpath.join("hello1.txt").exists(),
1209 "hello1.txt was moved"
1210 );
1211 assert!(
1212 !bar_realpath.join("hello2.txt").exists(),
1213 "hello2.txt was moved"
1214 );
1215 assert!(
1216 bar_realpath.join("baz/world2.txt").exists(),
1217 "world2.txt was moved to the correct place"
1218 );
1219 }
1220
1221 #[tokio::test]
1222 async fn test_metadata() {
1223 use std::thread::sleep;
1224 use std::time::Duration;
1225
1226 let temp = TempDir::new().unwrap();
1227
1228 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
1229
1230 let root_metadata = fs.metadata(Path::new("/")).unwrap();
1231
1232 assert!(root_metadata.ft.dir);
1233 #[cfg(not(target_env = "musl"))]
1235 assert_eq!(root_metadata.accessed, root_metadata.created);
1236 #[cfg(not(target_env = "musl"))]
1237 assert_eq!(root_metadata.modified, root_metadata.created);
1238 assert!(root_metadata.modified > 0);
1239
1240 let foo = Path::new("foo");
1241
1242 assert_eq!(fs.create_dir(foo), Ok(()));
1243
1244 let foo_metadata = fs.metadata(foo);
1245 assert!(foo_metadata.is_ok());
1246 let foo_metadata = foo_metadata.unwrap();
1247
1248 assert!(foo_metadata.ft.dir);
1249 #[cfg(not(target_env = "musl"))]
1250 assert_eq!(foo_metadata.accessed, foo_metadata.created);
1251 #[cfg(not(target_env = "musl"))]
1252 assert_eq!(foo_metadata.modified, foo_metadata.created);
1253 assert!(foo_metadata.modified > 0);
1254
1255 sleep(Duration::from_secs(3));
1256
1257 let bar = Path::new("bar");
1258
1259 assert_eq!(fs.rename(foo, bar).await, Ok(()));
1260
1261 let bar_metadata = fs.metadata(bar).unwrap();
1262 assert!(bar_metadata.ft.dir);
1263 assert!(bar_metadata.accessed >= foo_metadata.accessed);
1264 assert_eq!(bar_metadata.created, foo_metadata.created);
1265 assert!(bar_metadata.modified > foo_metadata.modified);
1266
1267 let root_metadata = fs.metadata(bar).unwrap();
1268 assert!(
1269 root_metadata.modified > foo_metadata.modified,
1270 "the parent modified time was updated"
1271 );
1272 }
1273
1274 #[tokio::test]
1275 async fn test_rejects_host_absolute_paths_inside_root() {
1276 let temp = TempDir::new().unwrap();
1277 let temp_canon = super::canonicalize(temp.path()).expect("canonicalize temp dir");
1280
1281 let file_path = temp_canon.join("foo.txt");
1282 std::fs::write(&file_path, b"hello").unwrap();
1283
1284 let fs = FileSystem::new(Handle::current(), &temp_canon).expect("get filesystem");
1285
1286 assert_eq!(fs.metadata(&file_path), Err(FsError::InvalidInput));
1287 assert!(matches!(
1288 fs.new_open_options().read(true).open(&file_path),
1289 Err(FsError::InvalidInput)
1290 ));
1291 }
1292
1293 #[tokio::test]
1294 async fn test_remove_file() {
1295 let temp = TempDir::new().unwrap();
1296 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
1297
1298 assert!(
1299 fs.new_open_options()
1300 .write(true)
1301 .create_new(true)
1302 .open(Path::new("foo.txt"))
1303 .is_ok(),
1304 "creating a new file",
1305 );
1306
1307 assert!(read_dir_names(&fs, Path::new("/")).contains(&"foo.txt".to_string()));
1308
1309 assert!(temp.path().join("foo.txt").is_file());
1310
1311 assert_eq!(
1312 fs.remove_file(Path::new("foo.txt")),
1313 Ok(()),
1314 "removing a file that exists",
1315 );
1316
1317 assert!(!temp.path().join("foo.txt").exists());
1318
1319 assert_eq!(
1320 fs.remove_file(Path::new("foo.txt")),
1321 Err(FsError::EntryNotFound),
1322 "removing a file that doesn't exists",
1323 );
1324 }
1325
1326 #[tokio::test]
1327 async fn test_readdir() {
1328 let temp = TempDir::new().unwrap();
1329 let fs = FileSystem::new(Handle::current(), temp.path()).expect("get filesystem");
1330
1331 assert_eq!(fs.create_dir(Path::new("foo")), Ok(()), "creating `foo`");
1332 assert_eq!(
1333 fs.create_dir(Path::new("foo/sub")),
1334 Ok(()),
1335 "creating `sub`"
1336 );
1337 assert_eq!(fs.create_dir(Path::new("bar")), Ok(()), "creating `bar`");
1338 assert_eq!(fs.create_dir(Path::new("baz")), Ok(()), "creating `bar`");
1339 assert!(
1340 fs.new_open_options()
1341 .write(true)
1342 .create_new(true)
1343 .open(Path::new("a.txt"))
1344 .is_ok(),
1345 "creating `a.txt`",
1346 );
1347 assert!(
1348 fs.new_open_options()
1349 .write(true)
1350 .create_new(true)
1351 .open(Path::new("b.txt"))
1352 .is_ok(),
1353 "creating `b.txt`",
1354 );
1355
1356 let readdir = fs.read_dir(Path::new("/"));
1357
1358 assert!(
1359 readdir.is_ok(),
1360 "reading the directory `{}`",
1361 Path::new("/").display()
1362 );
1363
1364 let mut readdir = readdir.unwrap();
1365
1366 let next = readdir.next().unwrap().unwrap();
1367 assert!(next.path.ends_with("a.txt"), "checking entry #1");
1368 assert!(next.metadata().unwrap().is_file(), "checking entry #1");
1369
1370 let next = readdir.next().unwrap().unwrap();
1371 assert!(next.path.ends_with("b.txt"), "checking entry #2");
1372 assert!(next.metadata().unwrap().is_file(), "checking entry #2");
1373
1374 let next = readdir.next().unwrap().unwrap();
1375 assert!(next.path.ends_with("bar"), "checking entry #3");
1376 assert!(next.metadata().unwrap().is_dir(), "checking entry #3");
1377
1378 let next = readdir.next().unwrap().unwrap();
1379 assert!(next.path.ends_with("baz"), "checking entry #4");
1380 assert!(next.metadata().unwrap().is_dir(), "checking entry #4");
1381
1382 let next = readdir.next().unwrap().unwrap();
1383 assert!(next.path.ends_with("foo"), "checking entry #5");
1384 assert!(next.metadata().unwrap().is_dir(), "checking entry #5");
1385
1386 if let Some(s) = readdir.next() {
1387 panic!("next: {s:?}");
1388 }
1389 }
1390}