deno.land / x / deno@v1.28.2 / runtime / ops / fs.rs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.// Some deserializer fields are only used on Unix and Windows build fails without ituse super::io::StdFileResource;use super::utils::into_string;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::op;use deno_core::CancelFuture;use deno_core::CancelHandle;use deno_core::ZeroCopyBuf;
use deno_core::Extension;use deno_core::OpState;use deno_core::ResourceId;use deno_crypto::rand::thread_rng;use deno_crypto::rand::Rng;use log::debug;use serde::Deserialize;use serde::Serialize;use std::borrow::Cow;use std::cell::RefCell;use std::convert::From;use std::env::current_dir;use std::env::set_current_dir;use std::env::temp_dir;use std::io;use std::io::Error;use std::io::Seek;use std::io::SeekFrom;use std::io::Write;use std::path::Path;use std::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() -> Extension { Extension::builder() .ops(vec![ op_open_sync::decl(), op_open_async::decl(), op_write_file_sync::decl(), op_write_file_async::decl(), op_seek_sync::decl(), op_seek_async::decl(), op_fdatasync_sync::decl(), op_fdatasync_async::decl(), op_fsync_sync::decl(), op_fsync_async::decl(), op_fstat_sync::decl(), op_fstat_async::decl(), op_flock_sync::decl(), op_flock_async::decl(), op_funlock_sync::decl(), op_funlock_async::decl(), op_umask::decl(), op_chdir::decl(), op_mkdir_sync::decl(), op_mkdir_async::decl(), op_chmod_sync::decl(), op_chmod_async::decl(), op_chown_sync::decl(), op_chown_async::decl(), op_remove_sync::decl(), op_remove_async::decl(), op_copy_file_sync::decl(), op_copy_file_async::decl(), op_stat_sync::decl(), op_stat_async::decl(), op_realpath_sync::decl(), op_realpath_async::decl(), op_read_dir_sync::decl(), op_read_dir_async::decl(), op_rename_sync::decl(), op_rename_async::decl(), op_link_sync::decl(), op_link_async::decl(), op_symlink_sync::decl(), op_symlink_async::decl(), op_read_link_sync::decl(), op_read_link_async::decl(), op_ftruncate_sync::decl(), op_ftruncate_async::decl(), op_truncate_sync::decl(), op_truncate_async::decl(), op_make_temp_dir_sync::decl(), op_make_temp_dir_async::decl(), op_make_temp_file_sync::decl(), op_make_temp_file_async::decl(), op_cwd::decl(), op_futime_sync::decl(), op_futime_async::decl(), op_utime_sync::decl(), op_utime_async::decl(), op_readfile_sync::decl(), op_readfile_text_sync::decl(), op_readfile_async::decl(), op_readfile_text_async::decl(), ]) .build()}
#[derive(Deserialize, Default, Debug)]#[serde(rename_all = "camelCase")]#[serde(default)]pub struct OpenOptions { read: bool, write: bool, create: bool, truncate: bool, append: bool, create_new: bool,}
#[inline]fn open_helper( state: &mut OpState, path: &str, mode: Option<u32>, options: Option<&OpenOptions>, api_name: &str,) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> { let path = Path::new(path).to_path_buf();
let mut open_options = std::fs::OpenOptions::new();
if let Some(mode) = 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_mut::<Permissions>();
match options { None => { permissions.read.check(&path, Some(api_name))?; open_options .read(true) .create(false) .write(false) .truncate(false) .append(false) .create_new(false); } Some(options) => { if options.read { permissions.read.check(&path, Some(api_name))?; }
if options.write || options.append { permissions.write.check(&path, Some(api_name))?; }
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))}
#[op]fn op_open_sync( state: &mut OpState, path: String, options: Option<OpenOptions>, mode: Option<u32>,) -> Result<ResourceId, AnyError> { let (path, open_options) = open_helper(state, &path, mode, options.as_ref(), "Deno.openSync()")?; let std_file = open_options.open(&path).map_err(|err| { Error::new(err.kind(), format!("{}, open '{}'", err, path.display())) })?; let resource = StdFileResource::fs_file(std_file); let rid = state.resource_table.add(resource); Ok(rid)}
#[op]async fn op_open_async( state: Rc<RefCell<OpState>>, path: String, options: Option<OpenOptions>, mode: Option<u32>,) -> Result<ResourceId, AnyError> { let (path, open_options) = open_helper( &mut state.borrow_mut(), &path, mode, options.as_ref(), "Deno.open()", )?; let std_file = tokio::task::spawn_blocking(move || { open_options.open(path.clone()).map_err(|err| { Error::new(err.kind(), format!("{}, open '{}'", err, path.display())) }) }) .await?; let resource = StdFileResource::fs_file(std_file?); let rid = state.borrow_mut().resource_table.add(resource); Ok(rid)}
#[inline]fn write_open_options(create: bool, append: bool) -> OpenOptions { OpenOptions { read: false, write: true, create, truncate: !append, append, create_new: false, }}
#[op]fn op_write_file_sync( state: &mut OpState, path: String, mode: Option<u32>, append: bool, create: bool, data: ZeroCopyBuf,) -> Result<(), AnyError> { let (path, open_options) = open_helper( state, &path, mode, Some(&write_open_options(create, append)), "Deno.writeFileSync()", )?; write_file(&path, open_options, mode, data)}
#[op]async fn op_write_file_async( state: Rc<RefCell<OpState>>, path: String, mode: Option<u32>, append: bool, create: bool, data: ZeroCopyBuf, cancel_rid: Option<ResourceId>,) -> Result<(), AnyError> { let cancel_handle = match cancel_rid { Some(cancel_rid) => state .borrow_mut() .resource_table .get::<CancelHandle>(cancel_rid) .ok(), None => None, }; let (path, open_options) = open_helper( &mut state.borrow_mut(), &path, mode, Some(&write_open_options(create, append)), "Deno.writeFile()", )?; let write_future = tokio::task::spawn_blocking(move || { write_file(&path, open_options, mode, data) }); if let Some(cancel_handle) = cancel_handle { write_future.or_cancel(cancel_handle).await???; } else { write_future.await??; } Ok(())}
fn write_file( path: &Path, open_options: std::fs::OpenOptions, _mode: Option<u32>, data: ZeroCopyBuf,) -> Result<(), AnyError> { let mut std_file = open_options.open(path).map_err(|err| { Error::new(err.kind(), format!("{}, open '{}'", err, path.display())) })?;
// need to chmod the file if it already exists and a mode is specified #[cfg(unix)] if let Some(mode) = _mode { use std::os::unix::fs::PermissionsExt; let permissions = PermissionsExt::from_mode(mode & 0o777); std_file .set_permissions(permissions) .map_err(|err: Error| { Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display())) })?; }
std_file.write_all(&data)?; Ok(())}
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]pub struct SeekArgs { rid: ResourceId, offset: i64, whence: i32,}
fn seek_helper(args: SeekArgs) -> Result<(u32, SeekFrom), AnyError> { let rid = args.rid; 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))}
#[op]fn op_seek_sync(state: &mut OpState, args: SeekArgs) -> Result<u64, AnyError> { let (rid, seek_from) = seek_helper(args)?; StdFileResource::with_file(state, rid, |std_file| { std_file.seek(seek_from).map_err(AnyError::from) })}
#[op]async fn op_seek_async( state: Rc<RefCell<OpState>>, args: SeekArgs,) -> Result<u64, AnyError> { let (rid, seek_from) = seek_helper(args)?;
StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.seek(seek_from).map_err(AnyError::from) }) .await}
#[op]fn op_fdatasync_sync( state: &mut OpState, rid: ResourceId,) -> Result<(), AnyError> { StdFileResource::with_file(state, rid, |std_file| { std_file.sync_data().map_err(AnyError::from) })}
#[op]async fn op_fdatasync_async( state: Rc<RefCell<OpState>>, rid: ResourceId,) -> Result<(), AnyError> { StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.sync_data().map_err(AnyError::from) }) .await}
#[op]fn op_fsync_sync(state: &mut OpState, rid: ResourceId) -> Result<(), AnyError> { StdFileResource::with_file(state, rid, |std_file| { std_file.sync_all().map_err(AnyError::from) })}
#[op]async fn op_fsync_async( state: Rc<RefCell<OpState>>, rid: ResourceId,) -> Result<(), AnyError> { StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.sync_all().map_err(AnyError::from) }) .await}
#[op]fn op_fstat_sync( state: &mut OpState, rid: ResourceId, out_buf: &mut [u32],) -> Result<(), AnyError> { let metadata = StdFileResource::with_file(state, rid, |std_file| { std_file.metadata().map_err(AnyError::from) })?; let stat = get_stat(metadata); stat.write(out_buf); Ok(())}
#[op]async fn op_fstat_async( state: Rc<RefCell<OpState>>, rid: ResourceId,) -> Result<FsStat, AnyError> { let metadata = StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.metadata().map_err(AnyError::from) }) .await?; Ok(get_stat(metadata))}
#[op]fn op_flock_sync( state: &mut OpState, rid: ResourceId, exclusive: bool,) -> Result<(), AnyError> { use fs3::FileExt; super::check_unstable(state, "Deno.flockSync");
StdFileResource::with_file(state, rid, |std_file| { if exclusive { std_file.lock_exclusive()?; } else { std_file.lock_shared()?; } Ok(()) })}
#[op]async fn op_flock_async( state: Rc<RefCell<OpState>>, rid: ResourceId, exclusive: bool,) -> Result<(), AnyError> { use fs3::FileExt; super::check_unstable2(&state, "Deno.flock");
StdFileResource::with_file_blocking_task(state, rid, move |std_file| { if exclusive { std_file.lock_exclusive()?; } else { std_file.lock_shared()?; } Ok(()) }) .await}
#[op]fn op_funlock_sync( state: &mut OpState, rid: ResourceId,) -> Result<(), AnyError> { use fs3::FileExt; super::check_unstable(state, "Deno.funlockSync");
StdFileResource::with_file(state, rid, |std_file| { std_file.unlock()?; Ok(()) })}
#[op]async fn op_funlock_async( state: Rc<RefCell<OpState>>, rid: ResourceId,) -> Result<(), AnyError> { use fs3::FileExt; super::check_unstable2(&state, "Deno.funlock");
StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.unlock()?; Ok(()) }) .await}
#[op]fn op_umask(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError> { super::check_unstable(state, "Deno.umask"); // 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 _ = 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) = 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(r.bits() as u32) }}
#[op]fn op_chdir(state: &mut OpState, directory: String) -> Result<(), AnyError> { let d = PathBuf::from(&directory); state .borrow_mut::<Permissions>() .read .check(&d, Some("Deno.chdir()"))?; set_current_dir(&d).map_err(|err| { Error::new(err.kind(), format!("{}, chdir '{}'", err, directory)) })?; Ok(())}
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]pub struct MkdirArgs { path: String, recursive: bool, mode: Option<u32>,}
#[op]fn op_mkdir_sync(state: &mut OpState, args: MkdirArgs) -> Result<(), AnyError> { let path = Path::new(&args.path).to_path_buf(); let mode = args.mode.unwrap_or(0o777) & 0o777; state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.mkdirSync()"))?; 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).map_err(|err| { Error::new(err.kind(), format!("{}, mkdir '{}'", err, path.display())) })?; Ok(())}
#[op]async fn op_mkdir_async( state: Rc<RefCell<OpState>>, args: MkdirArgs,) -> Result<(), AnyError> { let path = Path::new(&args.path).to_path_buf(); let mode = args.mode.unwrap_or(0o777) & 0o777;
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.mkdir()"))?; }
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).map_err(|err| { Error::new(err.kind(), format!("{}, mkdir '{}'", err, path.display())) })?; Ok(()) }) .await .unwrap()}
#[op]fn op_chmod_sync( state: &mut OpState, path: String, mode: u32,) -> Result<(), AnyError> { let path = Path::new(&path); let mode = mode & 0o777;
state .borrow_mut::<Permissions>() .write .check(path, Some("Deno.chmodSync()"))?; raw_chmod(path, mode)}
#[op]async fn op_chmod_async( state: Rc<RefCell<OpState>>, path: String, mode: u32,) -> Result<(), AnyError> { let path = Path::new(&path).to_path_buf(); let mode = mode & 0o777;
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.chmod()"))?; }
tokio::task::spawn_blocking(move || raw_chmod(&path, mode)) .await .unwrap()}
fn raw_chmod(path: &Path, _raw_mode: u32) -> Result<(), AnyError> { let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display())) }; #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let permissions = PermissionsExt::from_mode(_raw_mode); std::fs::set_permissions(path, permissions).map_err(err_mapper)?; Ok(()) } // TODO Implement chmod for Windows (#4357) #[cfg(not(unix))] { // Still check file/dir exists on Windows let _metadata = std::fs::metadata(path).map_err(err_mapper)?; Err(not_supported()) }}
#[op]fn op_chown_sync( state: &mut OpState, path: String, #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>, #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,) -> Result<(), AnyError> { let path = Path::new(&path).to_path_buf(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.chownSync()"))?; #[cfg(unix)] { use crate::errors::get_nix_error_class; use nix::unistd::{chown, Gid, Uid}; let nix_uid = uid.map(Uid::from_raw); let nix_gid = gid.map(Gid::from_raw); chown(&path, nix_uid, nix_gid).map_err(|err| { custom_error( get_nix_error_class(&err), format!("{}, chown '{}'", err.desc(), path.display()), ) })?; Ok(()) } // TODO Implement chown for Windows #[cfg(not(unix))] { Err(generic_error("Not implemented")) }}
#[op]async fn op_chown_async( state: Rc<RefCell<OpState>>, path: String, #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>, #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,) -> Result<(), AnyError> { let path = Path::new(&path).to_path_buf();
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.chown()"))?; }
tokio::task::spawn_blocking(move || { #[cfg(unix)] { use crate::errors::get_nix_error_class; use nix::unistd::{chown, Gid, Uid}; let nix_uid = uid.map(Uid::from_raw); let nix_gid = gid.map(Gid::from_raw); chown(&path, nix_uid, nix_gid).map_err(|err| { custom_error( get_nix_error_class(&err), format!("{}, chown '{}'", err.desc(), path.display()), ) })?; Ok(()) } // TODO Implement chown for Windows #[cfg(not(unix))] Err(not_supported()) }) .await .unwrap()}
#[op]fn op_remove_sync( state: &mut OpState, path: String, recursive: bool,) -> Result<(), AnyError> { let path = PathBuf::from(&path);
state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.removeSync()"))?;
#[cfg(not(unix))] use std::os::windows::prelude::MetadataExt;
let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, remove '{}'", err, path.display())) }; let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
let file_type = metadata.file_type(); if file_type.is_file() { std::fs::remove_file(&path).map_err(err_mapper)?; } else if recursive { std::fs::remove_dir_all(&path).map_err(err_mapper)?; } else if file_type.is_symlink() { #[cfg(unix)] std::fs::remove_file(&path).map_err(err_mapper)?; #[cfg(not(unix))] { use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY; if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 { std::fs::remove_dir(&path).map_err(err_mapper)?; } else { std::fs::remove_file(&path).map_err(err_mapper)?; } } } else if file_type.is_dir() { std::fs::remove_dir(&path).map_err(err_mapper)?; } else { // pipes, sockets, etc... std::fs::remove_file(&path).map_err(err_mapper)?; } Ok(())}
#[op]async fn op_remove_async( state: Rc<RefCell<OpState>>, path: String, recursive: bool,) -> Result<(), AnyError> { let path = PathBuf::from(&path);
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.remove()"))?; }
tokio::task::spawn_blocking(move || { #[cfg(not(unix))] use std::os::windows::prelude::MetadataExt; let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, remove '{}'", err, path.display())) }; let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
debug!("op_remove_async {} {}", path.display(), recursive); let file_type = metadata.file_type(); if file_type.is_file() { std::fs::remove_file(&path).map_err(err_mapper)?; } else if recursive { std::fs::remove_dir_all(&path).map_err(err_mapper)?; } else if file_type.is_symlink() { #[cfg(unix)] std::fs::remove_file(&path).map_err(err_mapper)?; #[cfg(not(unix))] { use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY; if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 { std::fs::remove_dir(&path).map_err(err_mapper)?; } else { std::fs::remove_file(&path).map_err(err_mapper)?; } } } else if file_type.is_dir() { std::fs::remove_dir(&path).map_err(err_mapper)?; } else { // pipes, sockets, etc... std::fs::remove_file(&path).map_err(err_mapper)?; } Ok(()) }) .await .unwrap()}
#[op]fn op_copy_file_sync( state: &mut OpState, from: String, to: String,) -> Result<(), AnyError> { let from_path = PathBuf::from(&from); let to_path = PathBuf::from(&to);
let permissions = state.borrow_mut::<Permissions>(); permissions .read .check(&from_path, Some("Deno.copyFileSync()"))?; permissions .write .check(&to_path, Some("Deno.copyFileSync()"))?;
// 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_path.is_file() { return Err(custom_error( "NotFound", format!( "File not found, copy '{}' -> '{}'", from_path.display(), to_path.display() ), )); }
let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, copy '{}' -> '{}'", err, from_path.display(), to_path.display() ), ) };
#[cfg(target_os = "macos")] { use libc::clonefile; use libc::stat; use libc::unlink; use std::ffi::CString; use std::io::Read;
let from = CString::new(from).unwrap(); let to = CString::new(to).unwrap();
// SAFETY: `from` and `to` are valid C strings. // std::fs::copy does open() + fcopyfile() on macOS. We try to use // clonefile() instead, which is more efficient. unsafe { let mut st = std::mem::zeroed(); let ret = stat(from.as_ptr(), &mut st); if ret != 0 { return Err(err_mapper(Error::last_os_error()).into()); }
if st.st_size > 128 * 1024 { // Try unlink. If it fails, we are going to try clonefile() anyway. let _ = unlink(to.as_ptr()); // Matches rust stdlib behavior for io::copy. // https://github.com/rust-lang/rust/blob/3fdd578d72a24d4efc2fe2ad18eec3b6ba72271e/library/std/src/sys/unix/fs.rs#L1613-L1616 if clonefile(from.as_ptr(), to.as_ptr(), 0) == 0 { return Ok(()); } } else { // Do a regular copy. fcopyfile() is an overkill for < 128KB // files. let mut buf = [0u8; 128 * 1024]; let mut from_file = std::fs::File::open(&from_path).map_err(err_mapper)?; let mut to_file = std::fs::File::create(&to_path).map_err(err_mapper)?; loop { let nread = from_file.read(&mut buf).map_err(err_mapper)?; if nread == 0 { break; } to_file.write_all(&buf[..nread]).map_err(err_mapper)?; } return Ok(()); } }
// clonefile() failed, fall back to std::fs::copy(). }
// returns size of from as u64 (we ignore) std::fs::copy(&from_path, &to_path).map_err(err_mapper)?; Ok(())}
#[op]async fn op_copy_file_async( state: Rc<RefCell<OpState>>, from: String, to: String,) -> Result<(), AnyError> { let from = PathBuf::from(&from); let to = PathBuf::from(&to);
{ let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&from, Some("Deno.copyFile()"))?; permissions.write.check(&to, Some("Deno.copyFile()"))?; }
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", format!( "File not found, copy '{}' -> '{}'", from.display(), to.display() ), )); }
let err_mapper = |err: Error| { Error::new( err.kind(), format!("{}, copy '{}' -> '{}'", err, from.display(), to.display()), ) }; // returns size of from as u64 (we ignore) std::fs::copy(&from, &to).map_err(err_mapper)?; Ok(()) }) .await .unwrap()}
fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> (u64, bool) { match maybe_time { Ok(time) => ( time .duration_since(UNIX_EPOCH) .map(|t| t.as_millis() as u64) .unwrap_or_else(|err| err.duration().as_millis() as u64), true, ), Err(_) => (0, false), }}
macro_rules! create_struct_writer { (pub struct $name:ident { $($field:ident: $type:ty),* $(,)? }) => { impl $name { fn write(self, buf: &mut [u32]) { let mut offset = 0; $( let value = self.$field as u64; buf[offset] = value as u32; buf[offset + 1] = (value >> 32) as u32; #[allow(unused_assignments)] { offset += 2; } )* } }
#[derive(Serialize)] #[serde(rename_all = "camelCase")] struct $name { $($field: $type),* } };}
create_struct_writer! { pub struct FsStat { is_file: bool, is_directory: bool, is_symlink: bool, size: u64, // In milliseconds, like JavaScript. Available on both Unix or Windows. mtime_set: bool, mtime: u64, atime_set: bool, atime: u64, birthtime_set: bool, birthtime: u64, // Following are only valid under Unix. dev: u64, ino: u64, mode: u32, nlink: u64, uid: u32, gid: u32, rdev: u64, blksize: u64, blocks: u64, }}
#[inline(always)]fn get_stat(metadata: std::fs::Metadata) -> FsStat { // 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 (mtime, mtime_set) = to_msec(metadata.modified()); let (atime, atime_set) = to_msec(metadata.accessed()); let (birthtime, birthtime_set) = to_msec(metadata.created());
FsStat { is_file: metadata.is_file(), is_directory: metadata.is_dir(), is_symlink: metadata.file_type().is_symlink(), size: metadata.len(), // In milliseconds, like JavaScript. Available on both Unix or Windows. mtime_set, mtime, atime_set, atime, birthtime_set, birthtime, // 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), blksize: usm!(blksize), blocks: usm!(blocks), }}
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]pub struct StatArgs { path: String, lstat: bool,}
#[op]fn op_stat_sync( state: &mut OpState, path: String, lstat: bool, out_buf: &mut [u32],) -> Result<(), AnyError> { let path = PathBuf::from(&path); state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.statSync()"))?; let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, stat '{}'", err, path.display())) }; let metadata = if lstat { std::fs::symlink_metadata(&path).map_err(err_mapper)? } else { std::fs::metadata(&path).map_err(err_mapper)? };
let stat = get_stat(metadata); stat.write(out_buf);
Ok(())}
#[op]async fn op_stat_async( state: Rc<RefCell<OpState>>, args: StatArgs,) -> Result<FsStat, AnyError> { let path = PathBuf::from(&args.path); let lstat = args.lstat;
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.stat()"))?; }
tokio::task::spawn_blocking(move || { debug!("op_stat_async {} {}", path.display(), lstat); let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, stat '{}'", err, path.display())) }; let metadata = if lstat { std::fs::symlink_metadata(&path).map_err(err_mapper)? } else { std::fs::metadata(&path).map_err(err_mapper)? }; Ok(get_stat(metadata)) }) .await .unwrap()}
#[op]fn op_realpath_sync( state: &mut OpState, path: String,) -> Result<String, AnyError> { let path = PathBuf::from(&path);
let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&path, Some("Deno.realPathSync()"))?; if path.is_relative() { permissions.read.check_blind( ¤t_dir()?, "CWD", "Deno.realPathSync()", )?; }
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(realpath_str)}
#[op]async fn op_realpath_async( state: Rc<RefCell<OpState>>, path: String,) -> Result<String, AnyError> { let path = PathBuf::from(&path);
{ let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&path, Some("Deno.realPath()"))?; if path.is_relative() { permissions.read.check_blind( ¤t_dir()?, "CWD", "Deno.realPath()", )?; } }
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(realpath_str) }) .await .unwrap()}
#[derive(Serialize)]#[serde(rename_all = "camelCase")]pub struct DirEntry { name: String, is_file: bool, is_directory: bool, is_symlink: bool,}
#[op]fn op_read_dir_sync( state: &mut OpState, path: String,) -> Result<Vec<DirEntry>, AnyError> { let path = PathBuf::from(&path);
state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.readDirSync()"))?;
debug!("op_read_dir_sync {}", path.display()); let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, readdir '{}'", err, path.display())) }; let entries: Vec<_> = std::fs::read_dir(&path) .map_err(err_mapper)? .filter_map(|entry| { let entry = entry.unwrap(); // Not all filenames can be encoded as UTF-8. Skip those for now. if let Ok(name) = into_string(entry.file_name()) { Some(DirEntry { name, is_file: entry .file_type() .map_or(false, |file_type| file_type.is_file()), is_directory: entry .file_type() .map_or(false, |file_type| file_type.is_dir()), is_symlink: entry .file_type() .map_or(false, |file_type| file_type.is_symlink()), }) } else { None } }) .collect();
Ok(entries)}
#[op]async fn op_read_dir_async( state: Rc<RefCell<OpState>>, path: String,) -> Result<Vec<DirEntry>, AnyError> { let path = PathBuf::from(&path); { let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.readDir()"))?; } tokio::task::spawn_blocking(move || { debug!("op_read_dir_async {}", path.display()); let err_mapper = |err: Error| { Error::new(err.kind(), format!("{}, readdir '{}'", err, path.display())) }; let entries: Vec<_> = std::fs::read_dir(&path) .map_err(err_mapper)? .filter_map(|entry| { let entry = entry.unwrap(); // Not all filenames can be encoded as UTF-8. Skip those for now. if let Ok(name) = into_string(entry.file_name()) { Some(DirEntry { name, is_file: entry .file_type() .map_or(false, |file_type| file_type.is_file()), is_directory: entry .file_type() .map_or(false, |file_type| file_type.is_dir()), is_symlink: entry .file_type() .map_or(false, |file_type| file_type.is_symlink()), }) } else { None } }) .collect();
Ok(entries) }) .await .unwrap()}
#[op]fn op_rename_sync( state: &mut OpState, oldpath: String, newpath: String,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath);
let permissions = state.borrow_mut::<Permissions>(); permissions .read .check(&oldpath, Some("Deno.renameSync()"))?; permissions .write .check(&oldpath, Some("Deno.renameSync()"))?; permissions .write .check(&newpath, Some("Deno.renameSync()"))?;
let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, rename '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; std::fs::rename(&oldpath, &newpath).map_err(err_mapper)?; Ok(())}
#[op]async fn op_rename_async( state: Rc<RefCell<OpState>>, oldpath: String, newpath: String,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath); { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&oldpath, Some("Deno.rename()"))?; permissions.write.check(&oldpath, Some("Deno.rename()"))?; permissions.write.check(&newpath, Some("Deno.rename()"))?; } tokio::task::spawn_blocking(move || { let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, rename '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; std::fs::rename(&oldpath, &newpath).map_err(err_mapper)?; Ok(()) }) .await .unwrap()}
#[op]fn op_link_sync( state: &mut OpState, oldpath: String, newpath: String,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath);
let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&oldpath, Some("Deno.linkSync()"))?; permissions.write.check(&oldpath, Some("Deno.linkSync()"))?; permissions.read.check(&newpath, Some("Deno.linkSync()"))?; permissions.write.check(&newpath, Some("Deno.linkSync()"))?;
let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, link '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; std::fs::hard_link(&oldpath, &newpath).map_err(err_mapper)?; Ok(())}
#[op]async fn op_link_async( state: Rc<RefCell<OpState>>, oldpath: String, newpath: String,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath);
{ let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<Permissions>(); permissions.read.check(&oldpath, Some("Deno.link()"))?; permissions.write.check(&oldpath, Some("Deno.link()"))?; permissions.read.check(&newpath, Some("Deno.link()"))?; permissions.write.check(&newpath, Some("Deno.link()"))?; }
tokio::task::spawn_blocking(move || { let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, link '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; std::fs::hard_link(&oldpath, &newpath).map_err(err_mapper)?; Ok(()) }) .await .unwrap()}
#[op]fn op_symlink_sync( state: &mut OpState, oldpath: String, newpath: String, _type: Option<String>,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath);
state .borrow_mut::<Permissions>() .write .check_all(Some("Deno.symlinkSync()"))?; state .borrow_mut::<Permissions>() .read .check_all(Some("Deno.symlinkSync()"))?;
let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, symlink '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; #[cfg(unix)] { use std::os::unix::fs::symlink; symlink(&oldpath, &newpath).map_err(err_mapper)?; Ok(()) } #[cfg(not(unix))] { use std::os::windows::fs::{symlink_dir, symlink_file};
match _type { Some(ty) => match ty.as_ref() { "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?, "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?, _ => 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).map_err(err_mapper)? } else if metadata.is_dir() { symlink_dir(&oldpath, &newpath).map_err(err_mapper)? } } Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())), } } }; Ok(()) }}
#[op]async fn op_symlink_async( state: Rc<RefCell<OpState>>, oldpath: String, newpath: String, _type: Option<String>,) -> Result<(), AnyError> { let oldpath = PathBuf::from(&oldpath); let newpath = PathBuf::from(&newpath);
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check_all(Some("Deno.symlink()"))?; state .borrow_mut::<Permissions>() .read .check_all(Some("Deno.symlink()"))?; }
tokio::task::spawn_blocking(move || { let err_mapper = |err: Error| { Error::new( err.kind(), format!( "{}, symlink '{}' -> '{}'", err, oldpath.display(), newpath.display() ), ) }; #[cfg(unix)] { use std::os::unix::fs::symlink; symlink(&oldpath, &newpath).map_err(err_mapper)?; Ok(()) } #[cfg(not(unix))] { use std::os::windows::fs::{symlink_dir, symlink_file};
match _type { Some(ty) => match ty.as_ref() { "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?, "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?, _ => 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).map_err(err_mapper)? } else if metadata.is_dir() { symlink_dir(&oldpath, &newpath).map_err(err_mapper)? } } Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())), } } }; Ok(()) } }) .await .unwrap()}
#[op]fn op_read_link_sync( state: &mut OpState, path: String,) -> Result<String, AnyError> { let path = PathBuf::from(&path);
state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.readLink()"))?;
debug!("op_read_link_value {}", path.display()); let err_mapper = |err: Error| { Error::new( err.kind(), format!("{}, readlink '{}'", err, path.display()), ) }; let target = std::fs::read_link(&path) .map_err(err_mapper)? .into_os_string(); let targetstr = into_string(target)?; Ok(targetstr)}
#[op]async fn op_read_link_async( state: Rc<RefCell<OpState>>, path: String,) -> Result<String, AnyError> { let path = PathBuf::from(&path); { let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .read .check(&path, Some("Deno.readLink()"))?; } tokio::task::spawn_blocking(move || { debug!("op_read_link_async {}", path.display()); let err_mapper = |err: Error| { Error::new( err.kind(), format!("{}, readlink '{}'", err, path.display()), ) }; let target = std::fs::read_link(&path) .map_err(err_mapper)? .into_os_string(); let targetstr = into_string(target)?; Ok(targetstr) }) .await .unwrap()}
#[op]fn op_ftruncate_sync( state: &mut OpState, rid: u32, len: i32,) -> Result<(), AnyError> { let len = len as u64; StdFileResource::with_file(state, rid, |std_file| { std_file.set_len(len).map_err(AnyError::from) })?; Ok(())}
#[op]async fn op_ftruncate_async( state: Rc<RefCell<OpState>>, rid: ResourceId, len: i32,) -> Result<(), AnyError> { let len = len as u64;
StdFileResource::with_file_blocking_task(state, rid, move |std_file| { std_file.set_len(len)?; Ok(()) }) .await}
#[op]fn op_truncate_sync( state: &mut OpState, path: String, len: u64,) -> Result<(), AnyError> { let path = PathBuf::from(&path);
state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.truncateSync()"))?;
debug!("op_truncate_sync {} {}", path.display(), len); let err_mapper = |err: Error| { Error::new( err.kind(), format!("{}, truncate '{}'", err, path.display()), ) }; let f = std::fs::OpenOptions::new() .write(true) .open(&path) .map_err(err_mapper)?; f.set_len(len).map_err(err_mapper)?; Ok(())}
#[op]async fn op_truncate_async( state: Rc<RefCell<OpState>>, path: String, len: u64,) -> Result<(), AnyError> { let path = PathBuf::from(&path);
{ let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.truncate()"))?; } tokio::task::spawn_blocking(move || { debug!("op_truncate_async {} {}", path.display(), len); let err_mapper = |err: Error| { Error::new( err.kind(), format!("{}, truncate '{}'", err, path.display()), ) }; let f = std::fs::OpenOptions::new() .write(true) .open(&path) .map_err(err_mapper)?; f.set_len(len).map_err(err_mapper)?; Ok(()) }) .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(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")]pub struct MakeTempArgs { dir: Option<String>, prefix: Option<String>, suffix: Option<String>,}
#[op]fn op_make_temp_dir_sync( state: &mut OpState, args: MakeTempArgs,) -> Result<String, AnyError> { 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_mut::<Permissions>().write.check( dir.clone().unwrap_or_else(temp_dir).as_path(), Some("Deno.makeTempDirSync()"), )?;
// 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(path_str)}
#[op]async fn op_make_temp_dir_async( state: Rc<RefCell<OpState>>, args: MakeTempArgs,) -> Result<String, AnyError> { let dir = args.dir.map(|s| PathBuf::from(&s)); let prefix = args.prefix.map(String::from); let suffix = args.suffix.map(String::from); { let mut state = state.borrow_mut(); state.borrow_mut::<Permissions>().write.check( dir.clone().unwrap_or_else(temp_dir).as_path(), Some("Deno.makeTempDir()"), )?; } 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(path_str) }) .await .unwrap()}
#[op]fn op_make_temp_file_sync( state: &mut OpState, args: MakeTempArgs,) -> Result<String, AnyError> { 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_mut::<Permissions>().write.check( dir.clone().unwrap_or_else(temp_dir).as_path(), Some("Deno.makeTempFileSync()"), )?;
// 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(path_str)}
#[op]async fn op_make_temp_file_async( state: Rc<RefCell<OpState>>, args: MakeTempArgs,) -> Result<String, AnyError> { let dir = args.dir.map(|s| PathBuf::from(&s)); let prefix = args.prefix.map(String::from); let suffix = args.suffix.map(String::from); { let mut state = state.borrow_mut(); state.borrow_mut::<Permissions>().write.check( dir.clone().unwrap_or_else(temp_dir).as_path(), Some("Deno.makeTempFile()"), )?; } 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(path_str) }) .await .unwrap()}
#[op]fn op_futime_sync( state: &mut OpState, rid: ResourceId, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32,) -> Result<(), AnyError> { let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
StdFileResource::with_file(state, rid, |std_file| { filetime::set_file_handle_times(std_file, Some(atime), Some(mtime)) .map_err(AnyError::from) })?;
Ok(())}
#[op]async fn op_futime_async( state: Rc<RefCell<OpState>>, rid: ResourceId, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32,) -> Result<(), AnyError> { let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
StdFileResource::with_file_blocking_task(state, rid, move |std_file| { filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))?; Ok(()) }) .await}
#[op]fn op_utime_sync( state: &mut OpState, path: String, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32,) -> Result<(), AnyError> { let path = PathBuf::from(&path); let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
state .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.utime()"))?; filetime::set_file_times(&path, atime, mtime).map_err(|err| { Error::new(err.kind(), format!("{}, utime '{}'", err, path.display())) })?; Ok(())}
#[op]async fn op_utime_async( state: Rc<RefCell<OpState>>, path: String, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32,) -> Result<(), AnyError> { let path = PathBuf::from(&path); let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
state .borrow_mut() .borrow_mut::<Permissions>() .write .check(&path, Some("Deno.utime()"))?;
tokio::task::spawn_blocking(move || { filetime::set_file_times(&path, atime, mtime).map_err(|err| { Error::new(err.kind(), format!("{}, utime '{}'", err, path.display())) })?; Ok(()) }) .await .unwrap()}
#[op]fn op_cwd(state: &mut OpState) -> Result<String, AnyError> { let path = current_dir()?; state.borrow_mut::<Permissions>().read.check_blind( &path, "CWD", "Deno.cwd()", )?; let path_str = into_string(path.into_os_string())?; Ok(path_str)}
#[op]fn op_readfile_sync( state: &mut OpState, path: String,) -> Result<ZeroCopyBuf, AnyError> { let permissions = state.borrow_mut::<Permissions>(); let path = Path::new(&path); permissions.read.check(path, Some("Deno.readFileSync()"))?; Ok(std::fs::read(path)?.into())}
#[op]fn op_readfile_text_sync( state: &mut OpState, path: String,) -> Result<String, AnyError> { let permissions = state.borrow_mut::<Permissions>(); let path = Path::new(&path); permissions .read .check(path, Some("Deno.readTextFileSync()"))?; Ok(string_from_utf8_lossy(std::fs::read(path)?))}
#[op]async fn op_readfile_async( state: Rc<RefCell<OpState>>, path: String, cancel_rid: Option<ResourceId>,) -> Result<ZeroCopyBuf, AnyError> { { let path = Path::new(&path); let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .read .check(path, Some("Deno.readFile()"))?; } let fut = tokio::task::spawn_blocking(move || { let path = Path::new(&path); Ok(std::fs::read(path).map(ZeroCopyBuf::from)?) }); if let Some(cancel_rid) = cancel_rid { let cancel_handle = state .borrow_mut() .resource_table .get::<CancelHandle>(cancel_rid); if let Ok(cancel_handle) = cancel_handle { return fut.or_cancel(cancel_handle).await??; } } fut.await?}
#[op]async fn op_readfile_text_async( state: Rc<RefCell<OpState>>, path: String, cancel_rid: Option<ResourceId>,) -> Result<String, AnyError> { { let path = Path::new(&path); let mut state = state.borrow_mut(); state .borrow_mut::<Permissions>() .read .check(path, Some("Deno.readTextFile()"))?; } let fut = tokio::task::spawn_blocking(move || { let path = Path::new(&path); Ok(string_from_utf8_lossy(std::fs::read(path)?)) }); if let Some(cancel_rid) = cancel_rid { let cancel_handle = state .borrow_mut() .resource_table .get::<CancelHandle>(cancel_rid); if let Ok(cancel_handle) = cancel_handle { return fut.or_cancel(cancel_handle).await??; } } fut.await?}
// Like String::from_utf8_lossy but operates on owned valuesfn string_from_utf8_lossy(buf: Vec<u8>) -> String { match String::from_utf8_lossy(&buf) { // buf contained non-utf8 chars than have been patched Cow::Owned(s) => s, // SAFETY: if Borrowed then the buf only contains utf8 chars, // we do this instead of .into_owned() to avoid copying the input buf Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) }, }}
Version Info