// Copyright 2018-2026 the Deno authors. MIT license.

use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::io::ErrorKind;
use std::io::SeekFrom;
use std::ops::Range;
use std::path::Path;
use std::path::PathBuf;
#[cfg(unix)]
use std::process::Stdio as StdStdio;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use std::time::SystemTime;

use deno_core::BufMutView;
use deno_core::BufView;
use deno_core::ResourceHandleFd;
use deno_lib::standalone::virtual_fs::FileSystemCaseSensitivity;
use deno_lib::standalone::virtual_fs::OffsetWithLength;
use deno_lib::standalone::virtual_fs::VfsEntry;
use deno_lib::standalone::virtual_fs::VfsEntryRef;
use deno_lib::standalone::virtual_fs::VirtualDirectory;
use deno_lib::standalone::virtual_fs::VirtualFile;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_fs::FsDirEntry;
use deno_runtime::deno_fs::FsFileType;
use deno_runtime::deno_fs::FsReadDir;
use deno_runtime::deno_fs::FsReadDirRc;
use deno_runtime::deno_fs::OpenOptions;
use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_fs::sync::new_rc;
use deno_runtime::deno_io;
use deno_runtime::deno_io::fs::File as DenoFile;
use deno_runtime::deno_io::fs::FsError;
use deno_runtime::deno_io::fs::FsResult;
use deno_runtime::deno_io::fs::FsStat;
use deno_runtime::deno_io::fs::FsStatFs;
use deno_runtime::deno_napi::DenoRtNativeAddonLoader;
use deno_runtime::deno_napi::DenoRtNativeAddonLoaderRc;
use deno_runtime::deno_permissions::CheckedPath;
use deno_runtime::deno_permissions::CheckedPathBuf;
#[cfg(windows)]
use deno_subprocess_windows::Stdio as StdStdio;
use sys_traits::FsCopy;
use sys_traits::boxed::BoxedFsDirEntry;
use sys_traits::boxed::BoxedFsMetadataValue;
use sys_traits::boxed::FsMetadataBoxed;
use sys_traits::boxed::FsReadDirBoxed;
use url::Url;

#[derive(Debug, Clone)]
pub struct DenoRtSys {
  vfs: Arc<FileBackedVfs>,
  self_extracting: bool,
}

impl DenoRtSys {
  pub fn new(vfs: Arc<FileBackedVfs>) -> Self {
    Self {
      vfs,
      self_extracting: false,
    }
  }

  pub fn new_self_extracting(vfs: Arc<FileBackedVfs>) -> Self {
    Self {
      vfs,
      self_extracting: true,
    }
  }

  pub fn maybe_native_addon_loader(&self) -> Option<DenoRtNativeAddonLoaderRc> {
    if self.self_extracting {
      // native addons are already on disk, no need for VFS extraction
      None
    } else {
      Some(self.vfs.clone())
    }
  }

  pub fn is_specifier_in_vfs(&self, specifier: &Url) -> bool {
    deno_path_util::url_to_file_path(specifier)
      .map(|p| self.is_in_vfs(&p))
      .unwrap_or(false)
  }

  pub fn is_in_vfs(&self, path: &Path) -> bool {
    self.vfs.is_path_within(path)
  }

  fn error_if_in_vfs(&self, path: &Path) -> FsResult<()> {
    if !self.self_extracting && self.vfs.is_path_within(path) {
      Err(FsError::NotSupported)
    } else {
      Ok(())
    }
  }

  /// Returns true if the VFS should be consulted for this path.
  fn is_vfs_path(&self, path: &Path) -> bool {
    !self.self_extracting && self.vfs.is_path_within(path)
  }

  fn copy_to_real_path(
    &self,
    oldpath: &CheckedPath,
    newpath: &CheckedPath,
  ) -> std::io::Result<u64> {
    let old_file = self.vfs.file_entry(oldpath)?;
    let old_file_bytes = self.vfs.read_file_all(old_file)?;
    let len = old_file_bytes.len() as u64;
    RealFs
      .write_file_sync(
        newpath,
        OpenOptions {
          read: false,
          write: true,
          create: true,
          truncate: true,
          append: false,
          create_new: false,
          custom_flags: None,
          mode: None,
        },
        &old_file_bytes,
      )
      .map_err(|err| err.into_io_error())?;
    Ok(len)
  }
}

#[async_trait::async_trait(?Send)]
impl FileSystem for DenoRtSys {
  fn cwd(&self) -> FsResult<PathBuf> {
    RealFs.cwd()
  }

  fn tmp_dir(&self) -> FsResult<PathBuf> {
    RealFs.tmp_dir()
  }

  fn chdir(&self, path: &CheckedPath) -> FsResult<()> {
    if self.is_vfs_path(path) {
      // The process working directory can't actually be changed to a path
      // inside the embedded virtual file system, but applications (e.g.
      // Next.js standalone builds) commonly chdir into their own directory.
      // Verify the target exists and is a directory in the VFS and treat the
      // change as a no-op rather than failing with NotSupported. Note that
      // Deno.cwd() still reports the previous (real) working directory.
      if self.vfs.stat(path)?.as_fs_stat().is_directory {
        Ok(())
      } else {
        Err(FsError::Io(std::io::Error::new(
          ErrorKind::NotADirectory,
          "Not a directory",
        )))
      }
    } else {
      RealFs.chdir(path)
    }
  }

  fn umask(&self, mask: Option<u32>) -> FsResult<u32> {
    RealFs.umask(mask)
  }

  fn open_sync(
    &self,
    path: &CheckedPath,
    options: OpenOptions,
  ) -> FsResult<Rc<dyn DenoFile>> {
    if self.is_vfs_path(path) {
      Ok(Rc::new(self.vfs.open_file(path)?))
    } else {
      RealFs.open_sync(path, options)
    }
  }
  async fn open_async<'a>(
    &'a self,
    path: CheckedPathBuf,
    options: OpenOptions,
  ) -> FsResult<Rc<dyn DenoFile>> {
    if self.is_vfs_path(&path) {
      Ok(Rc::new(self.vfs.open_file(&path)?))
    } else {
      RealFs.open_async(path, options).await
    }
  }

  fn mkdir_sync(
    &self,
    path: &CheckedPath,
    recursive: bool,
    mode: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.mkdir_sync(path, recursive, mode)
  }
  async fn mkdir_async(
    &self,
    path: CheckedPathBuf,
    recursive: bool,
    mode: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.mkdir_async(path, recursive, mode).await
  }

  #[cfg(unix)]
  fn chmod_sync(&self, path: &CheckedPath, mode: u32) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.chmod_sync(path, mode)
  }
  #[cfg(not(unix))]
  fn chmod_sync(&self, path: &CheckedPath, mode: i32) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.chmod_sync(path, mode)
  }

  #[cfg(unix)]
  async fn chmod_async(&self, path: CheckedPathBuf, mode: u32) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.chmod_async(path, mode).await
  }
  #[cfg(not(unix))]
  async fn chmod_async(&self, path: CheckedPathBuf, mode: i32) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.chmod_async(path, mode).await
  }

  fn chown_sync(
    &self,
    path: &CheckedPath,
    uid: Option<u32>,
    gid: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.chown_sync(path, uid, gid)
  }
  async fn chown_async(
    &self,
    path: CheckedPathBuf,
    uid: Option<u32>,
    gid: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.chown_async(path, uid, gid).await
  }

  fn exists_sync(&self, path: &CheckedPath) -> bool {
    if self.is_vfs_path(path) {
      self.vfs.exists(path)
    } else {
      RealFs.exists_sync(path)
    }
  }
  async fn exists_async(&self, path: CheckedPathBuf) -> FsResult<bool> {
    if self.is_vfs_path(&path) {
      Ok(self.vfs.exists(&path))
    } else {
      RealFs.exists_async(path).await
    }
  }

  fn lchmod_sync(&self, path: &CheckedPath, mode: u32) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.lchmod_sync(path, mode)
  }

  async fn lchmod_async(
    &self,
    path: CheckedPathBuf,
    mode: u32,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.lchmod_async(path, mode).await
  }

  fn lchown_sync(
    &self,
    path: &CheckedPath,
    uid: Option<u32>,
    gid: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.lchown_sync(path, uid, gid)
  }

  async fn lchown_async(
    &self,
    path: CheckedPathBuf,
    uid: Option<u32>,
    gid: Option<u32>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.lchown_async(path, uid, gid).await
  }

  fn remove_sync(&self, path: &CheckedPath, recursive: bool) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.remove_sync(path, recursive)
  }
  async fn remove_async(
    &self,
    path: CheckedPathBuf,
    recursive: bool,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.remove_async(path, recursive).await
  }

  fn rmdir_sync(&self, path: &CheckedPath) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.rmdir_sync(path)
  }
  async fn rmdir_async(&self, path: CheckedPathBuf) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.rmdir_async(path).await
  }

  fn copy_file_sync(
    &self,
    oldpath: &CheckedPath,
    newpath: &CheckedPath,
  ) -> FsResult<()> {
    self.error_if_in_vfs(newpath)?;
    if self.is_vfs_path(oldpath) {
      self
        .copy_to_real_path(oldpath, newpath)
        .map(|_| ())
        .map_err(FsError::Io)
    } else {
      RealFs.copy_file_sync(oldpath, newpath)
    }
  }
  async fn copy_file_async(
    &self,
    oldpath: CheckedPathBuf,
    newpath: CheckedPathBuf,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&newpath)?;
    if self.is_vfs_path(&oldpath) {
      let fs = self.clone();
      tokio::task::spawn_blocking(move || {
        fs.copy_to_real_path(
          &oldpath.as_checked_path(),
          &newpath.as_checked_path(),
        )
        .map(|_| ())
        .map_err(FsError::Io)
      })
      .await?
    } else {
      RealFs.copy_file_async(oldpath, newpath).await
    }
  }

  fn cp_sync(&self, from: &CheckedPath, to: &CheckedPath) -> FsResult<()> {
    self.error_if_in_vfs(to)?;

    RealFs.cp_sync(from, to)
  }
  async fn cp_async(
    &self,
    from: CheckedPathBuf,
    to: CheckedPathBuf,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&to)?;

    RealFs.cp_async(from, to).await
  }

  fn stat_sync(&self, path: &CheckedPath) -> FsResult<FsStat> {
    if self.is_vfs_path(path) {
      Ok(self.vfs.stat(path)?.as_fs_stat())
    } else {
      RealFs.stat_sync(path)
    }
  }
  async fn stat_async(&self, path: CheckedPathBuf) -> FsResult<FsStat> {
    if self.is_vfs_path(&path) {
      Ok(self.vfs.stat(&path)?.as_fs_stat())
    } else {
      RealFs.stat_async(path).await
    }
  }

  fn lstat_sync(&self, path: &CheckedPath) -> FsResult<FsStat> {
    if self.is_vfs_path(path) {
      Ok(self.vfs.lstat(path)?.as_fs_stat())
    } else {
      RealFs.lstat_sync(path)
    }
  }
  async fn lstat_async(&self, path: CheckedPathBuf) -> FsResult<FsStat> {
    if self.is_vfs_path(&path) {
      Ok(self.vfs.lstat(&path)?.as_fs_stat())
    } else {
      RealFs.lstat_async(path).await
    }
  }

  fn statfs_sync(
    &self,
    path: &CheckedPath,
    bigint: bool,
  ) -> FsResult<FsStatFs> {
    if self.is_vfs_path(path) {
      // the entry must exist within the embedded read-only file system
      self.vfs.stat(path)?;
      Ok(vfs_statfs())
    } else {
      RealFs.statfs_sync(path, bigint)
    }
  }
  async fn statfs_async(
    &self,
    path: CheckedPathBuf,
    bigint: bool,
  ) -> FsResult<FsStatFs> {
    if self.is_vfs_path(&path) {
      // the entry must exist within the embedded read-only file system
      self.vfs.stat(&path)?;
      Ok(vfs_statfs())
    } else {
      RealFs.statfs_async(path, bigint).await
    }
  }

  fn realpath_sync(&self, path: &CheckedPath) -> FsResult<PathBuf> {
    if self.is_vfs_path(path) {
      Ok(self.vfs.canonicalize(path)?)
    } else {
      RealFs.realpath_sync(path)
    }
  }
  async fn realpath_async(&self, path: CheckedPathBuf) -> FsResult<PathBuf> {
    if self.is_vfs_path(&path) {
      Ok(self.vfs.canonicalize(&path)?)
    } else {
      RealFs.realpath_async(path).await
    }
  }

  fn read_dir_sync(&self, path: &CheckedPath) -> FsResult<Vec<FsDirEntry>> {
    if self.is_vfs_path(path) {
      Ok(self.vfs.read_dir(path)?)
    } else {
      RealFs.read_dir_sync(path)
    }
  }
  async fn read_dir_async(
    &self,
    path: CheckedPathBuf,
  ) -> FsResult<FsReadDirRc> {
    if self.is_vfs_path(&path) {
      Ok(new_rc(VfsReadDir(Mutex::new(
        self.vfs.read_dir(&path)?.into_iter(),
      ))))
    } else {
      RealFs.read_dir_async(path).await
    }
  }

  fn rename_sync(
    &self,
    oldpath: &CheckedPath,
    newpath: &CheckedPath,
  ) -> FsResult<()> {
    self.error_if_in_vfs(oldpath)?;
    self.error_if_in_vfs(newpath)?;
    RealFs.rename_sync(oldpath, newpath)
  }
  async fn rename_async(
    &self,
    oldpath: CheckedPathBuf,
    newpath: CheckedPathBuf,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&oldpath)?;
    self.error_if_in_vfs(&newpath)?;
    RealFs.rename_async(oldpath, newpath).await
  }

  fn link_sync(
    &self,
    oldpath: &CheckedPath,
    newpath: &CheckedPath,
  ) -> FsResult<()> {
    self.error_if_in_vfs(oldpath)?;
    self.error_if_in_vfs(newpath)?;
    RealFs.link_sync(oldpath, newpath)
  }
  async fn link_async(
    &self,
    oldpath: CheckedPathBuf,
    newpath: CheckedPathBuf,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&oldpath)?;
    self.error_if_in_vfs(&newpath)?;
    RealFs.link_async(oldpath, newpath).await
  }

  fn symlink_sync(
    &self,
    oldpath: &CheckedPath,
    newpath: &CheckedPath,
    file_type: Option<FsFileType>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(oldpath)?;
    self.error_if_in_vfs(newpath)?;
    RealFs.symlink_sync(oldpath, newpath, file_type)
  }
  async fn symlink_async(
    &self,
    oldpath: CheckedPathBuf,
    newpath: CheckedPathBuf,
    file_type: Option<FsFileType>,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&oldpath)?;
    self.error_if_in_vfs(&newpath)?;
    RealFs.symlink_async(oldpath, newpath, file_type).await
  }

  fn read_link_sync(&self, path: &CheckedPath) -> FsResult<PathBuf> {
    if self.is_vfs_path(path) {
      Ok(self.vfs.read_link(path)?)
    } else {
      RealFs.read_link_sync(path)
    }
  }
  async fn read_link_async(&self, path: CheckedPathBuf) -> FsResult<PathBuf> {
    if self.is_vfs_path(&path) {
      Ok(self.vfs.read_link(&path)?)
    } else {
      RealFs.read_link_async(path).await
    }
  }

  fn truncate_sync(&self, path: &CheckedPath, len: u64) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.truncate_sync(path, len)
  }
  async fn truncate_async(
    &self,
    path: CheckedPathBuf,
    len: u64,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs.truncate_async(path, len).await
  }

  fn utime_sync(
    &self,
    path: &CheckedPath,
    atime_secs: i64,
    atime_nanos: u32,
    mtime_secs: i64,
    mtime_nanos: u32,
  ) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.utime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
  }
  async fn utime_async(
    &self,
    path: CheckedPathBuf,
    atime_secs: i64,
    atime_nanos: u32,
    mtime_secs: i64,
    mtime_nanos: u32,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs
      .utime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
      .await
  }

  fn lutime_sync(
    &self,
    path: &CheckedPath,
    atime_secs: i64,
    atime_nanos: u32,
    mtime_secs: i64,
    mtime_nanos: u32,
  ) -> FsResult<()> {
    self.error_if_in_vfs(path)?;
    RealFs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
  }
  async fn lutime_async(
    &self,
    path: CheckedPathBuf,
    atime_secs: i64,
    atime_nanos: u32,
    mtime_secs: i64,
    mtime_nanos: u32,
  ) -> FsResult<()> {
    self.error_if_in_vfs(&path)?;
    RealFs
      .lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
      .await
  }
}

#[derive(Debug)]
struct VfsReadDir(Mutex<std::vec::IntoIter<FsDirEntry>>);

#[async_trait::async_trait(?Send)]
impl FsReadDir for VfsReadDir {
  async fn next(&self) -> FsResult<Option<FsDirEntry>> {
    Ok(self.0.try_lock().map_err(|_| FsError::FileBusy)?.next())
  }
}

impl sys_traits::BaseFsHardLink for DenoRtSys {
  #[inline]
  fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> std::io::Result<()> {
    self
      .link_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(src)),
        &CheckedPath::unsafe_new(Cow::Borrowed(dst)),
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::BaseFsRead for DenoRtSys {
  #[inline]
  fn base_fs_read(&self, path: &Path) -> std::io::Result<Cow<'static, [u8]>> {
    self
      .read_file_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(path)),
        OpenOptions::read(),
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::FsMetadataValue for FileBackedVfsMetadata {
  fn file_type(&self) -> sys_traits::FileType {
    self.file_type
  }

  fn len(&self) -> u64 {
    self.len
  }

  fn accessed(&self) -> std::io::Result<SystemTime> {
    Err(not_supported("accessed time"))
  }

  fn created(&self) -> std::io::Result<SystemTime> {
    Err(not_supported("created time"))
  }

  fn changed(&self) -> std::io::Result<SystemTime> {
    Err(not_supported("changed time"))
  }

  fn modified(&self) -> std::io::Result<SystemTime> {
    Err(not_supported("modified time"))
  }

  fn dev(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn ino(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn mode(&self) -> std::io::Result<u32> {
    Ok(0)
  }

  fn nlink(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn uid(&self) -> std::io::Result<u32> {
    Ok(0)
  }

  fn gid(&self) -> std::io::Result<u32> {
    Ok(0)
  }

  fn rdev(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn blksize(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn blocks(&self) -> std::io::Result<u64> {
    Ok(0)
  }

  fn is_block_device(&self) -> std::io::Result<bool> {
    Ok(false)
  }

  fn is_char_device(&self) -> std::io::Result<bool> {
    Ok(false)
  }

  fn is_fifo(&self) -> std::io::Result<bool> {
    Ok(false)
  }

  fn is_socket(&self) -> std::io::Result<bool> {
    Ok(false)
  }

  fn file_attributes(&self) -> std::io::Result<u32> {
    Ok(0)
  }
}

/// `statfs` result for a path within the embedded read-only file system.
///
/// There is no real backing device, so this reports an empty read-only file
/// system (no free space) rather than failing or leaking host disk stats.
fn vfs_statfs() -> FsStatFs {
  FsStatFs::default()
}

fn not_supported(name: &str) -> std::io::Error {
  std::io::Error::new(
    ErrorKind::Unsupported,
    format!(
      "{} is not supported for an embedded deno compile file",
      name
    ),
  )
}

impl sys_traits::FsDirEntry for FileBackedVfsDirEntry {
  type Metadata = BoxedFsMetadataValue;

  fn file_name(&self) -> Cow<'_, std::ffi::OsStr> {
    Cow::Borrowed(self.metadata.name.as_ref())
  }

  fn file_type(&self) -> std::io::Result<sys_traits::FileType> {
    Ok(self.metadata.file_type)
  }

  fn metadata(&self) -> std::io::Result<Self::Metadata> {
    Ok(BoxedFsMetadataValue(Box::new(self.metadata.clone())))
  }

  fn path(&self) -> Cow<'_, Path> {
    Cow::Owned(self.parent_path.join(&self.metadata.name))
  }
}

impl sys_traits::BaseFsReadDir for DenoRtSys {
  type ReadDirEntry = BoxedFsDirEntry;

  fn base_fs_read_dir(
    &self,
    path: &Path,
  ) -> std::io::Result<
    Box<dyn Iterator<Item = std::io::Result<Self::ReadDirEntry>>>,
  > {
    if self.is_vfs_path(path) {
      let entries = self.vfs.read_dir_with_metadata(path)?;
      Ok(Box::new(
        entries.map(|entry| Ok(BoxedFsDirEntry::new(entry))),
      ))
    } else {
      #[allow(
        clippy::disallowed_methods,
        reason = "ok because we're implementing the sys"
      )]
      sys_traits::impls::RealSys.fs_read_dir_boxed(path)
    }
  }
}

impl sys_traits::BaseFsCanonicalize for DenoRtSys {
  #[inline]
  fn base_fs_canonicalize(&self, path: &Path) -> std::io::Result<PathBuf> {
    self
      .realpath_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(path)),
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::BaseFsMetadata for DenoRtSys {
  type Metadata = BoxedFsMetadataValue;

  #[inline]
  fn base_fs_metadata(&self, path: &Path) -> std::io::Result<Self::Metadata> {
    if self.is_vfs_path(path) {
      Ok(BoxedFsMetadataValue::new(self.vfs.stat(path)?))
    } else {
      #[allow(
        clippy::disallowed_methods,
        reason = "ok because we're implementing the sys"
      )]
      sys_traits::impls::RealSys.fs_metadata_boxed(path)
    }
  }

  #[inline]
  fn base_fs_symlink_metadata(
    &self,
    path: &Path,
  ) -> std::io::Result<Self::Metadata> {
    if self.is_vfs_path(path) {
      Ok(BoxedFsMetadataValue::new(self.vfs.lstat(path)?))
    } else {
      #[allow(
        clippy::disallowed_methods,
        reason = "ok because we're implementing the sys"
      )]
      sys_traits::impls::RealSys.fs_symlink_metadata_boxed(path)
    }
  }
}

impl sys_traits::BaseFsCopy for DenoRtSys {
  #[inline]
  fn base_fs_copy(&self, from: &Path, to: &Path) -> std::io::Result<u64> {
    self
      .error_if_in_vfs(to)
      .map_err(|err| err.into_io_error())?;
    if self.is_vfs_path(from) {
      self.copy_to_real_path(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(from)),
        &CheckedPath::unsafe_new(Cow::Borrowed(to)),
      )
    } else {
      #[allow(
        clippy::disallowed_types,
        reason = "ok because we're implementing the sys"
      )]
      sys_traits::impls::RealSys.fs_copy(from, to)
    }
  }
}

impl sys_traits::BaseFsCloneFile for DenoRtSys {
  fn base_fs_clone_file(
    &self,
    _from: &Path,
    _to: &Path,
  ) -> std::io::Result<()> {
    // will cause a fallback in the code that uses this
    Err(not_supported("cloning files"))
  }
}

impl sys_traits::BaseFsCreateDir for DenoRtSys {
  #[inline]
  fn base_fs_create_dir(
    &self,
    path: &Path,
    options: &sys_traits::CreateDirOptions,
  ) -> std::io::Result<()> {
    self
      .mkdir_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(path)),
        options.recursive,
        options.mode,
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::BaseFsRemoveFile for DenoRtSys {
  #[inline]
  fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> {
    self
      .remove_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(path)),
        false,
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::BaseFsRename for DenoRtSys {
  #[inline]
  fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> {
    self
      .rename_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(from)),
        &CheckedPath::unsafe_new(Cow::Borrowed(to)),
      )
      .map_err(|err| err.into_io_error())
  }
}

pub enum FsFileAdapter {
  Real(sys_traits::impls::RealFsFile),
  Vfs(FileBackedVfsFile),
}

impl sys_traits::FsFile for FsFileAdapter {}

impl sys_traits::FsFileAsRaw for FsFileAdapter {
  #[cfg(windows)]
  fn fs_file_as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
    match self {
      Self::Real(file) => file.fs_file_as_raw_handle(),
      Self::Vfs(_) => None,
    }
  }

  #[cfg(unix)]
  fn fs_file_as_raw_fd(&self) -> Option<std::os::fd::RawFd> {
    match self {
      Self::Real(file) => file.fs_file_as_raw_fd(),
      Self::Vfs(_) => None,
    }
  }
}

impl sys_traits::FsFileMetadata for FsFileAdapter {
  #[inline]
  fn fs_file_metadata(&self) -> std::io::Result<BoxedFsMetadataValue> {
    match self {
      Self::Real(file) => file.fs_file_metadata(),
      Self::Vfs(file) => Ok(BoxedFsMetadataValue::new(FileBackedVfsMetadata {
        file_type: sys_traits::FileType::File,
        name: file.file.name.clone(),
        len: file.file.offset.len,
        mtime: file.file.mtime,
      })),
    }
  }
}

impl sys_traits::FsFileSyncData for FsFileAdapter {
  fn fs_file_sync_data(&mut self) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_sync_data(),
      Self::Vfs(_) => Ok(()),
    }
  }
}

impl sys_traits::FsFileSyncAll for FsFileAdapter {
  fn fs_file_sync_all(&mut self) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_sync_all(),
      Self::Vfs(_) => Ok(()),
    }
  }
}

impl sys_traits::FsFileSetPermissions for FsFileAdapter {
  #[inline]
  fn fs_file_set_permissions(&mut self, mode: u32) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_set_permissions(mode),
      Self::Vfs(_) => Ok(()),
    }
  }
}

impl std::io::Read for FsFileAdapter {
  #[inline]
  fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
    match self {
      Self::Real(file) => file.read(buf),
      Self::Vfs(file) => file.read_to_buf(buf),
    }
  }
}

impl std::io::Seek for FsFileAdapter {
  fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
    match self {
      Self::Real(file) => file.seek(pos),
      Self::Vfs(file) => file.seek(pos),
    }
  }
}

impl std::io::Write for FsFileAdapter {
  #[inline]
  fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
    match self {
      Self::Real(file) => file.write(buf),
      Self::Vfs(_) => Err(not_supported("writing files")),
    }
  }

  #[inline]
  fn flush(&mut self) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.flush(),
      Self::Vfs(_) => Err(not_supported("writing files")),
    }
  }
}

impl sys_traits::FsFileSetLen for FsFileAdapter {
  #[inline]
  fn fs_file_set_len(&mut self, len: u64) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_set_len(len),
      Self::Vfs(_) => Err(not_supported("setting file length")),
    }
  }
}

impl sys_traits::FsFileSetTimes for FsFileAdapter {
  fn fs_file_set_times(
    &mut self,
    times: sys_traits::FsFileTimes,
  ) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_set_times(times),
      Self::Vfs(_) => Err(not_supported("setting file times")),
    }
  }
}

impl sys_traits::FsFileLock for FsFileAdapter {
  fn fs_file_lock(
    &mut self,
    mode: sys_traits::FsFileLockMode,
  ) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_lock(mode),
      Self::Vfs(_) => Err(not_supported("locking files")),
    }
  }

  fn fs_file_try_lock(
    &mut self,
    mode: sys_traits::FsFileLockMode,
  ) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_try_lock(mode),
      Self::Vfs(_) => Err(not_supported("locking files")),
    }
  }

  fn fs_file_unlock(&mut self) -> std::io::Result<()> {
    match self {
      Self::Real(file) => file.fs_file_unlock(),
      Self::Vfs(_) => Err(not_supported("unlocking files")),
    }
  }
}

impl sys_traits::FsFileIsTerminal for FsFileAdapter {
  #[inline]
  fn fs_file_is_terminal(&self) -> bool {
    match self {
      Self::Real(file) => file.fs_file_is_terminal(),
      Self::Vfs(_) => false,
    }
  }
}

impl sys_traits::BaseFsOpen for DenoRtSys {
  type File = FsFileAdapter;

  fn base_fs_open(
    &self,
    path: &Path,
    options: &sys_traits::OpenOptions,
  ) -> std::io::Result<Self::File> {
    if self.is_vfs_path(path) {
      Ok(FsFileAdapter::Vfs(self.vfs.open_file(path)?))
    } else {
      #[allow(
        clippy::disallowed_methods,
        reason = "ok because we're implementing the sys"
      )]
      Ok(FsFileAdapter::Real(
        sys_traits::impls::RealSys.base_fs_open(path, options)?,
      ))
    }
  }
}

impl sys_traits::BaseFsSymlinkDir for DenoRtSys {
  fn base_fs_symlink_dir(&self, src: &Path, dst: &Path) -> std::io::Result<()> {
    self
      .symlink_sync(
        // PERMISSIONS: this is ok because JS code will never use sys_traits. Probably
        // we should flip this so that the `deno_fs::FileSystem` implementation uses `sys_traits`
        // rather than this calling into `deno_fs::FileSystem`
        &CheckedPath::unsafe_new(Cow::Borrowed(src)),
        &CheckedPath::unsafe_new(Cow::Borrowed(dst)),
        Some(FsFileType::Directory),
      )
      .map_err(|err| err.into_io_error())
  }
}

impl sys_traits::SystemRandom for DenoRtSys {
  #[inline]
  fn sys_random(&self, buf: &mut [u8]) -> std::io::Result<()> {
    #[allow(
      clippy::disallowed_methods,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.sys_random(buf)
  }
}

impl sys_traits::SystemTimeNow for DenoRtSys {
  #[inline]
  fn sys_time_now(&self) -> SystemTime {
    #[allow(
      clippy::disallowed_types,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.sys_time_now()
  }
}

impl sys_traits::ThreadSleep for DenoRtSys {
  #[inline]
  fn thread_sleep(&self, dur: Duration) {
    #[allow(
      clippy::disallowed_methods,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.thread_sleep(dur)
  }
}

impl sys_traits::EnvCurrentDir for DenoRtSys {
  #[inline]
  fn env_current_dir(&self) -> std::io::Result<PathBuf> {
    #[allow(
      clippy::disallowed_methods,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.env_current_dir()
  }
}

impl sys_traits::EnvHomeDir for DenoRtSys {
  #[inline]
  fn env_home_dir(&self) -> Option<PathBuf> {
    #[allow(
      clippy::disallowed_methods,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.env_home_dir()
  }
}

impl sys_traits::BaseEnvVar for DenoRtSys {
  fn base_env_var_os(
    &self,
    key: &std::ffi::OsStr,
  ) -> Option<std::ffi::OsString> {
    #[allow(
      clippy::disallowed_methods,
      reason = "ok because we're implementing the sys"
    )]
    sys_traits::impls::RealSys.base_env_var_os(key)
  }
}

#[derive(Debug)]
pub struct VfsRoot {
  pub dir: VirtualDirectory,
  pub root_path: PathBuf,
  pub start_file_offset: u64,
}

impl VfsRoot {
  fn find_entry<'a>(
    &'a self,
    path: &Path,
    case_sensitivity: FileSystemCaseSensitivity,
  ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
    self.find_entry_inner(path, &mut HashSet::new(), case_sensitivity)
  }

  fn find_entry_inner<'a>(
    &'a self,
    path: &Path,
    seen: &mut HashSet<PathBuf>,
    case_sensitivity: FileSystemCaseSensitivity,
  ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
    let mut path = Cow::Borrowed(path);
    loop {
      let (resolved_path, entry) =
        self.find_entry_no_follow_inner(&path, seen, case_sensitivity)?;
      match entry {
        VfsEntryRef::Symlink(symlink) => {
          if !seen.insert(path.to_path_buf()) {
            return Err(std::io::Error::other("circular symlinks"));
          }
          path = Cow::Owned(symlink.resolve_dest_from_root(&self.root_path));
        }
        _ => {
          return Ok((resolved_path, entry));
        }
      }
    }
  }

  fn find_entry_no_follow(
    &self,
    path: &Path,
    case_sensitivity: FileSystemCaseSensitivity,
  ) -> std::io::Result<(PathBuf, VfsEntryRef<'_>)> {
    self.find_entry_no_follow_inner(path, &mut HashSet::new(), case_sensitivity)
  }

  fn find_entry_no_follow_inner<'a>(
    &'a self,
    path: &Path,
    seen: &mut HashSet<PathBuf>,
    case_sensitivity: FileSystemCaseSensitivity,
  ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
    let relative_path = match path.strip_prefix(&self.root_path) {
      Ok(p) => p,
      Err(_) => {
        // "outside root" is the common host-FS fallback path in
        // desktop --hmr / runtime-dynamic imports. Don't print at all
        // by default; the caller falls through to a real filesystem
        // read. Set DENO_LOG=denort=debug to see it.
        log::debug!(
          target: "denort",
          "[VFS] path outside root '{}': {}",
          self.root_path.display(),
          path.display(),
        );
        return Err(std::io::Error::new(
          std::io::ErrorKind::NotFound,
          format!("path not found (outside root): {}", path.display()),
        ));
      }
    };
    let mut final_path = self.root_path.clone();
    let mut current_entry = VfsEntryRef::Dir(&self.dir);
    for component in relative_path.components() {
      let component = component.as_os_str();
      let current_dir = match current_entry {
        VfsEntryRef::Dir(dir) => {
          final_path.push(component);
          dir
        }
        VfsEntryRef::Symlink(symlink) => {
          let dest = symlink.resolve_dest_from_root(&self.root_path);
          let (resolved_path, entry) =
            self.find_entry_inner(&dest, seen, case_sensitivity)?;
          final_path = resolved_path; // overwrite with the new resolved path
          match entry {
            VfsEntryRef::Dir(dir) => {
              final_path.push(component);
              dir
            }
            _ => {
              return Err(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("path not found (symlink not dir): {}", path.display()),
              ));
            }
          }
        }
        _ => {
          return Err(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("path not found (not dir): {}", path.display()),
          ));
        }
      };
      let component = component.to_string_lossy();
      current_entry = current_dir
        .entries
        .get_by_name(&component, case_sensitivity)
        .ok_or_else(|| {
          std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("path not found (entry missing): {}", path.display()),
          )
        })?
        .as_ref();
    }

    Ok((final_path, current_entry))
  }
}

pub struct FileBackedVfsFile {
  file: VirtualFile,
  pos: RefCell<u64>,
  vfs: Arc<FileBackedVfs>,
}

impl FileBackedVfsFile {
  pub fn seek(&self, pos: SeekFrom) -> std::io::Result<u64> {
    match pos {
      SeekFrom::Start(pos) => {
        *self.pos.borrow_mut() = pos;
        Ok(pos)
      }
      SeekFrom::End(offset) => {
        if offset < 0 && -offset as u64 > self.file.offset.len {
          let msg = "An attempt was made to move the file pointer before the beginning of the file.";
          Err(std::io::Error::new(
            std::io::ErrorKind::PermissionDenied,
            msg,
          ))
        } else {
          let mut current_pos = self.pos.borrow_mut();
          *current_pos = if offset >= 0 {
            self.file.offset.len - (offset as u64)
          } else {
            self.file.offset.len + (-offset as u64)
          };
          Ok(*current_pos)
        }
      }
      SeekFrom::Current(offset) => {
        let mut current_pos = self.pos.borrow_mut();
        if offset >= 0 {
          *current_pos += offset as u64;
        } else if -offset as u64 > *current_pos {
          return Err(std::io::Error::new(
            std::io::ErrorKind::PermissionDenied,
            "An attempt was made to move the file pointer before the beginning of the file.",
          ));
        } else {
          *current_pos -= -offset as u64;
        }
        Ok(*current_pos)
      }
    }
  }

  pub fn read_to_buf(&self, buf: &mut [u8]) -> std::io::Result<usize> {
    let read_pos = {
      let mut pos = self.pos.borrow_mut();
      let read_pos = *pos;
      // advance the position due to the read
      *pos = std::cmp::min(self.file.offset.len, *pos + buf.len() as u64);
      read_pos
    };
    self.vfs.read_file(&self.file, read_pos, buf)
  }

  fn metadata(&self) -> FileBackedVfsMetadata {
    FileBackedVfsMetadata {
      name: self.file.name.clone(),
      file_type: sys_traits::FileType::File,
      len: self.file.offset.len,
      mtime: self.file.mtime,
    }
  }

  fn read_to_end(&self) -> FsResult<Cow<'static, [u8]>> {
    let read_pos = {
      let mut pos = self.pos.borrow_mut();
      let read_pos = *pos;
      // todo(dsherret): should this always set it to the end of the file?
      if *pos < self.file.offset.len {
        // advance the position due to the read
        *pos = self.file.offset.len;
      }
      read_pos
    };
    if read_pos > self.file.offset.len {
      return Ok(Cow::Borrowed(&[]));
    }
    if read_pos == 0 {
      Ok(self.vfs.read_file_all(&self.file)?)
    } else {
      let size = (self.file.offset.len - read_pos) as usize;
      let mut buf = vec![0; size];
      self.vfs.read_file(&self.file, read_pos, &mut buf)?;
      Ok(Cow::Owned(buf))
    }
  }
}

#[async_trait::async_trait(?Send)]
impl deno_io::fs::File for FileBackedVfsFile {
  fn maybe_path(&self) -> Option<&Path> {
    // ok because a vfs file will never be written to and this
    // method is only used for checking write permissions
    None
  }

  fn read_sync(self: Rc<Self>, buf: &mut [u8]) -> FsResult<usize> {
    self.read_to_buf(buf).map_err(Into::into)
  }
  async fn read_byob(
    self: Rc<Self>,
    mut buf: BufMutView,
  ) -> FsResult<(usize, BufMutView)> {
    // this is fast, no need to spawn a task
    let nread = self.read_to_buf(&mut buf)?;
    Ok((nread, buf))
  }

  fn write_sync(self: Rc<Self>, _buf: &[u8]) -> FsResult<usize> {
    Err(FsError::NotSupported)
  }
  async fn write(
    self: Rc<Self>,
    _buf: BufView,
  ) -> FsResult<deno_core::WriteOutcome> {
    Err(FsError::NotSupported)
  }

  fn write_all_sync(self: Rc<Self>, _buf: &[u8]) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn write_all(self: Rc<Self>, _buf: BufView) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
    self.read_to_end()
  }
  async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
    // this is fast, no need to spawn a task
    self.read_to_end()
  }

  fn chmod_sync(self: Rc<Self>, _pathmode: u32) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn chmod_async(self: Rc<Self>, _mode: u32) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn chown_sync(
    self: Rc<Self>,
    _uid: Option<u32>,
    _gid: Option<u32>,
  ) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  async fn chown_async(
    self: Rc<Self>,
    _uid: Option<u32>,
    _gid: Option<u32>,
  ) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn seek_sync(self: Rc<Self>, pos: SeekFrom) -> FsResult<u64> {
    self.seek(pos).map_err(|err| err.into())
  }
  async fn seek_async(self: Rc<Self>, pos: SeekFrom) -> FsResult<u64> {
    self.seek(pos).map_err(|err| err.into())
  }

  fn datasync_sync(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn datasync_async(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn sync_sync(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn sync_async(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> {
    Ok(self.metadata().as_fs_stat())
  }
  async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> {
    Ok(self.metadata().as_fs_stat())
  }

  fn lock_sync(self: Rc<Self>, _exclusive: bool) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn lock_async(self: Rc<Self>, _exclusive: bool) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn try_lock_sync(self: Rc<Self>, _exclusive: bool) -> FsResult<bool> {
    Err(FsError::NotSupported)
  }
  async fn try_lock_async(self: Rc<Self>, _exclusive: bool) -> FsResult<bool> {
    Err(FsError::NotSupported)
  }

  fn unlock_sync(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn unlock_async(self: Rc<Self>) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn truncate_sync(self: Rc<Self>, _len: u64) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn truncate_async(self: Rc<Self>, _len: u64) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  fn utime_sync(
    self: Rc<Self>,
    _atime_secs: i64,
    _atime_nanos: u32,
    _mtime_secs: i64,
    _mtime_nanos: u32,
  ) -> FsResult<()> {
    Err(FsError::NotSupported)
  }
  async fn utime_async(
    self: Rc<Self>,
    _atime_secs: i64,
    _atime_nanos: u32,
    _mtime_secs: i64,
    _mtime_nanos: u32,
  ) -> FsResult<()> {
    Err(FsError::NotSupported)
  }

  // lower level functionality
  fn as_stdio(self: Rc<Self>) -> FsResult<StdStdio> {
    Err(FsError::NotSupported)
  }
  fn read_at_sync(
    self: Rc<Self>,
    buf: &mut [u8],
    position: u64,
  ) -> FsResult<usize> {
    self
      .vfs
      .read_file(&self.file, position, buf)
      .map_err(FsError::Io)
  }
  async fn read_at_async(
    self: Rc<Self>,
    mut buf: BufMutView,
    position: u64,
  ) -> FsResult<(usize, BufMutView)> {
    let nread = self
      .vfs
      .read_file(&self.file, position, &mut buf)
      .map_err(FsError::Io)?;
    Ok((nread, buf))
  }
  fn write_at_sync(
    self: Rc<Self>,
    _buf: &[u8],
    _position: u64,
  ) -> FsResult<usize> {
    Err(FsError::NotSupported)
  }
  fn backing_fd(self: Rc<Self>) -> Option<ResourceHandleFd> {
    None
  }
  fn try_clone_inner(self: Rc<Self>) -> FsResult<Rc<dyn deno_io::fs::File>> {
    Ok(self)
  }
}

#[derive(Debug, Clone)]
pub struct FileBackedVfsDirEntry {
  pub parent_path: PathBuf,
  pub metadata: FileBackedVfsMetadata,
}

#[derive(Debug, Clone)]
pub struct FileBackedVfsMetadata {
  pub name: String,
  pub file_type: sys_traits::FileType,
  pub len: u64,
  pub mtime: Option<u128>,
}

impl FileBackedVfsMetadata {
  pub fn from_vfs_entry_ref(vfs_entry: VfsEntryRef) -> Self {
    FileBackedVfsMetadata {
      file_type: match vfs_entry {
        VfsEntryRef::Dir(_) => sys_traits::FileType::Dir,
        VfsEntryRef::File(_) => sys_traits::FileType::File,
        VfsEntryRef::Symlink(_) => sys_traits::FileType::Symlink,
      },
      name: vfs_entry.name().to_string(),
      len: match vfs_entry {
        VfsEntryRef::Dir(_) => 0,
        VfsEntryRef::File(file) => file.offset.len,
        VfsEntryRef::Symlink(_) => 0,
      },
      mtime: match vfs_entry {
        VfsEntryRef::Dir(_) => None,
        VfsEntryRef::File(file) => file.mtime,
        VfsEntryRef::Symlink(_) => None,
      },
    }
  }

  pub fn as_fs_stat(&self) -> FsStat {
    // to use lower overhead, use mtime instead of all time params
    //
    // VFS files are always readable. Directories also get execute (traverse).
    // Symlinks get 0o777 per Unix convention (target permissions matter).
    // This ensures node:fs access() checks succeed for embedded files.
    let mode = if self.file_type == sys_traits::FileType::Dir {
      0o555 // r-xr-xr-x
    } else if self.file_type == sys_traits::FileType::Symlink {
      0o777 // rwxrwxrwx (conventional for symlinks)
    } else {
      0o444 // r--r--r--
    };
    FsStat {
      is_directory: self.file_type == sys_traits::FileType::Dir,
      is_file: self.file_type == sys_traits::FileType::File,
      is_symlink: self.file_type == sys_traits::FileType::Symlink,
      atime: Some(self.get_mtime()),
      birthtime: Some(self.get_mtime()),
      mtime: Some(self.get_mtime()),
      ctime: Some(self.get_mtime()),
      blksize: 0,
      size: self.len,
      dev: 0,
      ino: None,
      mode,
      nlink: None,
      uid: 0,
      gid: 0,
      rdev: 0,
      blocks: None,
      is_block_device: false,
      is_char_device: false,
      is_fifo: false,
      is_socket: false,
    }
  }

  /// if `mtime` is `None`, return `0`.
  ///
  /// if `mtime` is greater than `i64::MAX`, return `i64::MAX`.
  fn get_mtime(&self) -> i64 {
    self.mtime.unwrap_or(0).try_into().unwrap_or(i64::MAX)
  }
}

#[derive(Debug)]
pub struct FileBackedVfs {
  vfs_data: Cow<'static, [u8]>,
  fs_root: VfsRoot,
  case_sensitivity: FileSystemCaseSensitivity,
}

impl FileBackedVfs {
  pub fn new(
    data: Cow<'static, [u8]>,
    fs_root: VfsRoot,
    case_sensitivity: FileSystemCaseSensitivity,
  ) -> Self {
    Self {
      vfs_data: data,
      fs_root,
      case_sensitivity,
    }
  }

  pub fn root(&self) -> &Path {
    &self.fs_root.root_path
  }

  pub fn root_dir(&self) -> &VirtualDirectory {
    &self.fs_root.dir
  }

  pub fn is_path_within(&self, path: &Path) -> bool {
    path.starts_with(&self.fs_root.root_path)
  }

  pub fn exists(&self, path: &Path) -> bool {
    self.fs_root.find_entry(path, self.case_sensitivity).is_ok()
  }

  pub fn open_file(
    self: &Arc<Self>,
    path: &Path,
  ) -> std::io::Result<FileBackedVfsFile> {
    let file = self.file_entry(path)?;
    Ok(FileBackedVfsFile {
      file: file.clone(),
      vfs: self.clone(),
      pos: Default::default(),
    })
  }

  pub fn read_dir(&self, path: &Path) -> std::io::Result<Vec<FsDirEntry>> {
    let dir = self.dir_entry(path)?;
    Ok(
      dir
        .entries
        .iter()
        .map(|entry| FsDirEntry {
          name: entry.name().to_string(),
          is_file: matches!(entry, VfsEntry::File(_)),
          is_directory: matches!(entry, VfsEntry::Dir(_)),
          is_symlink: matches!(entry, VfsEntry::Symlink(_)),
        })
        .collect(),
    )
  }

  pub fn read_dir_with_metadata(
    &self,
    path: &Path,
  ) -> std::io::Result<impl Iterator<Item = FileBackedVfsDirEntry> + use<>> {
    let dir = self.dir_entry(path)?;
    let path = path.to_path_buf();
    Ok(
      dir
        .entries
        .iter()
        .map(move |entry| FileBackedVfsDirEntry {
          parent_path: path.to_path_buf(),
          metadata: FileBackedVfsMetadata::from_vfs_entry_ref(entry.as_ref()),
        })
        .collect::<Vec<_>>()
        .into_iter(),
    )
  }

  pub fn read_link(&self, path: &Path) -> std::io::Result<PathBuf> {
    let (_, entry) = self
      .fs_root
      .find_entry_no_follow(path, self.case_sensitivity)?;
    match entry {
      VfsEntryRef::Symlink(symlink) => {
        Ok(symlink.resolve_dest_from_root(&self.fs_root.root_path))
      }
      VfsEntryRef::Dir(_) | VfsEntryRef::File(_) => {
        Err(std::io::Error::other("not a symlink"))
      }
    }
  }

  pub fn lstat(&self, path: &Path) -> std::io::Result<FileBackedVfsMetadata> {
    let (_, entry) = self
      .fs_root
      .find_entry_no_follow(path, self.case_sensitivity)?;
    Ok(FileBackedVfsMetadata::from_vfs_entry_ref(entry))
  }

  pub fn stat(&self, path: &Path) -> std::io::Result<FileBackedVfsMetadata> {
    let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
    Ok(FileBackedVfsMetadata::from_vfs_entry_ref(entry))
  }

  pub fn canonicalize(&self, path: &Path) -> std::io::Result<PathBuf> {
    let (path, _) = self.fs_root.find_entry(path, self.case_sensitivity)?;
    Ok(path)
  }

  pub fn read_file_all(
    &self,
    file: &VirtualFile,
  ) -> std::io::Result<Cow<'static, [u8]>> {
    self.read_file_offset_with_len(file.offset)
  }

  pub fn read_file_offset_with_len(
    &self,
    offset_with_len: OffsetWithLength,
  ) -> std::io::Result<Cow<'static, [u8]>> {
    let read_range =
      self.get_read_range(offset_with_len, 0, offset_with_len.len)?;
    match &self.vfs_data {
      Cow::Borrowed(data) => Ok(Cow::Borrowed(&data[read_range])),
      Cow::Owned(data) => Ok(Cow::Owned(data[read_range].to_vec())),
    }
  }

  pub fn read_file(
    &self,
    file: &VirtualFile,
    pos: u64,
    buf: &mut [u8],
  ) -> std::io::Result<usize> {
    let read_range = self.get_read_range(file.offset, pos, buf.len() as u64)?;
    let read_len = read_range.len();
    buf[..read_len].copy_from_slice(&self.vfs_data[read_range]);
    Ok(read_len)
  }

  fn get_read_range(
    &self,
    file_offset_and_len: OffsetWithLength,
    pos: u64,
    len: u64,
  ) -> std::io::Result<Range<usize>> {
    if pos > file_offset_and_len.len {
      return Err(std::io::Error::new(
        std::io::ErrorKind::UnexpectedEof,
        "unexpected EOF",
      ));
    }
    let file_offset =
      self.fs_root.start_file_offset + file_offset_and_len.offset;
    let start = file_offset + pos;
    let end = file_offset + std::cmp::min(pos + len, file_offset_and_len.len);
    Ok(start as usize..end as usize)
  }

  pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> {
    let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
    match entry {
      VfsEntryRef::Dir(dir) => Ok(dir),
      VfsEntryRef::Symlink(_) => unreachable!(),
      VfsEntryRef::File(_) => Err(std::io::Error::other("path is a file")),
    }
  }

  pub fn file_entry(&self, path: &Path) -> std::io::Result<&VirtualFile> {
    let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
    match entry {
      VfsEntryRef::Dir(_) => Err(std::io::Error::other("path is a directory")),
      VfsEntryRef::Symlink(_) => unreachable!(),
      VfsEntryRef::File(file) => Ok(file),
    }
  }
}

impl DenoRtNativeAddonLoader for FileBackedVfs {
  fn load_if_in_vfs(&self, path: &Path) -> Option<Cow<'static, [u8]>> {
    if !self.is_path_within(path) {
      return None;
    }
    let file = self.file_entry(path).ok()?;
    self.read_file_offset_with_len(file.offset).ok()
  }
}

#[cfg(test)]
mod test {
  use std::io::Write;

  use deno_lib::standalone::virtual_fs::VfsBuilder;
  use test_util::TempDir;
  use test_util::assert_contains;

  use super::*;

  #[track_caller]
  fn read_file(vfs: &FileBackedVfs, path: &Path) -> String {
    let file = vfs.file_entry(path).unwrap();
    String::from_utf8(vfs.read_file_all(file).unwrap().into_owned()).unwrap()
  }

  #[test]
  fn builds_and_uses_virtual_fs() {
    let temp_dir = TempDir::new();
    // we canonicalize the temp directory because the vfs builder
    // will canonicalize the root path
    let src_path = temp_dir.path().canonicalize().join("src");
    src_path.create_dir_all();
    src_path.join("sub_dir").create_dir_all();
    src_path.join("e.txt").write("e");
    src_path.symlink_file("e.txt", "sub_dir/e.txt");
    let src_path = src_path.to_path_buf();
    let mut builder = VfsBuilder::new();
    builder
      .add_file_with_data_raw_for_testing(
        &src_path.join("a.txt"),
        "data".into(),
        None,
      )
      .unwrap();
    builder
      .add_file_with_data_raw_for_testing(
        &src_path.join("b.txt"),
        "data".into(),
        Some(
          SystemTime::UNIX_EPOCH
            .checked_add(Duration::from_secs(2))
            .unwrap(),
        ),
      )
      .unwrap();
    assert_eq!(builder.files_len(), 1); // because duplicate data
    builder
      .add_file_with_data_raw_for_testing(
        &src_path.join("c.txt"),
        "c".into(),
        None,
      )
      .unwrap();
    builder
      .add_file_with_data_raw_for_testing(
        &src_path.join("sub_dir").join("d.txt"),
        "d".into(),
        None,
      )
      .unwrap();
    builder.add_path(&src_path.join("e.txt")).unwrap();
    builder
      .add_symlink(&src_path.join("sub_dir").join("e.txt"))
      .unwrap();

    // get the virtual fs
    let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir);

    assert_eq!(read_file(&virtual_fs, &dest_path.join("a.txt")), "data");
    assert_eq!(read_file(&virtual_fs, &dest_path.join("b.txt")), "data");

    // attempt reading a symlink
    assert_eq!(
      read_file(&virtual_fs, &dest_path.join("sub_dir").join("e.txt")),
      "e",
    );

    // canonicalize symlink
    assert_eq!(
      virtual_fs
        .canonicalize(&dest_path.join("sub_dir").join("e.txt"))
        .unwrap(),
      dest_path.join("e.txt"),
    );

    // metadata
    assert_eq!(
      virtual_fs
        .lstat(&dest_path.join("sub_dir").join("e.txt"))
        .unwrap()
        .file_type,
      sys_traits::FileType::Symlink,
    );
    assert_eq!(
      virtual_fs.lstat(&dest_path.join("b.txt")).unwrap().mtime,
      Some(2_000),
    );
    assert_eq!(
      virtual_fs
        .stat(&dest_path.join("sub_dir").join("e.txt"))
        .unwrap()
        .file_type,
      sys_traits::FileType::File,
    );
    assert_eq!(
      virtual_fs
        .stat(&dest_path.join("sub_dir"))
        .unwrap()
        .file_type,
      sys_traits::FileType::Dir,
    );
    assert_eq!(
      virtual_fs.stat(&dest_path.join("e.txt")).unwrap().file_type,
      sys_traits::FileType::File
    );
  }

  #[test]
  fn test_include_dir_recursive() {
    let temp_dir = TempDir::new();
    let temp_dir_path = temp_dir.path().canonicalize();
    temp_dir.create_dir_all("src/nested/sub_dir");
    temp_dir.write("src/a.txt", "data");
    temp_dir.write("src/b.txt", "data");
    temp_dir.path().symlink_dir(
      temp_dir_path.join("src/nested/sub_dir"),
      temp_dir_path.join("src/sub_dir_link"),
    );
    temp_dir.write("src/nested/sub_dir/c.txt", "c");

    // build and create the virtual fs
    let src_path = temp_dir_path.join("src").to_path_buf();
    let mut builder = VfsBuilder::new();
    builder.add_dir_recursive(&src_path).unwrap();
    let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir);

    assert_eq!(read_file(&virtual_fs, &dest_path.join("a.txt")), "data",);
    assert_eq!(read_file(&virtual_fs, &dest_path.join("b.txt")), "data",);

    assert_eq!(
      read_file(
        &virtual_fs,
        &dest_path.join("nested").join("sub_dir").join("c.txt")
      ),
      "c",
    );
    assert_eq!(
      read_file(&virtual_fs, &dest_path.join("sub_dir_link").join("c.txt")),
      "c",
    );
    assert_eq!(
      virtual_fs
        .lstat(&dest_path.join("sub_dir_link"))
        .unwrap()
        .file_type,
      sys_traits::FileType::Symlink,
    );

    assert_eq!(
      virtual_fs
        .canonicalize(&dest_path.join("sub_dir_link").join("c.txt"))
        .unwrap(),
      dest_path.join("nested").join("sub_dir").join("c.txt"),
    );
  }

  fn into_virtual_fs(
    builder: VfsBuilder,
    temp_dir: &TempDir,
  ) -> (PathBuf, FileBackedVfs) {
    let virtual_fs_file = temp_dir.path().join("virtual_fs");
    let vfs = builder.build();
    {
      let mut file = std::fs::File::create(&virtual_fs_file).unwrap();
      for file_data in &vfs.files {
        file.write_all(file_data).unwrap();
      }
    }
    let dest_path = temp_dir.path().join("dest");
    let data = std::fs::read(&virtual_fs_file).unwrap();
    (
      dest_path.to_path_buf(),
      FileBackedVfs::new(
        Cow::Owned(data),
        VfsRoot {
          dir: VirtualDirectory {
            name: "".to_string(),
            entries: vfs.entries,
          },
          root_path: dest_path.to_path_buf(),
          start_file_offset: 0,
        },
        FileSystemCaseSensitivity::Sensitive,
      ),
    )
  }

  #[test]
  fn circular_symlink() {
    let temp_dir = TempDir::new();
    let src_path = temp_dir.path().canonicalize().join("src");
    src_path.create_dir_all();
    src_path.symlink_file("a.txt", "b.txt");
    src_path.symlink_file("b.txt", "c.txt");
    src_path.symlink_file("c.txt", "a.txt");
    let src_path = src_path.to_path_buf();
    let mut builder = VfsBuilder::new();
    let err = builder
      .add_symlink(src_path.join("a.txt").as_path())
      .unwrap_err();
    assert_contains!(err.to_string(), "Circular symlink detected",);
  }

  #[tokio::test]
  async fn test_open_file() {
    let temp_dir = TempDir::new();
    let temp_path = temp_dir.path().canonicalize();
    let mut builder = VfsBuilder::new();
    builder
      .add_file_with_data_raw_for_testing(
        temp_path.join("a.txt").as_path(),
        "0123456789".to_string().into_bytes(),
        None,
      )
      .unwrap();
    let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir);
    let virtual_fs = Arc::new(virtual_fs);
    let file = virtual_fs.open_file(&dest_path.join("a.txt")).unwrap();
    file.seek(SeekFrom::Current(2)).unwrap();
    let mut buf = vec![0; 2];
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"23");
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"45");
    file.seek(SeekFrom::Current(-4)).unwrap();
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"23");
    file.seek(SeekFrom::Start(2)).unwrap();
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"23");
    file.seek(SeekFrom::End(2)).unwrap();
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"89");
    file.seek(SeekFrom::Current(-8)).unwrap();
    file.read_to_buf(&mut buf).unwrap();
    assert_eq!(buf, b"23");
    assert_eq!(
      file.seek(SeekFrom::Current(-5)).unwrap_err().to_string(),
      "An attempt was made to move the file pointer before the beginning of the file."
    );
    // go beyond the file length, then back
    file.seek(SeekFrom::Current(40)).unwrap();
    file.seek(SeekFrom::Current(-38)).unwrap();
    let file = Rc::new(file);
    let read_buf = file.clone().read(2).await.unwrap();
    assert_eq!(read_buf.to_vec(), b"67");
    file.clone().seek_sync(SeekFrom::Current(-2)).unwrap();

    // read to the end of the file
    let all_buf = file.clone().read_all_sync().unwrap();
    assert_eq!(all_buf.to_vec(), b"6789");
    file.clone().seek_sync(SeekFrom::Current(-9)).unwrap();

    // try try_clone_inner and read_all_async
    let all_buf = file
      .try_clone_inner()
      .unwrap()
      .read_all_async()
      .await
      .unwrap();
    assert_eq!(all_buf.to_vec(), b"123456789");
  }
}
