summaryrefslogtreecommitdiff
path: root/runtime/ops/fs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops/fs.rs')
-rw-r--r--runtime/ops/fs.rs1702
1 files changed, 1702 insertions, 0 deletions
diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs
new file mode 100644
index 000000000..865c5bcca
--- /dev/null
+++ b/runtime/ops/fs.rs
@@ -0,0 +1,1702 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+// Some deserializer fields are only used on Unix and Windows build fails without it
+use super::io::std_file_resource;
+use super::io::{FileMetadata, StreamResource, StreamResourceHolder};
+use crate::fs_util::canonicalize_path;
+use crate::permissions::Permissions;
+use deno_core::error::custom_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::serde_json;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::BufVec;
+use deno_core::OpState;
+use deno_core::ZeroCopyBuf;
+use deno_crypto::rand::thread_rng;
+use deno_crypto::rand::Rng;
+use serde::Deserialize;
+use std::cell::RefCell;
+use std::convert::From;
+use std::env::{current_dir, set_current_dir, temp_dir};
+use std::io;
+use std::io::{Seek, SeekFrom};
+use std::path::{Path, PathBuf};
+use std::rc::Rc;
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+#[cfg(not(unix))]
+use deno_core::error::generic_error;
+#[cfg(not(unix))]
+use deno_core::error::not_supported;
+
+pub fn init(rt: &mut deno_core::JsRuntime) {
+ super::reg_json_sync(rt, "op_open_sync", op_open_sync);
+ super::reg_json_async(rt, "op_open_async", op_open_async);
+
+ super::reg_json_sync(rt, "op_seek_sync", op_seek_sync);
+ super::reg_json_async(rt, "op_seek_async", op_seek_async);
+
+ super::reg_json_sync(rt, "op_fdatasync_sync", op_fdatasync_sync);
+ super::reg_json_async(rt, "op_fdatasync_async", op_fdatasync_async);
+
+ super::reg_json_sync(rt, "op_fsync_sync", op_fsync_sync);
+ super::reg_json_async(rt, "op_fsync_async", op_fsync_async);
+
+ super::reg_json_sync(rt, "op_fstat_sync", op_fstat_sync);
+ super::reg_json_async(rt, "op_fstat_async", op_fstat_async);
+
+ super::reg_json_sync(rt, "op_umask", op_umask);
+ super::reg_json_sync(rt, "op_chdir", op_chdir);
+
+ super::reg_json_sync(rt, "op_mkdir_sync", op_mkdir_sync);
+ super::reg_json_async(rt, "op_mkdir_async", op_mkdir_async);
+
+ super::reg_json_sync(rt, "op_chmod_sync", op_chmod_sync);
+ super::reg_json_async(rt, "op_chmod_async", op_chmod_async);
+
+ super::reg_json_sync(rt, "op_chown_sync", op_chown_sync);
+ super::reg_json_async(rt, "op_chown_async", op_chown_async);
+
+ super::reg_json_sync(rt, "op_remove_sync", op_remove_sync);
+ super::reg_json_async(rt, "op_remove_async", op_remove_async);
+
+ super::reg_json_sync(rt, "op_copy_file_sync", op_copy_file_sync);
+ super::reg_json_async(rt, "op_copy_file_async", op_copy_file_async);
+
+ super::reg_json_sync(rt, "op_stat_sync", op_stat_sync);
+ super::reg_json_async(rt, "op_stat_async", op_stat_async);
+
+ super::reg_json_sync(rt, "op_realpath_sync", op_realpath_sync);
+ super::reg_json_async(rt, "op_realpath_async", op_realpath_async);
+
+ super::reg_json_sync(rt, "op_read_dir_sync", op_read_dir_sync);
+ super::reg_json_async(rt, "op_read_dir_async", op_read_dir_async);
+
+ super::reg_json_sync(rt, "op_rename_sync", op_rename_sync);
+ super::reg_json_async(rt, "op_rename_async", op_rename_async);
+
+ super::reg_json_sync(rt, "op_link_sync", op_link_sync);
+ super::reg_json_async(rt, "op_link_async", op_link_async);
+
+ super::reg_json_sync(rt, "op_symlink_sync", op_symlink_sync);
+ super::reg_json_async(rt, "op_symlink_async", op_symlink_async);
+
+ super::reg_json_sync(rt, "op_read_link_sync", op_read_link_sync);
+ super::reg_json_async(rt, "op_read_link_async", op_read_link_async);
+
+ super::reg_json_sync(rt, "op_ftruncate_sync", op_ftruncate_sync);
+ super::reg_json_async(rt, "op_ftruncate_async", op_ftruncate_async);
+
+ super::reg_json_sync(rt, "op_truncate_sync", op_truncate_sync);
+ super::reg_json_async(rt, "op_truncate_async", op_truncate_async);
+
+ super::reg_json_sync(rt, "op_make_temp_dir_sync", op_make_temp_dir_sync);
+ super::reg_json_async(rt, "op_make_temp_dir_async", op_make_temp_dir_async);
+
+ super::reg_json_sync(rt, "op_make_temp_file_sync", op_make_temp_file_sync);
+ super::reg_json_async(rt, "op_make_temp_file_async", op_make_temp_file_async);
+
+ super::reg_json_sync(rt, "op_cwd", op_cwd);
+
+ super::reg_json_sync(rt, "op_futime_sync", op_futime_sync);
+ super::reg_json_async(rt, "op_futime_async", op_futime_async);
+
+ super::reg_json_sync(rt, "op_utime_sync", op_utime_sync);
+ super::reg_json_async(rt, "op_utime_async", op_utime_async);
+}
+
+fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
+ s.into_string().map_err(|s| {
+ let message = format!("File name or path {:?} is not valid UTF-8", s);
+ custom_error("InvalidData", message)
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct OpenArgs {
+ path: String,
+ mode: Option<u32>,
+ options: OpenOptions,
+}
+
+#[derive(Deserialize, Default, Debug)]
+#[serde(rename_all = "camelCase")]
+#[serde(default)]
+struct OpenOptions {
+ read: bool,
+ write: bool,
+ create: bool,
+ truncate: bool,
+ append: bool,
+ create_new: bool,
+}
+
+fn open_helper(
+ state: &mut OpState,
+ args: Value,
+) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> {
+ let args: OpenArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+
+ let mut open_options = std::fs::OpenOptions::new();
+
+ if let Some(mode) = args.mode {
+ // mode only used if creating the file on Unix
+ // if not specified, defaults to 0o666
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::OpenOptionsExt;
+ open_options.mode(mode & 0o777);
+ }
+ #[cfg(not(unix))]
+ let _ = mode; // avoid unused warning
+ }
+
+ let permissions = state.borrow::<Permissions>();
+ let options = args.options;
+
+ if options.read {
+ permissions.check_read(&path)?;
+ }
+
+ if options.write || options.append {
+ permissions.check_write(&path)?;
+ }
+
+ open_options
+ .read(options.read)
+ .create(options.create)
+ .write(options.write)
+ .truncate(options.truncate)
+ .append(options.append)
+ .create_new(options.create_new);
+
+ Ok((path, open_options))
+}
+
+fn op_open_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let (path, open_options) = open_helper(state, args)?;
+ let std_file = open_options.open(path)?;
+ let tokio_file = tokio::fs::File::from_std(std_file);
+ let rid = state.resource_table.add(
+ "fsFile",
+ Box::new(StreamResourceHolder::new(StreamResource::FsFile(Some((
+ tokio_file,
+ FileMetadata::default(),
+ ))))),
+ );
+ Ok(json!(rid))
+}
+
+async fn op_open_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let (path, open_options) = open_helper(&mut state.borrow_mut(), args)?;
+ let tokio_file = tokio::fs::OpenOptions::from(open_options)
+ .open(path)
+ .await?;
+ let rid = state.borrow_mut().resource_table.add(
+ "fsFile",
+ Box::new(StreamResourceHolder::new(StreamResource::FsFile(Some((
+ tokio_file,
+ FileMetadata::default(),
+ ))))),
+ );
+ Ok(json!(rid))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct SeekArgs {
+ rid: i32,
+ offset: i64,
+ whence: i32,
+}
+
+fn seek_helper(args: Value) -> Result<(u32, SeekFrom), AnyError> {
+ let args: SeekArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let offset = args.offset;
+ let whence = args.whence as u32;
+ // Translate seek mode to Rust repr.
+ let seek_from = match whence {
+ 0 => SeekFrom::Start(offset as u64),
+ 1 => SeekFrom::Current(offset),
+ 2 => SeekFrom::End(offset),
+ _ => {
+ return Err(type_error(format!("Invalid seek mode: {}", whence)));
+ }
+ };
+
+ Ok((rid, seek_from))
+}
+
+fn op_seek_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let (rid, seek_from) = seek_helper(args)?;
+ let pos = std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => std_file.seek(seek_from).map_err(AnyError::from),
+ Err(_) => Err(type_error(
+ "cannot seek on this type of resource".to_string(),
+ )),
+ })?;
+ Ok(json!(pos))
+}
+
+async fn op_seek_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let (rid, seek_from) = seek_helper(args)?;
+ // TODO(ry) This is a fake async op. We need to use poll_fn,
+ // tokio::fs::File::start_seek and tokio::fs::File::poll_complete
+ let pos = std_file_resource(&mut state.borrow_mut(), rid, |r| match r {
+ Ok(std_file) => std_file.seek(seek_from).map_err(AnyError::from),
+ Err(_) => Err(type_error(
+ "cannot seek on this type of resource".to_string(),
+ )),
+ })?;
+ Ok(json!(pos))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct FdatasyncArgs {
+ rid: i32,
+}
+
+fn op_fdatasync_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: FdatasyncArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => std_file.sync_data().map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot sync this type of resource".to_string())),
+ })?;
+ Ok(json!({}))
+}
+
+async fn op_fdatasync_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: FdatasyncArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ std_file_resource(&mut state.borrow_mut(), rid, |r| match r {
+ Ok(std_file) => std_file.sync_data().map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot sync this type of resource".to_string())),
+ })?;
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct FsyncArgs {
+ rid: i32,
+}
+
+fn op_fsync_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: FsyncArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => std_file.sync_all().map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot sync this type of resource".to_string())),
+ })?;
+ Ok(json!({}))
+}
+
+async fn op_fsync_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: FsyncArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ std_file_resource(&mut state.borrow_mut(), rid, |r| match r {
+ Ok(std_file) => std_file.sync_all().map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot sync this type of resource".to_string())),
+ })?;
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct FstatArgs {
+ rid: i32,
+}
+
+fn op_fstat_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.fstat");
+ let args: FstatArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let metadata = std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => std_file.metadata().map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot stat this type of resource".to_string())),
+ })?;
+ Ok(get_stat_json(metadata))
+}
+
+async fn op_fstat_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ super::check_unstable2(&state, "Deno.fstat");
+
+ let args: FstatArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let metadata =
+ std_file_resource(&mut state.borrow_mut(), rid, |r| match r {
+ Ok(std_file) => std_file.metadata().map_err(AnyError::from),
+ Err(_) => {
+ Err(type_error("cannot stat this type of resource".to_string()))
+ }
+ })?;
+ Ok(get_stat_json(metadata))
+}
+
+#[derive(Deserialize)]
+struct UmaskArgs {
+ mask: Option<u32>,
+}
+
+fn op_umask(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.umask");
+ let args: UmaskArgs = serde_json::from_value(args)?;
+ // TODO implement umask for Windows
+ // see https://github.com/nodejs/node/blob/master/src/node_process_methods.cc
+ // and https://docs.microsoft.com/fr-fr/cpp/c-runtime-library/reference/umask?view=vs-2019
+ #[cfg(not(unix))]
+ {
+ let _ = args.mask; // avoid unused warning.
+ Err(not_supported())
+ }
+ #[cfg(unix)]
+ {
+ use nix::sys::stat::mode_t;
+ use nix::sys::stat::umask;
+ use nix::sys::stat::Mode;
+ let r = if let Some(mask) = args.mask {
+ // If mask provided, return previous.
+ umask(Mode::from_bits_truncate(mask as mode_t))
+ } else {
+ // If no mask provided, we query the current. Requires two syscalls.
+ let prev = umask(Mode::from_bits_truncate(0o777));
+ let _ = umask(prev);
+ prev
+ };
+ Ok(json!(r.bits() as u32))
+ }
+}
+
+#[derive(Deserialize)]
+struct ChdirArgs {
+ directory: String,
+}
+
+fn op_chdir(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: ChdirArgs = serde_json::from_value(args)?;
+ let d = PathBuf::from(&args.directory);
+ state.borrow::<Permissions>().check_read(&d)?;
+ set_current_dir(&d)?;
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MkdirArgs {
+ path: String,
+ recursive: bool,
+ mode: Option<u32>,
+}
+
+fn op_mkdir_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: MkdirArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+ let mode = args.mode.unwrap_or(0o777) & 0o777;
+ state.borrow::<Permissions>().check_write(&path)?;
+ debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
+ let mut builder = std::fs::DirBuilder::new();
+ builder.recursive(args.recursive);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::DirBuilderExt;
+ builder.mode(mode);
+ }
+ builder.create(path)?;
+ Ok(json!({}))
+}
+
+async fn op_mkdir_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: MkdirArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+ let mode = args.mode.unwrap_or(0o777) & 0o777;
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&path)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
+ let mut builder = std::fs::DirBuilder::new();
+ builder.recursive(args.recursive);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::DirBuilderExt;
+ builder.mode(mode);
+ }
+ builder.create(path)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ChmodArgs {
+ path: String,
+ mode: u32,
+}
+
+fn op_chmod_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: ChmodArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+ let mode = args.mode & 0o777;
+
+ state.borrow::<Permissions>().check_write(&path)?;
+ debug!("op_chmod_sync {} {:o}", path.display(), mode);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let permissions = PermissionsExt::from_mode(mode);
+ std::fs::set_permissions(&path, permissions)?;
+ Ok(json!({}))
+ }
+ // TODO Implement chmod for Windows (#4357)
+ #[cfg(not(unix))]
+ {
+ // Still check file/dir exists on Windows
+ let _metadata = std::fs::metadata(&path)?;
+ Err(generic_error("Not implemented"))
+ }
+}
+
+async fn op_chmod_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: ChmodArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+ let mode = args.mode & 0o777;
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&path)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_chmod_async {} {:o}", path.display(), mode);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let permissions = PermissionsExt::from_mode(mode);
+ std::fs::set_permissions(&path, permissions)?;
+ Ok(json!({}))
+ }
+ // TODO Implement chmod for Windows (#4357)
+ #[cfg(not(unix))]
+ {
+ // Still check file/dir exists on Windows
+ let _metadata = std::fs::metadata(&path)?;
+ Err(not_supported())
+ }
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ChownArgs {
+ path: String,
+ uid: Option<u32>,
+ gid: Option<u32>,
+}
+
+fn op_chown_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: ChownArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+ state.borrow::<Permissions>().check_write(&path)?;
+ debug!(
+ "op_chown_sync {} {:?} {:?}",
+ path.display(),
+ args.uid,
+ args.gid,
+ );
+ #[cfg(unix)]
+ {
+ use nix::unistd::{chown, Gid, Uid};
+ let nix_uid = args.uid.map(Uid::from_raw);
+ let nix_gid = args.gid.map(Gid::from_raw);
+ chown(&path, nix_uid, nix_gid)?;
+ Ok(json!({}))
+ }
+ // TODO Implement chown for Windows
+ #[cfg(not(unix))]
+ {
+ Err(generic_error("Not implemented"))
+ }
+}
+
+async fn op_chown_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: ChownArgs = serde_json::from_value(args)?;
+ let path = Path::new(&args.path).to_path_buf();
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&path)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!(
+ "op_chown_async {} {:?} {:?}",
+ path.display(),
+ args.uid,
+ args.gid,
+ );
+ #[cfg(unix)]
+ {
+ use nix::unistd::{chown, Gid, Uid};
+ let nix_uid = args.uid.map(Uid::from_raw);
+ let nix_gid = args.gid.map(Gid::from_raw);
+ chown(&path, nix_uid, nix_gid)?;
+ Ok(json!({}))
+ }
+ // TODO Implement chown for Windows
+ #[cfg(not(unix))]
+ Err(not_supported())
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RemoveArgs {
+ path: String,
+ recursive: bool,
+}
+
+fn op_remove_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: RemoveArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let recursive = args.recursive;
+
+ state.borrow::<Permissions>().check_write(&path)?;
+
+ #[cfg(not(unix))]
+ use std::os::windows::prelude::MetadataExt;
+
+ let metadata = std::fs::symlink_metadata(&path)?;
+
+ debug!("op_remove_sync {} {}", path.display(), recursive);
+ let file_type = metadata.file_type();
+ if file_type.is_file() {
+ std::fs::remove_file(&path)?;
+ } else if recursive {
+ std::fs::remove_dir_all(&path)?;
+ } else if file_type.is_symlink() {
+ #[cfg(unix)]
+ std::fs::remove_file(&path)?;
+ #[cfg(not(unix))]
+ {
+ use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
+ if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
+ std::fs::remove_dir(&path)?;
+ } else {
+ std::fs::remove_file(&path)?;
+ }
+ }
+ } else if file_type.is_dir() {
+ std::fs::remove_dir(&path)?;
+ } else {
+ // pipes, sockets, etc...
+ std::fs::remove_file(&path)?;
+ }
+ Ok(json!({}))
+}
+
+async fn op_remove_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: RemoveArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let recursive = args.recursive;
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&path)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ #[cfg(not(unix))]
+ use std::os::windows::prelude::MetadataExt;
+
+ let metadata = std::fs::symlink_metadata(&path)?;
+
+ debug!("op_remove_async {} {}", path.display(), recursive);
+ let file_type = metadata.file_type();
+ if file_type.is_file() {
+ std::fs::remove_file(&path)?;
+ } else if recursive {
+ std::fs::remove_dir_all(&path)?;
+ } else if file_type.is_symlink() {
+ #[cfg(unix)]
+ std::fs::remove_file(&path)?;
+ #[cfg(not(unix))]
+ {
+ use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
+ if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
+ std::fs::remove_dir(&path)?;
+ } else {
+ std::fs::remove_file(&path)?;
+ }
+ }
+ } else if file_type.is_dir() {
+ std::fs::remove_dir(&path)?;
+ } else {
+ // pipes, sockets, etc...
+ std::fs::remove_file(&path)?;
+ }
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct CopyFileArgs {
+ from: String,
+ to: String,
+}
+
+fn op_copy_file_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: CopyFileArgs = serde_json::from_value(args)?;
+ let from = PathBuf::from(&args.from);
+ let to = PathBuf::from(&args.to);
+
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&from)?;
+ permissions.check_write(&to)?;
+
+ debug!("op_copy_file_sync {} {}", from.display(), to.display());
+ // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
+ // See https://github.com/rust-lang/rust/issues/54800
+ // Once the issue is resolved, we should remove this workaround.
+ if cfg!(unix) && !from.is_file() {
+ return Err(custom_error("NotFound", "File not found"));
+ }
+
+ // returns size of from as u64 (we ignore)
+ std::fs::copy(&from, &to)?;
+ Ok(json!({}))
+}
+
+async fn op_copy_file_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: CopyFileArgs = serde_json::from_value(args)?;
+ let from = PathBuf::from(&args.from);
+ let to = PathBuf::from(&args.to);
+
+ {
+ let state = state.borrow();
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&from)?;
+ permissions.check_write(&to)?;
+ }
+
+ debug!("op_copy_file_async {} {}", from.display(), to.display());
+ tokio::task::spawn_blocking(move || {
+ // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
+ // See https://github.com/rust-lang/rust/issues/54800
+ // Once the issue is resolved, we should remove this workaround.
+ if cfg!(unix) && !from.is_file() {
+ return Err(custom_error("NotFound", "File not found"));
+ }
+
+ // returns size of from as u64 (we ignore)
+ std::fs::copy(&from, &to)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> Value {
+ match maybe_time {
+ Ok(time) => {
+ let msec = time
+ .duration_since(UNIX_EPOCH)
+ .map(|t| t.as_secs_f64() * 1000f64)
+ .unwrap_or_else(|err| err.duration().as_secs_f64() * -1000f64);
+ serde_json::Number::from_f64(msec)
+ .map(Value::Number)
+ .unwrap_or(Value::Null)
+ }
+ Err(_) => Value::Null,
+ }
+}
+
+#[inline(always)]
+fn get_stat_json(metadata: std::fs::Metadata) -> Value {
+ // Unix stat member (number types only). 0 if not on unix.
+ macro_rules! usm {
+ ($member:ident) => {{
+ #[cfg(unix)]
+ {
+ metadata.$member()
+ }
+ #[cfg(not(unix))]
+ {
+ 0
+ }
+ }};
+ }
+
+ #[cfg(unix)]
+ use std::os::unix::fs::MetadataExt;
+ let json_val = json!({
+ "isFile": metadata.is_file(),
+ "isDirectory": metadata.is_dir(),
+ "isSymlink": metadata.file_type().is_symlink(),
+ "size": metadata.len(),
+ // In milliseconds, like JavaScript. Available on both Unix or Windows.
+ "mtime": to_msec(metadata.modified()),
+ "atime": to_msec(metadata.accessed()),
+ "birthtime": to_msec(metadata.created()),
+ // Following are only valid under Unix.
+ "dev": usm!(dev),
+ "ino": usm!(ino),
+ "mode": usm!(mode),
+ "nlink": usm!(nlink),
+ "uid": usm!(uid),
+ "gid": usm!(gid),
+ "rdev": usm!(rdev),
+ // TODO(kevinkassimo): *time_nsec requires BigInt.
+ // Probably should be treated as String if we need to add them.
+ "blksize": usm!(blksize),
+ "blocks": usm!(blocks),
+ });
+ json_val
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct StatArgs {
+ path: String,
+ lstat: bool,
+}
+
+fn op_stat_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: StatArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let lstat = args.lstat;
+ state.borrow::<Permissions>().check_read(&path)?;
+ debug!("op_stat_sync {} {}", path.display(), lstat);
+ let metadata = if lstat {
+ std::fs::symlink_metadata(&path)?
+ } else {
+ std::fs::metadata(&path)?
+ };
+ Ok(get_stat_json(metadata))
+}
+
+async fn op_stat_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: StatArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let lstat = args.lstat;
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_read(&path)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_stat_async {} {}", path.display(), lstat);
+ let metadata = if lstat {
+ std::fs::symlink_metadata(&path)?
+ } else {
+ std::fs::metadata(&path)?
+ };
+ Ok(get_stat_json(metadata))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RealpathArgs {
+ path: String,
+}
+
+fn op_realpath_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: RealpathArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&path)?;
+ if path.is_relative() {
+ permissions.check_read_blind(&current_dir()?, "CWD")?;
+ }
+
+ debug!("op_realpath_sync {}", path.display());
+ // corresponds to the realpath on Unix and
+ // CreateFile and GetFinalPathNameByHandle on Windows
+ let realpath = canonicalize_path(&path)?;
+ let realpath_str = into_string(realpath.into_os_string())?;
+ Ok(json!(realpath_str))
+}
+
+async fn op_realpath_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: RealpathArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+
+ {
+ let state = state.borrow();
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&path)?;
+ if path.is_relative() {
+ permissions.check_read_blind(&current_dir()?, "CWD")?;
+ }
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_realpath_async {}", path.display());
+ // corresponds to the realpath on Unix and
+ // CreateFile and GetFinalPathNameByHandle on Windows
+ let realpath = canonicalize_path(&path)?;
+ let realpath_str = into_string(realpath.into_os_string())?;
+ Ok(json!(realpath_str))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ReadDirArgs {
+ path: String,
+}
+
+fn op_read_dir_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: ReadDirArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+
+ state.borrow::<Permissions>().check_read(&path)?;
+
+ debug!("op_read_dir_sync {}", path.display());
+ let entries: Vec<_> = std::fs::read_dir(path)?
+ .filter_map(|entry| {
+ let entry = entry.unwrap();
+ let file_type = entry.file_type().unwrap();
+ // Not all filenames can be encoded as UTF-8. Skip those for now.
+ if let Ok(name) = into_string(entry.file_name()) {
+ Some(json!({
+ "name": name,
+ "isFile": file_type.is_file(),
+ "isDirectory": file_type.is_dir(),
+ "isSymlink": file_type.is_symlink()
+ }))
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ Ok(json!({ "entries": entries }))
+}
+
+async fn op_read_dir_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: ReadDirArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_read(&path)?;
+ }
+ tokio::task::spawn_blocking(move || {
+ debug!("op_read_dir_async {}", path.display());
+ let entries: Vec<_> = std::fs::read_dir(path)?
+ .filter_map(|entry| {
+ let entry = entry.unwrap();
+ let file_type = entry.file_type().unwrap();
+ // Not all filenames can be encoded as UTF-8. Skip those for now.
+ if let Ok(name) = into_string(entry.file_name()) {
+ Some(json!({
+ "name": name,
+ "isFile": file_type.is_file(),
+ "isDirectory": file_type.is_dir(),
+ "isSymlink": file_type.is_symlink()
+ }))
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ Ok(json!({ "entries": entries }))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RenameArgs {
+ oldpath: String,
+ newpath: String,
+}
+
+fn op_rename_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: RenameArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&oldpath)?;
+ permissions.check_write(&oldpath)?;
+ permissions.check_write(&newpath)?;
+ debug!("op_rename_sync {} {}", oldpath.display(), newpath.display());
+ std::fs::rename(&oldpath, &newpath)?;
+ Ok(json!({}))
+}
+
+async fn op_rename_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: RenameArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+ {
+ let state = state.borrow();
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&oldpath)?;
+ permissions.check_write(&oldpath)?;
+ permissions.check_write(&newpath)?;
+ }
+ tokio::task::spawn_blocking(move || {
+ debug!(
+ "op_rename_async {} {}",
+ oldpath.display(),
+ newpath.display()
+ );
+ std::fs::rename(&oldpath, &newpath)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct LinkArgs {
+ oldpath: String,
+ newpath: String,
+}
+
+fn op_link_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.link");
+ let args: LinkArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&oldpath)?;
+ permissions.check_write(&newpath)?;
+
+ debug!("op_link_sync {} {}", oldpath.display(), newpath.display());
+ std::fs::hard_link(&oldpath, &newpath)?;
+ Ok(json!({}))
+}
+
+async fn op_link_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ super::check_unstable2(&state, "Deno.link");
+
+ let args: LinkArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+
+ {
+ let state = state.borrow();
+ let permissions = state.borrow::<Permissions>();
+ permissions.check_read(&oldpath)?;
+ permissions.check_write(&newpath)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_link_async {} {}", oldpath.display(), newpath.display());
+ std::fs::hard_link(&oldpath, &newpath)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct SymlinkArgs {
+ oldpath: String,
+ newpath: String,
+ #[cfg(not(unix))]
+ options: Option<SymlinkOptions>,
+}
+
+#[cfg(not(unix))]
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct SymlinkOptions {
+ _type: String,
+}
+
+fn op_symlink_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.symlink");
+ let args: SymlinkArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+
+ state.borrow::<Permissions>().check_write(&newpath)?;
+
+ debug!(
+ "op_symlink_sync {} {}",
+ oldpath.display(),
+ newpath.display()
+ );
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::symlink;
+ symlink(&oldpath, &newpath)?;
+ Ok(json!({}))
+ }
+ #[cfg(not(unix))]
+ {
+ use std::os::windows::fs::{symlink_dir, symlink_file};
+
+ match args.options {
+ Some(options) => match options._type.as_ref() {
+ "file" => symlink_file(&oldpath, &newpath)?,
+ "dir" => symlink_dir(&oldpath, &newpath)?,
+ _ => return Err(type_error("unsupported type")),
+ },
+ None => {
+ let old_meta = std::fs::metadata(&oldpath);
+ match old_meta {
+ Ok(metadata) => {
+ if metadata.is_file() {
+ symlink_file(&oldpath, &newpath)?
+ } else if metadata.is_dir() {
+ symlink_dir(&oldpath, &newpath)?
+ }
+ }
+ Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
+ }
+ }
+ };
+ Ok(json!({}))
+ }
+}
+
+async fn op_symlink_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ super::check_unstable2(&state, "Deno.symlink");
+
+ let args: SymlinkArgs = serde_json::from_value(args)?;
+ let oldpath = PathBuf::from(&args.oldpath);
+ let newpath = PathBuf::from(&args.newpath);
+
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&newpath)?;
+ }
+
+ tokio::task::spawn_blocking(move || {
+ debug!("op_symlink_async {} {}", oldpath.display(), newpath.display());
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::symlink;
+ symlink(&oldpath, &newpath)?;
+ Ok(json!({}))
+ }
+ #[cfg(not(unix))]
+ {
+ use std::os::windows::fs::{symlink_dir, symlink_file};
+
+ match args.options {
+ Some(options) => match options._type.as_ref() {
+ "file" => symlink_file(&oldpath, &newpath)?,
+ "dir" => symlink_dir(&oldpath, &newpath)?,
+ _ => return Err(type_error("unsupported type")),
+ },
+ None => {
+ let old_meta = std::fs::metadata(&oldpath);
+ match old_meta {
+ Ok(metadata) => {
+ if metadata.is_file() {
+ symlink_file(&oldpath, &newpath)?
+ } else if metadata.is_dir() {
+ symlink_dir(&oldpath, &newpath)?
+ }
+ }
+ Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
+ }
+ }
+ };
+ Ok(json!({}))
+ }
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ReadLinkArgs {
+ path: String,
+}
+
+fn op_read_link_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: ReadLinkArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+
+ state.borrow::<Permissions>().check_read(&path)?;
+
+ debug!("op_read_link_value {}", path.display());
+ let target = std::fs::read_link(&path)?.into_os_string();
+ let targetstr = into_string(target)?;
+ Ok(json!(targetstr))
+}
+
+async fn op_read_link_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: ReadLinkArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_read(&path)?;
+ }
+ tokio::task::spawn_blocking(move || {
+ debug!("op_read_link_async {}", path.display());
+ let target = std::fs::read_link(&path)?.into_os_string();
+ let targetstr = into_string(target)?;
+ Ok(json!(targetstr))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct FtruncateArgs {
+ rid: i32,
+ len: i32,
+}
+
+fn op_ftruncate_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.ftruncate");
+ let args: FtruncateArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let len = args.len as u64;
+ std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => std_file.set_len(len).map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot truncate this type of resource")),
+ })?;
+ Ok(json!({}))
+}
+
+async fn op_ftruncate_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ super::check_unstable2(&state, "Deno.ftruncate");
+ let args: FtruncateArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let len = args.len as u64;
+ std_file_resource(&mut state.borrow_mut(), rid, |r| match r {
+ Ok(std_file) => std_file.set_len(len).map_err(AnyError::from),
+ Err(_) => Err(type_error("cannot truncate this type of resource")),
+ })?;
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct TruncateArgs {
+ path: String,
+ len: u64,
+}
+
+fn op_truncate_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: TruncateArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let len = args.len;
+
+ state.borrow::<Permissions>().check_write(&path)?;
+
+ debug!("op_truncate_sync {} {}", path.display(), len);
+ let f = std::fs::OpenOptions::new().write(true).open(&path)?;
+ f.set_len(len)?;
+ Ok(json!({}))
+}
+
+async fn op_truncate_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: TruncateArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let len = args.len;
+ {
+ let state = state.borrow();
+ state.borrow::<Permissions>().check_write(&path)?;
+ }
+ tokio::task::spawn_blocking(move || {
+ debug!("op_truncate_async {} {}", path.display(), len);
+ let f = std::fs::OpenOptions::new().write(true).open(&path)?;
+ f.set_len(len)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+fn make_temp(
+ dir: Option<&Path>,
+ prefix: Option<&str>,
+ suffix: Option<&str>,
+ is_dir: bool,
+) -> std::io::Result<PathBuf> {
+ let prefix_ = prefix.unwrap_or("");
+ let suffix_ = suffix.unwrap_or("");
+ let mut buf: PathBuf = match dir {
+ Some(ref p) => p.to_path_buf(),
+ None => temp_dir(),
+ }
+ .join("_");
+ let mut rng = thread_rng();
+ loop {
+ let unique = rng.gen::<u32>();
+ buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_));
+ let r = if is_dir {
+ #[allow(unused_mut)]
+ let mut builder = std::fs::DirBuilder::new();
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::DirBuilderExt;
+ builder.mode(0o700);
+ }
+ builder.create(buf.as_path())
+ } else {
+ let mut open_options = std::fs::OpenOptions::new();
+ open_options.write(true).create_new(true);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::OpenOptionsExt;
+ open_options.mode(0o600);
+ }
+ open_options.open(buf.as_path())?;
+ Ok(())
+ };
+ match r {
+ Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
+ Ok(_) => return Ok(buf),
+ Err(e) => return Err(e),
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MakeTempArgs {
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+}
+
+fn op_make_temp_dir_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: MakeTempArgs = serde_json::from_value(args)?;
+
+ let dir = args.dir.map(|s| PathBuf::from(&s));
+ let prefix = args.prefix.map(String::from);
+ let suffix = args.suffix.map(String::from);
+
+ state
+ .borrow::<Permissions>()
+ .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+
+ // TODO(piscisaureus): use byte vector for paths, not a string.
+ // See https://github.com/denoland/deno/issues/627.
+ // We can't assume that paths are always valid utf8 strings.
+ let path = make_temp(
+ // Converting Option<String> to Option<&str>
+ dir.as_deref(),
+ prefix.as_deref(),
+ suffix.as_deref(),
+ true,
+ )?;
+ let path_str = into_string(path.into_os_string())?;
+
+ Ok(json!(path_str))
+}
+
+async fn op_make_temp_dir_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: MakeTempArgs = serde_json::from_value(args)?;
+
+ let dir = args.dir.map(|s| PathBuf::from(&s));
+ let prefix = args.prefix.map(String::from);
+ let suffix = args.suffix.map(String::from);
+ {
+ let state = state.borrow();
+ state
+ .borrow::<Permissions>()
+ .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ }
+ tokio::task::spawn_blocking(move || {
+ // TODO(piscisaureus): use byte vector for paths, not a string.
+ // See https://github.com/denoland/deno/issues/627.
+ // We can't assume that paths are always valid utf8 strings.
+ let path = make_temp(
+ // Converting Option<String> to Option<&str>
+ dir.as_deref(),
+ prefix.as_deref(),
+ suffix.as_deref(),
+ true,
+ )?;
+ let path_str = into_string(path.into_os_string())?;
+
+ Ok(json!(path_str))
+ })
+ .await
+ .unwrap()
+}
+
+fn op_make_temp_file_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let args: MakeTempArgs = serde_json::from_value(args)?;
+
+ let dir = args.dir.map(|s| PathBuf::from(&s));
+ let prefix = args.prefix.map(String::from);
+ let suffix = args.suffix.map(String::from);
+
+ state
+ .borrow::<Permissions>()
+ .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+
+ // TODO(piscisaureus): use byte vector for paths, not a string.
+ // See https://github.com/denoland/deno/issues/627.
+ // We can't assume that paths are always valid utf8 strings.
+ let path = make_temp(
+ // Converting Option<String> to Option<&str>
+ dir.as_deref(),
+ prefix.as_deref(),
+ suffix.as_deref(),
+ false,
+ )?;
+ let path_str = into_string(path.into_os_string())?;
+
+ Ok(json!(path_str))
+}
+
+async fn op_make_temp_file_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let args: MakeTempArgs = serde_json::from_value(args)?;
+
+ let dir = args.dir.map(|s| PathBuf::from(&s));
+ let prefix = args.prefix.map(String::from);
+ let suffix = args.suffix.map(String::from);
+ {
+ let state = state.borrow();
+ state
+ .borrow::<Permissions>()
+ .check_write(dir.clone().unwrap_or_else(temp_dir).as_path())?;
+ }
+ tokio::task::spawn_blocking(move || {
+ // TODO(piscisaureus): use byte vector for paths, not a string.
+ // See https://github.com/denoland/deno/issues/627.
+ // We can't assume that paths are always valid utf8 strings.
+ let path = make_temp(
+ // Converting Option<String> to Option<&str>
+ dir.as_deref(),
+ prefix.as_deref(),
+ suffix.as_deref(),
+ false,
+ )?;
+ let path_str = into_string(path.into_os_string())?;
+
+ Ok(json!(path_str))
+ })
+ .await
+ .unwrap()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct FutimeArgs {
+ rid: i32,
+ atime: (i64, u32),
+ mtime: (i64, u32),
+}
+
+fn op_futime_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.futimeSync");
+ let args: FutimeArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
+ let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
+
+ std_file_resource(state, rid, |r| match r {
+ Ok(std_file) => {
+ filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))
+ .map_err(AnyError::from)
+ }
+ Err(_) => Err(type_error(
+ "cannot futime on this type of resource".to_string(),
+ )),
+ })?;
+
+ Ok(json!({}))
+}
+
+async fn op_futime_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let mut state = state.borrow_mut();
+ super::check_unstable(&state, "Deno.futime");
+ let args: FutimeArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+ let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
+ let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
+ // TODO Not actually async! https://github.com/denoland/deno/issues/7400
+ std_file_resource(&mut state, rid, |r| match r {
+ Ok(std_file) => {
+ filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))
+ .map_err(AnyError::from)
+ }
+ Err(_) => Err(type_error(
+ "cannot futime on this type of resource".to_string(),
+ )),
+ })?;
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct UtimeArgs {
+ path: String,
+ atime: (i64, u32),
+ mtime: (i64, u32),
+}
+
+fn op_utime_sync(
+ state: &mut OpState,
+ args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ super::check_unstable(state, "Deno.utime");
+
+ let args: UtimeArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
+ let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
+
+ state.borrow::<Permissions>().check_write(&path)?;
+ filetime::set_file_times(path, atime, mtime)?;
+ Ok(json!({}))
+}
+
+async fn op_utime_async(
+ state: Rc<RefCell<OpState>>,
+ args: Value,
+ _zero_copy: BufVec,
+) -> Result<Value, AnyError> {
+ let state = state.borrow();
+ super::check_unstable(&state, "Deno.utime");
+
+ let args: UtimeArgs = serde_json::from_value(args)?;
+ let path = PathBuf::from(&args.path);
+ let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
+ let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
+
+ state.borrow::<Permissions>().check_write(&path)?;
+
+ tokio::task::spawn_blocking(move || {
+ filetime::set_file_times(path, atime, mtime)?;
+ Ok(json!({}))
+ })
+ .await
+ .unwrap()
+}
+
+fn op_cwd(
+ state: &mut OpState,
+ _args: Value,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let path = current_dir()?;
+ state
+ .borrow::<Permissions>()
+ .check_read_blind(&path, "CWD")?;
+ let path_str = into_string(path.into_os_string())?;
+ Ok(json!(path_str))
+}