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