diff options
| author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2019-08-14 17:03:02 +0200 |
|---|---|---|
| committer | Ryan Dahl <ry@tinyclouds.org> | 2019-08-14 11:03:02 -0400 |
| commit | e6c349af9f7260c2c4ec713bd231fe554240721e (patch) | |
| tree | 7671dabb8270cc0c2d7f7f5a3b5f5d8d4b1e0986 /cli/ops/fs.rs | |
| parent | 58f0e9b9b1b53ca486ef38ae662b98cbde839248 (diff) | |
split up ops.rs (#2753)
Note cli/dispatch_minimal.rs ops are not yet included in cli/ops.
This is part of work towards #2730
Diffstat (limited to 'cli/ops/fs.rs')
| -rw-r--r-- | cli/ops/fs.rs | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs new file mode 100644 index 000000000..6530e3816 --- /dev/null +++ b/cli/ops/fs.rs @@ -0,0 +1,506 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +use crate::deno_error::DenoError; +use crate::deno_error::ErrorKind; +use crate::fs as deno_fs; +use crate::msg; +use crate::ops::blocking; +use crate::ops::empty_buf; +use crate::ops::ok_buf; +use crate::ops::serialize_response; +use crate::ops::CliOpResult; +use crate::state::ThreadSafeState; +use deno::*; +use flatbuffers::FlatBufferBuilder; +use remove_dir_all::remove_dir_all; +use std; +use std::convert::From; +use std::fs; +use std::path::PathBuf; +use std::time::UNIX_EPOCH; +use utime; + +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + +pub fn op_chdir( + _state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_chdir().unwrap(); + let directory = inner.directory().unwrap(); + std::env::set_current_dir(&directory)?; + ok_buf(empty_buf()) +} + +pub fn op_mkdir( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_mkdir().unwrap(); + let (path, path_) = deno_fs::resolve_from_cwd(inner.path().unwrap())?; + let recursive = inner.recursive(); + let mode = inner.mode(); + + state.check_write(&path_)?; + + blocking(base.sync(), move || { + debug!("op_mkdir {}", path_); + deno_fs::mkdir(&path, mode, recursive)?; + Ok(empty_buf()) + }) +} + +pub fn op_chmod( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_chmod().unwrap(); + let _mode = inner.mode(); + let (path, path_) = deno_fs::resolve_from_cwd(inner.path().unwrap())?; + + state.check_write(&path_)?; + + blocking(base.sync(), move || { + debug!("op_chmod {}", &path_); + // Still check file/dir exists on windows + let _metadata = fs::metadata(&path)?; + #[cfg(any(unix))] + { + let mut permissions = _metadata.permissions(); + permissions.set_mode(_mode); + fs::set_permissions(&path, permissions)?; + } + Ok(empty_buf()) + }) +} + +pub fn op_chown( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_chown().unwrap(); + let path = String::from(inner.path().unwrap()); + let uid = inner.uid(); + let gid = inner.gid(); + + state.check_write(&path)?; + + blocking(base.sync(), move || { + debug!("op_chown {}", &path); + match deno_fs::chown(&path, uid, gid) { + Ok(_) => Ok(empty_buf()), + Err(e) => Err(e), + } + }) +} + +pub fn op_remove( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_remove().unwrap(); + let (path, path_) = deno_fs::resolve_from_cwd(inner.path().unwrap())?; + let recursive = inner.recursive(); + + state.check_write(&path_)?; + + blocking(base.sync(), move || { + debug!("op_remove {}", path.display()); + let metadata = fs::metadata(&path)?; + if metadata.is_file() { + fs::remove_file(&path)?; + } else if recursive { + remove_dir_all(&path)?; + } else { + fs::remove_dir(&path)?; + } + Ok(empty_buf()) + }) +} + +pub fn op_copy_file( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_copy_file().unwrap(); + let (from, from_) = deno_fs::resolve_from_cwd(inner.from().unwrap())?; + let (to, to_) = deno_fs::resolve_from_cwd(inner.to().unwrap())?; + + state.check_read(&from_)?; + state.check_write(&to_)?; + + debug!("op_copy_file {} {}", from.display(), to.display()); + blocking(base.sync(), move || { + // On *nix, Rust deem non-existent path as invalid input + // See https://github.com/rust-lang/rust/issues/54800 + // Once the issue is reolved, we should remove this workaround. + if cfg!(unix) && !from.is_file() { + return Err( + DenoError::new(ErrorKind::NotFound, "File not found".to_string()) + .into(), + ); + } + + fs::copy(&from, &to)?; + Ok(empty_buf()) + }) +} + +macro_rules! to_seconds { + ($time:expr) => {{ + // Unwrap is safe here as if the file is before the unix epoch + // something is very wrong. + $time + .and_then(|t| Ok(t.duration_since(UNIX_EPOCH).unwrap().as_secs())) + .unwrap_or(0) + }}; +} + +#[cfg(any(unix))] +fn get_mode(perm: &fs::Permissions) -> u32 { + perm.mode() +} + +#[cfg(not(any(unix)))] +fn get_mode(_perm: &fs::Permissions) -> u32 { + 0 +} + +pub fn op_stat( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_stat().unwrap(); + let cmd_id = base.cmd_id(); + let (filename, filename_) = + deno_fs::resolve_from_cwd(inner.filename().unwrap())?; + let lstat = inner.lstat(); + + state.check_read(&filename_)?; + + blocking(base.sync(), move || { + let builder = &mut FlatBufferBuilder::new(); + debug!("op_stat {} {}", filename.display(), lstat); + let metadata = if lstat { + fs::symlink_metadata(&filename)? + } else { + fs::metadata(&filename)? + }; + + let inner = msg::StatRes::create( + builder, + &msg::StatResArgs { + is_file: metadata.is_file(), + is_symlink: metadata.file_type().is_symlink(), + len: metadata.len(), + modified: to_seconds!(metadata.modified()), + accessed: to_seconds!(metadata.accessed()), + created: to_seconds!(metadata.created()), + mode: get_mode(&metadata.permissions()), + has_mode: cfg!(target_family = "unix"), + ..Default::default() + }, + ); + + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::StatRes, + ..Default::default() + }, + )) + }) +} + +pub fn op_read_dir( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_read_dir().unwrap(); + let cmd_id = base.cmd_id(); + let (path, path_) = deno_fs::resolve_from_cwd(inner.path().unwrap())?; + + state.check_read(&path_)?; + + blocking(base.sync(), move || { + debug!("op_read_dir {}", path.display()); + let builder = &mut FlatBufferBuilder::new(); + let entries: Vec<_> = fs::read_dir(path)? + .map(|entry| { + let entry = entry.unwrap(); + let metadata = entry.metadata().unwrap(); + let file_type = metadata.file_type(); + let name = builder.create_string(entry.file_name().to_str().unwrap()); + + msg::StatRes::create( + builder, + &msg::StatResArgs { + is_file: file_type.is_file(), + is_symlink: file_type.is_symlink(), + len: metadata.len(), + modified: to_seconds!(metadata.modified()), + accessed: to_seconds!(metadata.accessed()), + created: to_seconds!(metadata.created()), + name: Some(name), + mode: get_mode(&metadata.permissions()), + has_mode: cfg!(target_family = "unix"), + }, + ) + }) + .collect(); + + let entries = builder.create_vector(&entries); + let inner = msg::ReadDirRes::create( + builder, + &msg::ReadDirResArgs { + entries: Some(entries), + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ReadDirRes, + ..Default::default() + }, + )) + }) +} + +pub fn op_rename( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_rename().unwrap(); + let (oldpath, oldpath_) = + deno_fs::resolve_from_cwd(inner.oldpath().unwrap())?; + let (newpath, newpath_) = + deno_fs::resolve_from_cwd(inner.newpath().unwrap())?; + + state.check_read(&oldpath_)?; + state.check_write(&oldpath_)?; + state.check_write(&newpath_)?; + + blocking(base.sync(), move || { + debug!("op_rename {} {}", oldpath.display(), newpath.display()); + fs::rename(&oldpath, &newpath)?; + Ok(empty_buf()) + }) +} + +pub fn op_link( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_link().unwrap(); + let (oldname, oldpath_) = + deno_fs::resolve_from_cwd(inner.oldname().unwrap())?; + let (newname, newname_) = + deno_fs::resolve_from_cwd(inner.newname().unwrap())?; + + state.check_read(&oldpath_)?; + state.check_write(&newname_)?; + + blocking(base.sync(), move || { + debug!("op_link {} {}", oldname.display(), newname.display()); + std::fs::hard_link(&oldname, &newname)?; + Ok(empty_buf()) + }) +} + +pub fn op_symlink( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_symlink().unwrap(); + let (oldname, _) = deno_fs::resolve_from_cwd(inner.oldname().unwrap())?; + let (newname, newname_) = + deno_fs::resolve_from_cwd(inner.newname().unwrap())?; + + state.check_write(&newname_)?; + // TODO Use type for Windows. + if cfg!(windows) { + return Err( + DenoError::new(ErrorKind::Other, "Not implemented".to_string()).into(), + ); + } + blocking(base.sync(), move || { + debug!("op_symlink {} {}", oldname.display(), newname.display()); + #[cfg(any(unix))] + std::os::unix::fs::symlink(&oldname, &newname)?; + Ok(empty_buf()) + }) +} + +pub fn op_read_link( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_readlink().unwrap(); + let cmd_id = base.cmd_id(); + let (name, name_) = deno_fs::resolve_from_cwd(inner.name().unwrap())?; + + state.check_read(&name_)?; + + blocking(base.sync(), move || { + debug!("op_read_link {}", name.display()); + let path = fs::read_link(&name)?; + let builder = &mut FlatBufferBuilder::new(); + let path_off = builder.create_string(path.to_str().unwrap()); + let inner = msg::ReadlinkRes::create( + builder, + &msg::ReadlinkResArgs { + path: Some(path_off), + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ReadlinkRes, + ..Default::default() + }, + )) + }) +} + +pub fn op_truncate( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + + let inner = base.inner_as_truncate().unwrap(); + let (filename, filename_) = deno_fs::resolve_from_cwd(inner.name().unwrap())?; + let len = inner.len(); + + state.check_write(&filename_)?; + + blocking(base.sync(), move || { + debug!("op_truncate {} {}", filename_, len); + let f = fs::OpenOptions::new().write(true).open(&filename)?; + f.set_len(u64::from(len))?; + Ok(empty_buf()) + }) +} + +pub fn op_make_temp_dir( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let base = Box::new(*base); + let inner = base.inner_as_make_temp_dir().unwrap(); + let cmd_id = base.cmd_id(); + + // FIXME + state.check_write("make_temp")?; + + let dir = inner.dir().map(PathBuf::from); + let prefix = inner.prefix().map(String::from); + let suffix = inner.suffix().map(String::from); + + blocking(base.sync(), 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 = deno_fs::make_temp_dir( + // Converting Option<String> to Option<&str> + dir.as_ref().map(|x| &**x), + prefix.as_ref().map(|x| &**x), + suffix.as_ref().map(|x| &**x), + )?; + let builder = &mut FlatBufferBuilder::new(); + let path_off = builder.create_string(path.to_str().unwrap()); + let inner = msg::MakeTempDirRes::create( + builder, + &msg::MakeTempDirResArgs { + path: Some(path_off), + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::MakeTempDirRes, + ..Default::default() + }, + )) + }) +} + +pub fn op_utime( + state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + + let inner = base.inner_as_utime().unwrap(); + let filename = String::from(inner.filename().unwrap()); + let atime = inner.atime(); + let mtime = inner.mtime(); + + state.check_write(&filename)?; + + blocking(base.sync(), move || { + debug!("op_utimes {} {} {}", filename, atime, mtime); + utime::set_file_times(filename, atime, mtime)?; + Ok(empty_buf()) + }) +} + +pub fn op_cwd( + _state: &ThreadSafeState, + base: &msg::Base<'_>, + data: Option<PinnedBuf>, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let path = std::env::current_dir()?; + let builder = &mut FlatBufferBuilder::new(); + let cwd = + builder.create_string(&path.into_os_string().into_string().unwrap()); + let inner = msg::CwdRes::create(builder, &msg::CwdResArgs { cwd: Some(cwd) }); + let response_buf = serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::CwdRes, + ..Default::default() + }, + ); + ok_buf(response_buf) +} |
