Skip to content

Commit 83d758a

Browse files
committed
Add chmod/chmodSync (and fix Cargo.toml)
1 parent 0501330 commit 83d758a

File tree

8 files changed

+226
-0
lines changed

8 files changed

+226
-0
lines changed

BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ main_extern = [
6969
ts_sources = [
7070
"js/assets.ts",
7171
"js/blob.ts",
72+
"js/chmod.ts",
7273
"js/compiler.ts",
7374
"js/console.ts",
7475
"js/copy_file.ts",

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ ring = "0.13.2"
2323
tempfile = "3"
2424
tokio = "0.1.11"
2525
url = "1.7.1"
26+
getopts = "0.2.18"

js/chmod.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
2+
import * as msg from "gen/msg_generated";
3+
import * as flatbuffers from "./flatbuffers";
4+
import * as dispatch from "./dispatch";
5+
6+
/** Changes the permission of a specific file/directory of specified path
7+
* synchronously.
8+
*
9+
* import { chmodSync } from "deno";
10+
* chmodSync("/path/to/file", 0o666);
11+
*/
12+
export function chmodSync(path: string, mode: number): void {
13+
dispatch.sendSync(...req(path, mode));
14+
}
15+
16+
/** Changes the permission of a specific file/directory of specified path.
17+
*
18+
* import { chmod } from "deno";
19+
* await chmod("/path/to/file", 0o666);
20+
*/
21+
export async function chmod(path: string, mode: number): Promise<void> {
22+
await dispatch.sendAsync(...req(path, mode));
23+
}
24+
25+
function req(
26+
path: string,
27+
mode: number
28+
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
29+
const builder = flatbuffers.createBuilder();
30+
const path_ = builder.createString(path);
31+
msg.Chmod.startChmod(builder);
32+
msg.Chmod.addPath(builder, path_);
33+
msg.Chmod.addMode(builder, mode);
34+
const inner = msg.Chmod.endChmod(builder);
35+
return [builder, msg.Any.Chmod, inner];
36+
}

js/chmod_test.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
2+
import { testPerm, assertEqual } from "./test_util.ts";
3+
import * as deno from "deno";
4+
5+
const isNotWindows = deno.platform.os !== "win";
6+
7+
testPerm({ write: true }, function chmodSyncSuccess() {
8+
const enc = new TextEncoder();
9+
const data = enc.encode("Hello");
10+
const tempDir = deno.makeTempDirSync();
11+
const filename = tempDir + "/test.txt";
12+
deno.writeFileSync(filename, data, 0o666);
13+
14+
let fileInfo = deno.statSync(filename);
15+
assertEqual(fileInfo.mode & 0o777, 0o666);
16+
17+
// On windows, no effect, but should not crash
18+
deno.chmodSync(filename, 0o777);
19+
20+
// Check success when not on windows
21+
if (isNotWindows) {
22+
fileInfo = deno.statSync(filename);
23+
assertEqual(fileInfo.mode & 0o777, 0o777);
24+
}
25+
});
26+
27+
// Check symlink when not on windows
28+
if (isNotWindows) {
29+
testPerm({ write: true }, function chmodSyncSymlinkSuccess() {
30+
const enc = new TextEncoder();
31+
const data = enc.encode("Hello");
32+
const tempDir = deno.makeTempDirSync();
33+
34+
const filename = tempDir + "/test.txt";
35+
deno.writeFileSync(filename, data, 0o666);
36+
const symlinkName = tempDir + "/test_symlink.txt";
37+
deno.symlink(filename, symlinkName);
38+
39+
let fileInfo = deno.statSync(filename);
40+
assertEqual(fileInfo.mode & 0o777, 0o666);
41+
let symlinkInfo = deno.lstatSync(symlinkName);
42+
const symlinkMode = symlinkInfo.mode & 0o777; // plaform dependent
43+
44+
deno.chmodSync(symlinkName, 0o777);
45+
46+
// Change actual file mode, not symlink
47+
fileInfo = deno.statSync(filename);
48+
assertEqual(fileInfo.mode & 0o777, 0o777);
49+
symlinkInfo = deno.lstatSync(symlinkName);
50+
assertEqual(symlinkInfo.mode & 0o777, symlinkMode);
51+
});
52+
}
53+
54+
testPerm({ write: true }, function chmodSyncFailure() {
55+
let err;
56+
try {
57+
const filename = "/badfile.txt";
58+
deno.chmodSync(filename, 0o777);
59+
} catch (e) {
60+
err = e;
61+
}
62+
assertEqual(err.kind, deno.ErrorKind.NotFound);
63+
assertEqual(err.name, "NotFound");
64+
});
65+
66+
testPerm({ write: false }, function chmodSyncPerm() {
67+
let err;
68+
try {
69+
const filename = deno.makeTempDirSync() + "/test.txt";
70+
deno.chmodSync(filename, 0o777);
71+
} catch (e) {
72+
err = e;
73+
}
74+
assertEqual(err.kind, deno.ErrorKind.PermissionDenied);
75+
assertEqual(err.name, "PermissionDenied");
76+
});
77+
78+
testPerm({ write: true }, async function chmodSuccess() {
79+
const enc = new TextEncoder();
80+
const data = enc.encode("Hello");
81+
const tempDir = deno.makeTempDirSync();
82+
const filename = tempDir + "/test.txt";
83+
deno.writeFileSync(filename, data, 0o666);
84+
85+
let fileInfo = deno.statSync(filename);
86+
assertEqual(fileInfo.mode & 0o777, 0o666);
87+
88+
// On windows, no effect, but should not crash
89+
await deno.chmod(filename, 0o777);
90+
91+
// Check success when not on windows
92+
if (isNotWindows) {
93+
fileInfo = deno.statSync(filename);
94+
assertEqual(fileInfo.mode & 0o777, 0o777);
95+
}
96+
});
97+
98+
// Check symlink when not on windows
99+
if (isNotWindows) {
100+
testPerm({ write: true }, async function chmodSymlinkSuccess() {
101+
const enc = new TextEncoder();
102+
const data = enc.encode("Hello");
103+
const tempDir = deno.makeTempDirSync();
104+
105+
const filename = tempDir + "/test.txt";
106+
deno.writeFileSync(filename, data, 0o666);
107+
const symlinkName = tempDir + "/test_symlink.txt";
108+
deno.symlink(filename, symlinkName);
109+
110+
let fileInfo = deno.statSync(filename);
111+
assertEqual(fileInfo.mode & 0o777, 0o666);
112+
let symlinkInfo = deno.lstatSync(symlinkName);
113+
const symlinkMode = symlinkInfo.mode & 0o777; // plaform dependent
114+
115+
await deno.chmod(symlinkName, 0o777);
116+
117+
// Just change actual file mode, not symlink
118+
fileInfo = deno.statSync(filename);
119+
assertEqual(fileInfo.mode & 0o777, 0o777);
120+
symlinkInfo = deno.lstatSync(symlinkName);
121+
assertEqual(symlinkInfo.mode & 0o777, symlinkMode);
122+
});
123+
}
124+
125+
testPerm({ write: true }, async function chmodFailure() {
126+
let err;
127+
try {
128+
const filename = "/badfile.txt";
129+
await deno.chmod(filename, 0o777);
130+
} catch (e) {
131+
err = e;
132+
}
133+
assertEqual(err.kind, deno.ErrorKind.NotFound);
134+
assertEqual(err.name, "NotFound");
135+
});
136+
137+
testPerm({ write: false }, async function chmodPerm() {
138+
let err;
139+
try {
140+
const filename = deno.makeTempDirSync() + "/test.txt";
141+
await deno.chmod(filename, 0o777);
142+
} catch (e) {
143+
err = e;
144+
}
145+
assertEqual(err.kind, deno.ErrorKind.PermissionDenied);
146+
assertEqual(err.name, "PermissionDenied");
147+
});

js/deno.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export {
2121
} from "./io";
2222
export { mkdirSync, mkdir } from "./mkdir";
2323
export { makeTempDirSync, makeTempDir } from "./make_temp_dir";
24+
export { chmodSync, chmod } from "./chmod";
2425
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
2526
export { renameSync, rename } from "./rename";
2627
export { readFileSync, readFile } from "./read_file";

js/unit_tests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import "./read_dir_test.ts";
1212
import "./write_file_test.ts";
1313
import "./copy_file_test.ts";
1414
import "./mkdir_test.ts";
15+
import "./chmod_test.ts";
1516
import "./dir_test";
1617
import "./make_temp_dir_test.ts";
1718
import "./stat_test.ts";

src/msg.fbs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ union Any {
1313
MakeTempDir,
1414
MakeTempDirRes,
1515
Mkdir,
16+
Chmod,
1617
Remove,
1718
ReadFile,
1819
ReadFileRes,
@@ -213,6 +214,12 @@ table Mkdir {
213214
// mode specified by https://godoc.org/os#FileMode
214215
}
215216

217+
table Chmod {
218+
path: string;
219+
mode: uint;
220+
// mode specified by https://godoc.org/os#FileMode
221+
}
222+
216223
table Remove {
217224
path: string;
218225
recursive: bool;

src/ops.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub fn dispatch(
7474
let op_creator: OpCreator = match inner_type {
7575
msg::Any::Accept => op_accept,
7676
msg::Any::Chdir => op_chdir,
77+
msg::Any::Chmod => op_chmod,
7778
msg::Any::Close => op_close,
7879
msg::Any::CodeCache => op_code_cache,
7980
msg::Any::CodeFetch => op_code_fetch,
@@ -572,6 +573,37 @@ fn op_mkdir(
572573
})
573574
}
574575

576+
#[allow(dead_code)]
577+
fn op_chmod(
578+
state: Arc<IsolateState>,
579+
base: &msg::Base,
580+
data: &'static mut [u8],
581+
) -> Box<Op> {
582+
assert_eq!(data.len(), 0);
583+
let inner = base.inner_as_chmod().unwrap();
584+
let mode = inner.mode();
585+
let path = String::from(inner.path().unwrap());
586+
587+
if !state.flags.allow_write {
588+
return odd_future(permission_denied());
589+
}
590+
591+
blocking!(base.sync(), || {
592+
debug!("op_chmod {}", &path);
593+
let path = PathBuf::from(&path);
594+
// Still check file/dir exists on windows
595+
let metadata = fs::metadata(&path)?;
596+
// Only work in unix
597+
#[cfg(any(unix))]
598+
{
599+
let mut permissions = metadata.permissions();
600+
permissions.set_mode(mode);
601+
fs::set_permissions(&path, permissions)?;
602+
}
603+
Ok(empty_buf())
604+
})
605+
}
606+
575607
fn op_open(
576608
_state: Arc<IsolateState>,
577609
base: &msg::Base,

0 commit comments

Comments
 (0)