diff --git a/cli/js/deno.ts b/cli/js/deno.ts index 23fcfda5d8..f0115f2612 100644 --- a/cli/js/deno.ts +++ b/cli/js/deno.ts @@ -113,6 +113,7 @@ export { symlinkSync, symlink } from "./ops/fs/symlink.ts"; export { connectTLS, listenTLS } from "./tls.ts"; export { truncateSync, truncate } from "./ops/fs/truncate.ts"; export { isatty, setRaw } from "./ops/tty.ts"; +export { umask } from "./ops/fs/umask.ts"; export { utimeSync, utime } from "./ops/fs/utime.ts"; export { version } from "./version.ts"; export { writeFileSync, writeFile, WriteFileOptions } from "./write_file.ts"; diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index 8eb2e1f4d8..6950d90500 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -287,6 +287,14 @@ declare namespace Deno { */ export function chdir(directory: string): void; + /** + * **UNSTABLE**: New API. Maybe needs permissions. + * + * If `mask` is provided, sets the process umask. Always returns what the umask + * was before the call. + */ + export function umask(mask?: number): number; + /** **UNSTABLE**: might move to `Deno.symbols`. */ export const EOF: unique symbol; export type EOF = typeof EOF; diff --git a/cli/js/ops/fs/umask.ts b/cli/js/ops/fs/umask.ts new file mode 100644 index 0000000000..ee56fecb56 --- /dev/null +++ b/cli/js/ops/fs/umask.ts @@ -0,0 +1,12 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { sendSync } from "../dispatch_json.ts"; + +/** + * **UNSTABLE**: maybe needs `allow-env` permissions. + * + * If `mask` is provided, sets the process umask. Always returns what the umask + * was before the call. + */ +export function umask(mask?: number): number { + return sendSync("op_umask", { mask }); +} diff --git a/cli/js/tests/umask_test.ts b/cli/js/tests/umask_test.ts new file mode 100644 index 0000000000..543372a464 --- /dev/null +++ b/cli/js/tests/umask_test.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { unitTest, assertEquals } from "./test_util.ts"; + +unitTest( + { + skip: Deno.build.os === "win" + }, + function umaskSuccess(): void { + const prevMask = Deno.umask(0o020); + const newMask = Deno.umask(prevMask); + const finalMask = Deno.umask(); + assertEquals(newMask, 0o020); + assertEquals(finalMask, prevMask); + assertEquals(prevMask, 0o022); + } +); diff --git a/cli/js/tests/unit_tests.ts b/cli/js/tests/unit_tests.ts index cc51e6ade5..9ae724d535 100644 --- a/cli/js/tests/unit_tests.ts +++ b/cli/js/tests/unit_tests.ts @@ -57,6 +57,7 @@ import "./timers_test.ts"; import "./tls_test.ts"; import "./truncate_test.ts"; import "./tty_test.ts"; +import "./umask_test.ts"; import "./url_test.ts"; import "./url_search_params_test.ts"; import "./utime_test.ts"; diff --git a/cli/op_error.rs b/cli/op_error.rs index a2cf03a661..bebfd2e723 100644 --- a/cli/op_error.rs +++ b/cli/op_error.rs @@ -71,6 +71,10 @@ impl OpError { Self::new(ErrorKind::NotFound, msg) } + pub fn not_implemented() -> Self { + Self::other("not implemented".to_string()) + } + pub fn other(msg: String) -> Self { Self::new(ErrorKind::Other, msg) } diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs index ad1283c122..df5ad7bef1 100644 --- a/cli/ops/fs.rs +++ b/cli/ops/fs.rs @@ -23,6 +23,7 @@ use std::os::unix::fs::{MetadataExt, PermissionsExt}; pub fn init(i: &mut Isolate, s: &State) { i.register_op("op_open", s.stateful_json_op(op_open)); i.register_op("op_seek", s.stateful_json_op(op_seek)); + i.register_op("op_umask", s.stateful_json_op(op_umask)); i.register_op("op_chdir", s.stateful_json_op(op_chdir)); i.register_op("op_mkdir", s.stateful_json_op(op_mkdir)); i.register_op("op_chmod", s.stateful_json_op(op_chmod)); @@ -221,6 +222,43 @@ fn op_seek( } } +#[derive(Deserialize)] +struct UmaskArgs { + mask: Option, +} + +fn op_umask( + _state: &State, + args: Value, + _zero_copy: Option, +) -> Result { + 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. + return Err(OpError::not_implemented()); + } + #[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(JsonOp::Sync(json!(r.bits() as u32))) + } +} + #[derive(Deserialize)] struct ChdirArgs { directory: String,