diff --git a/js/chmod.ts b/js/chmod.ts new file mode 100644 index 00000000000000..adc94d2c1c9336 --- /dev/null +++ b/js/chmod.ts @@ -0,0 +1,24 @@ +import * as fbs from "gen/msg_generated"; +import { flatbuffers } from "flatbuffers"; +import * as dispatch from "./dispatch"; +/** + * Synchronously changes the file permissions. + * + * import { chmodSync } from "deno"; + * chmodSync(path, mode); + */ +export function chmodSync(path: string, mode: number): void { + dispatch.sendSync(...req(path, mode)); +} +function req( + path: string, + mode: number +): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] { + const builder = new flatbuffers.Builder(); + const path_ = builder.createString(path); + fbs.Chmod.startChmod(builder); + fbs.Chmod.addPath(builder, path_); + fbs.Chmod.addMode(builder, mode); + const msg = fbs.Chmod.endChmod(builder); + return [builder, fbs.Any.Chmod, msg]; +} diff --git a/js/chmod_test.ts b/js/chmod_test.ts new file mode 100644 index 00000000000000..59983e7dfd851a --- /dev/null +++ b/js/chmod_test.ts @@ -0,0 +1,25 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +import { test, testPerm, assert, assertEqual } from "./test_util.ts"; +import * as deno from "deno"; +testPerm({ write: true }, function chmodSyncSuccess() { + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = deno.makeTempDirSync() + "/test.txt"; + deno.writeFileSync(filename, data, 0o666); + let fileInfo = deno.statSync(filename); + console.log(fileInfo.mode, 0o666); // This is printing 33206 438 + deno.chmodSync(filename, 0o777); + fileInfo = deno.statSync(filename); + assertEqual(fileInfo.mode, 0o777); +}); +testPerm({ write: false }, function chmodSyncPerm() { + let err; + try { + const filename = deno.makeTempDirSync() + "/test.txt"; + deno.chmodSync(filename, 0o777); + } catch (e) { + err = e; + } + assertEqual(err.kind, deno.ErrorKind.PermissionDenied); + assertEqual(err.name, "PermissionDenied"); +}); diff --git a/js/compiler.ts b/js/compiler.ts index f0f82ffb111fae..cf4d1906af83d3 100644 --- a/js/compiler.ts +++ b/js/compiler.ts @@ -418,10 +418,10 @@ export class DenoCompiler */ compile(moduleMetaData: ModuleMetaData): OutputCode { const recompile = !!this.recompile; - this._log( - "compiler.compile", - { filename: moduleMetaData.fileName, recompile } - ); + this._log("compiler.compile", { + filename: moduleMetaData.fileName, + recompile + }); if (!recompile && moduleMetaData.outputCode) { return moduleMetaData.outputCode; } diff --git a/js/compiler_test.ts b/js/compiler_test.ts index fef18ec3d26bca..e3be535bd45f4e 100644 --- a/js/compiler_test.ts +++ b/js/compiler_test.ts @@ -461,16 +461,24 @@ test(function compilerGetScriptFileNames() { test(function compilerRecompileFlag() { setup(); compilerInstance.run("foo/bar.ts", "/root/project"); - assertEqual(getEmitOutputStack.length, 1, "Expected only a single emitted file."); + assertEqual( + getEmitOutputStack.length, + 1, + "Expected only a single emitted file." + ); // running compiler against same file should use cached code compilerInstance.run("foo/bar.ts", "/root/project"); - assertEqual(getEmitOutputStack.length, 1, "Expected only a single emitted file."); + assertEqual( + getEmitOutputStack.length, + 1, + "Expected only a single emitted file." + ); compilerInstance.recompile = true; compilerInstance.run("foo/bar.ts", "/root/project"); assertEqual(getEmitOutputStack.length, 2, "Expected two emitted file."); assert( - getEmitOutputStack[0] === getEmitOutputStack[1], - "Expected same file to be emitted twice." + getEmitOutputStack[0] === getEmitOutputStack[1], + "Expected same file to be emitted twice." ); teardown(); }); diff --git a/js/deno.ts b/js/deno.ts index 3bbda69e8f23fd..3f5567138ab233 100644 --- a/js/deno.ts +++ b/js/deno.ts @@ -15,4 +15,5 @@ export { ErrorKind, DenoError } from "./errors"; export { libdeno } from "./libdeno"; export { arch, platform } from "./platform"; export { trace } from "./trace"; +export { chmodSync } from "./chmod"; export const args: string[] = []; diff --git a/js/read_link.ts b/js/read_link.ts index 6bd613389450c7..38db09d62f331b 100644 --- a/js/read_link.ts +++ b/js/read_link.ts @@ -24,9 +24,7 @@ export async function readlink(name: string): Promise { return res(await dispatch.sendAsync(...req(name))); } -function req( - name: string -): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] { +function req(name: string): [flatbuffers.Builder, fbs.Any, flatbuffers.Offset] { const builder = new flatbuffers.Builder(); const name_ = builder.createString(name); fbs.Readlink.startReadlink(builder); diff --git a/js/unit_tests.ts b/js/unit_tests.ts index 578a65c6da539a..1ea281cb527f74 100644 --- a/js/unit_tests.ts +++ b/js/unit_tests.ts @@ -2,6 +2,7 @@ // This test is executed as part of tools/test.py // But it can also be run manually: ./out/debug/deno js/unit_tests.ts import "./compiler_test.ts"; +import "./chmod_test.ts"; import "./console_test.ts"; import "./fetch_test.ts"; import "./os_test.ts"; diff --git a/src/handlers.rs b/src/handlers.rs index 964baabf8d8e93..a2175e0b2d746e 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -15,6 +15,8 @@ use msg; use remove_dir_all::remove_dir_all; use std; use std::fs; +extern crate libc; +use std::ffi::CString; #[cfg(any(unix))] use std::os::unix::fs::PermissionsExt; use std::path::Path; @@ -47,6 +49,7 @@ pub extern "C" fn msg_from_js(i: *const isolate, buf: deno_buf) { msg::Any::Start => handle_start, msg::Any::CodeFetch => handle_code_fetch, msg::Any::CodeCache => handle_code_cache, + msg::Any::Chmod => handle_chmod, msg::Any::Environ => handle_env, msg::Any::FetchReq => handle_fetch_req, msg::Any::TimerStart => handle_timer_start, @@ -147,7 +150,7 @@ fn permission_denied() -> DenoError { fn not_implemented() -> DenoError { DenoError::from(std::io::Error::new( std::io::ErrorKind::Other, - "Not implemented" + "Not implemented", )) } @@ -302,7 +305,8 @@ fn handle_env(i: *const isolate, base: &msg::Base) -> Box { ..Default::default() }, ) - }).collect(); + }) + .collect(); let tables = builder.create_vector(&vars); let msg = msg::EnvironRes::create( builder, @@ -415,7 +419,8 @@ where .and_then(|_| { cb(); Ok(()) - }).select(cancel_rx) + }) + .select(cancel_rx) .map(|_| ()) .map_err(|_| ()); @@ -704,6 +709,27 @@ fn handle_symlink(i: *const isolate, base: &msg::Base) -> Box { } } +fn handle_chmod(i: *const isolate, base: &msg::Base) -> Box { + let deno = from_c(i); + if !deno.flags.allow_write { + return odd_future(permission_denied()); + }; + let msg = base.msg_as_chmod().unwrap(); + let path = msg.path().unwrap(); + let mode = msg.mode(); + debug!("handle_chmod {} {}", path, mode); + if cfg!(target_family = "unix") { + Box::new(futures::future::result(|| -> OpResult { + let path_as_cstring = CString::new(path).unwrap(); + unsafe { + libc::chmod(path_as_cstring.as_ptr(), mode); + } + Ok(None) + }())) + } else { + Box::new(futures::future::result(|| -> OpResult { Ok(None) }())) + } +} fn handle_read_link(_i: *const isolate, base: &msg::Base) -> Box { let msg = base.msg_as_readlink().unwrap(); let cmd_id = base.cmd_id(); diff --git a/src/msg.fbs b/src/msg.fbs index 6358668b7ede37..478d8d2dcdd547 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -4,6 +4,7 @@ union Any { CodeFetch, CodeFetchRes, CodeCache, + Chmod, Exit, TimerStart, TimerReady, @@ -222,6 +223,12 @@ table Stat { lstat: bool; } +table Chmod { + path: string; + mode: uint32; +} + + table StatRes { is_file: bool; is_symlink: bool;