summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2018-08-24 00:36:45 +0200
committerBert Belder <bertbelder@gmail.com>2018-08-29 22:40:05 +0200
commitceaf82268282d16b97101c00c75612745de416bb (patch)
tree6294eeeb063ca2196ef285ad7f125437cbc3bd45
parenta836c493f30323e7b40e988140ed2603f0e3d10f (diff)
Implement makeTempDirSync()
-rw-r--r--BUILD.gn1
-rw-r--r--Cargo.toml1
-rw-r--r--js/deno.ts2
-rw-r--r--js/os.ts51
-rw-r--r--js/unit_tests.ts69
-rw-r--r--src/fs.rs32
-rw-r--r--src/handlers.rs44
-rw-r--r--src/main.rs1
-rw-r--r--src/msg.fbs12
9 files changed, 188 insertions, 25 deletions
diff --git a/BUILD.gn b/BUILD.gn
index c5f28dcbe..2e487738f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -43,6 +43,7 @@ main_extern = [
"$rust_build:log",
"$rust_build:sha1",
"$rust_build:tempfile",
+ "$rust_build:rand",
"$rust_build:tokio",
"$rust_build:tokio_current_thread",
"$rust_build:url",
diff --git a/Cargo.toml b/Cargo.toml
index 7d73ede4c..c6ebbbf15 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,7 @@ version = "0.0.0"
url = "1.7.1"
libc = "0.2.42"
log = "0.4.3"
+rand = "0.5.4"
sha1 = "0.6.0"
tempfile = "3"
tokio = {git = "https://github.com/tokio-rs/tokio.git", rev = "5d0d2a2e1214f856993df6965825c89bfcaa879e"}
diff --git a/js/deno.ts b/js/deno.ts
index 63bee0b7c..7a5c4217e 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -1,5 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
// Public deno module.
-export { exit, readFileSync, writeFileSync } from "./os";
+export { exit, makeTempDirSync, readFileSync, writeFileSync } from "./os";
export { libdeno } from "./libdeno";
export const argv: string[] = [];
diff --git a/js/os.ts b/js/os.ts
index 3104c5013..10ba32f9d 100644
--- a/js/os.ts
+++ b/js/os.ts
@@ -85,6 +85,57 @@ export function codeCache(
}
}
+/**
+ * makeTempDirSync creates a new temporary directory in the directory `dir`, its
+ * name beginning with `prefix` and ending with `suffix`.
+ * It returns the full path to the newly created directory.
+ * If `dir` is unspecified, tempDir uses the default directory for temporary
+ * files. Multiple programs calling tempDir simultaneously will not choose the
+ * same directory. It is the caller's responsibility to remove the directory
+ * when no longer needed.
+ */
+export interface MakeTempDirOptions {
+ dir?: string;
+ prefix?: string;
+ suffix?: string;
+}
+export function makeTempDirSync({
+ dir,
+ prefix,
+ suffix
+}: MakeTempDirOptions = {}): string {
+ const builder = new flatbuffers.Builder();
+ const fbDir = dir == null ? -1 : builder.createString(dir);
+ const fbPrefix = prefix == null ? -1 : builder.createString(prefix);
+ const fbSuffix = suffix == null ? -1 : builder.createString(suffix);
+ fbs.MakeTempDir.startMakeTempDir(builder);
+ if (dir != null) {
+ fbs.MakeTempDir.addDir(builder, fbDir);
+ }
+ if (prefix != null) {
+ fbs.MakeTempDir.addPrefix(builder, fbPrefix);
+ }
+ if (suffix != null) {
+ fbs.MakeTempDir.addSuffix(builder, fbSuffix);
+ }
+ const msg = fbs.MakeTempDir.endMakeTempDir(builder);
+ fbs.Base.startBase(builder);
+ fbs.Base.addMsg(builder, msg);
+ fbs.Base.addMsgType(builder, fbs.Any.MakeTempDir);
+ builder.finish(fbs.Base.endBase(builder));
+ const resBuf = libdeno.send(builder.asUint8Array());
+ assert(resBuf != null);
+ const bb = new flatbuffers.ByteBuffer(new Uint8Array(resBuf!));
+ const baseRes = fbs.Base.getRootAsBase(bb);
+ maybeThrowError(baseRes);
+ assert(fbs.Any.MakeTempDirRes === baseRes.msgType());
+ const res = new fbs.MakeTempDirRes();
+ assert(baseRes.msg(res) != null);
+ const path = res.path();
+ assert(path != null);
+ return path!;
+}
+
export function readFileSync(filename: string): Uint8Array {
/* Ideally we could write
const res = send({
diff --git a/js/unit_tests.ts b/js/unit_tests.ts
index 17ddd5256..92957c493 100644
--- a/js/unit_tests.ts
+++ b/js/unit_tests.ts
@@ -41,16 +41,16 @@ test(function tests_readFileSync_NotFound() {
});
*/
-/* TODO(ry) Add this once we can create a tmpDir to write the file into.
-test(function writeFileSyncSuccess() {
+testPerm({ write: true }, function writeFileSync() {
const enc = new TextEncoder();
- const dataWritten = enc.encode("Hello");
- const filename = "TEMPDIR/test.txt";
- deno.writeFileSync(filename, dataWritten, 0o666);
+ const data = enc.encode("Hello");
+ const filename = deno.makeTempDirSync() + "/test.txt";
+ deno.writeFileSync(filename, data, 0o666);
const dataRead = deno.readFileSync(filename);
- assertEqual(dataRead, dataWritten);
+ const dec = new TextDecoder("utf-8");
+ const actual = dec.decode(dataRead);
+ assertEqual("Hello", actual);
});
-*/
// For this test to pass we need --allow-write permission.
// Otherwise it will fail with deno.PermissionDenied instead of deno.NotFound.
@@ -70,23 +70,48 @@ testPerm({ write: true }, function writeFileSyncFail() {
assert(caughtError);
});
+testPerm({ write: true }, function makeTempDirSync() {
+ const dir1 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
+ const dir2 = deno.makeTempDirSync({ prefix: "hello", suffix: "world" });
+ // Check that both dirs are different.
+ assert(dir1 != dir2);
+ for (const dir of [dir1, dir2]) {
+ // Check that the prefix and suffix are applied.
+ const lastPart = dir.replace(/^.*[\\\/]/, "");
+ assert(lastPart.startsWith("hello"));
+ assert(lastPart.endsWith("world"));
+ }
+ // Check that the `dir` option works.
+ const dir3 = deno.makeTempDirSync({ dir: dir1 });
+ assert(dir3.startsWith(dir1));
+ assert(/^[\\\/]/.test(dir3.slice(dir1.length)));
+ // Check that creating a temp dir inside a nonexisting directory fails.
+ let err;
+ try {
+ deno.makeTempDirSync({ dir: "/baddir" });
+ } catch (err_) {
+ err = err_;
+ }
+ // TODO assert(err instanceof deno.NotFound).
+ assert(err);
+ assertEqual(err.name, "deno.NotFound");
+});
+
+test(function makeTempDirSyncPerm() {
+ // makeTempDirSync should require write permissions (for now).
+ let err;
+ try {
+ deno.makeTempDirSync({ dir: "/baddir" });
+ } catch (err_) {
+ err = err_;
+ }
+ // TODO assert(err instanceof deno.PermissionDenied).
+ assert(err);
+ assertEqual(err.name, "deno.PermissionDenied");
+});
+
testPerm({ net: true }, async function tests_fetch() {
const response = await fetch("http://localhost:4545/package.json");
const json = await response.json();
assertEqual(json.name, "deno");
});
-
-/*
-test(async function tests_writeFileSync() {
- const enc = new TextEncoder();
- const data = enc.encode("Hello");
- // TODO need ability to get tmp dir.
- const fn = "/tmp/test.txt";
- writeFileSync("/tmp/test.txt", data, 0o666);
- const dataRead = deno.readFileSync("/tmp/test.txt");
- const dec = new TextDecoder("utf-8");
- const actual = dec.decode(dataRead);
- assertEqual("Hello", actual);
-});
-
-*/
diff --git a/src/fs.rs b/src/fs.rs
index 50c99c593..786c35691 100644
--- a/src/fs.rs
+++ b/src/fs.rs
@@ -1,13 +1,41 @@
use std;
-use std::fs::File;
+use std::fs::{create_dir, File};
+use std::io::ErrorKind;
use std::io::Write;
-use std::path::Path;
+use std::path::{Path, PathBuf};
+
+use rand;
+use rand::RngCore;
pub fn write_file_sync(path: &Path, content: &[u8]) -> std::io::Result<()> {
let mut f = File::create(path)?;
f.write_all(content)
}
+pub fn make_temp_dir(
+ dir: Option<&Path>,
+ prefix: Option<&str>,
+ suffix: Option<&str>,
+) -> 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 => std::env::temp_dir(),
+ }.join("_");
+ loop {
+ let unique = rand::thread_rng().next_u32();
+ buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_));
+ // TODO: on posix, set mode flags to 0o700.
+ let r = create_dir(buf.as_path());
+ match r {
+ Err(ref e) if e.kind() == ErrorKind::AlreadyExists => continue,
+ Ok(_) => return Ok(buf),
+ Err(e) => return Err(e),
+ }
+ }
+}
+
pub fn mkdir(path: &Path) -> std::io::Result<()> {
debug!("mkdir -p {}", path.display());
assert!(path.has_root(), "non-has_root not yet implemented");
diff --git a/src/handlers.rs b/src/handlers.rs
index 4638288ca..866ca8b3a 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -65,6 +65,13 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
let msg = msg::Exit::init_from_table(base.msg().unwrap());
std::process::exit(msg.code())
}
+ msg::Any::MakeTempDir => {
+ let msg = msg::MakeTempDir::init_from_table(base.msg().unwrap());
+ let dir = msg.dir();
+ let prefix = msg.prefix();
+ let suffix = msg.suffix();
+ handle_make_temp_dir(d, &mut builder, dir, prefix, suffix)
+ }
msg::Any::ReadFileSync => {
// TODO base.msg_as_ReadFileSync();
let msg = msg::ReadFileSync::init_from_table(base.msg().unwrap());
@@ -395,6 +402,43 @@ fn send_timer_ready(d: *const DenoC, timer_id: u32, done: bool) {
);
}
+fn handle_make_temp_dir(
+ d: *const DenoC,
+ builder: &mut FlatBufferBuilder,
+ dir: Option<&str>,
+ prefix: Option<&str>,
+ suffix: Option<&str>,
+) -> HandlerResult {
+ let deno = from_c(d);
+ if !deno.flags.allow_write {
+ let err = std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "allow_write is off.",
+ );
+ return Err(err.into());
+ }
+ // 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(dir.map(Path::new), prefix, suffix)?;
+ let path_off = builder.create_string(path.to_str().unwrap());
+ let msg = msg::MakeTempDirRes::create(
+ builder,
+ &msg::MakeTempDirResArgs {
+ path: Some(path_off),
+ ..Default::default()
+ },
+ );
+ Ok(create_msg(
+ builder,
+ &msg::BaseArgs {
+ msg: Some(flatbuffers::Offset::new(msg.value())),
+ msg_type: msg::Any::MakeTempDirRes,
+ ..Default::default()
+ },
+ ))
+}
+
// Prototype https://github.com/denoland/deno/blob/golang/os.go#L171-L184
fn handle_read_file_sync(
_d: *const DenoC,
diff --git a/src/main.rs b/src/main.rs
index 28e852fdc..6a013bea0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@ extern crate futures;
extern crate hyper;
extern crate libc;
extern crate msg_rs as msg_generated;
+extern crate rand;
extern crate sha1;
extern crate tempfile;
extern crate tokio;
diff --git a/src/msg.fbs b/src/msg.fbs
index a20af6892..162a8ddee 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -12,6 +12,8 @@ union Any {
TimerClear,
FetchReq,
FetchRes,
+ MakeTempDir,
+ MakeTempDirRes,
ReadFileSync,
ReadFileSyncRes,
WriteFileSync,
@@ -133,6 +135,16 @@ table FetchRes {
body: [ubyte];
}
+table MakeTempDir {
+ dir: string;
+ prefix: string;
+ suffix: string;
+}
+
+table MakeTempDirRes {
+ path: string;
+}
+
table ReadFileSync {
filename: string;
}