Deno openSync 用 O_RDONLY | O_CREAT 报 write or append access
Deno 旧兼容实现拒绝 O_RDONLY 与 O_CREAT 的组合,2.6.10 已修正。区分创建授权与只读描述符,并验证新建、已有内容和权限边界。
移植 Node 程序时,openSync 使用 O_RDONLY | O_CREAT,Deno 却抛出下面的 TypeError:
creating or truncating a file requires write or append access
这里先区分两件事:应用是否被允许创建目标文件,以及运行时是否支持“缺失时创建,随后只读打开”这组标志。Deno 2.6.10 包含后一项兼容修正;旧实现即使已经获得所需文件权限,也可能因底层打开选项的组合而失败。
只读描述符仍可以带有创建意图
O_RDONLY 控制返回的文件描述符如何访问内容。O_CREAT 则要求目标不存在时创建文件;它并不承诺整个调用没有写入副作用。POSIX 与 Node 支持两者组合,不能把它简单等同于“只读取一个已经存在的文件”。
下面的示例要求 cache 目录已经存在,调用方获准在其中创建文件。0o600 只用于新建文件的模式,不会替已有文件重设权限:
import { openSync, closeSync, constants } from "node:fs";
const fd = openSync(
"./cache/state.bin",
constants.O_RDONLY | constants.O_CREAT,
0o600,
);
try {
console.log("opened", fd);
} finally {
closeSync(fd);
}
这段会在文件缺失时创建它。路径只是示例,不能用现有业务文件验证写入失败,也不能把打印出描述符当成内容已经读对。
受影响的 Deno 实现把这组标志直接交给 Rust 文件打开接口。后者要求创建选项配合写入或追加模式,因此在最终只读的组合上拒绝调用。把标志随意改成 O_RDWR 虽可能避开这个检查,却改变了调用者拿到的访问能力。
2.6.10 保留最终只读打开
正式版 Deno 2.6.10 对这组标志单独处理:缺失时先创建并关闭临时打开的文件,再按只读方式打开;已有文件不应因此被截断。它同时把创建意图纳入写入权限检查。
因此,更新之后若得到的变成 Deno 权限拒绝,要看程序原来是否真的获准创建该路径。只有读取权限的应用,应重新确认自己是否需要 O_CREAT;若文件本就必须预先存在,可只用 O_RDONLY,缺失时按业务约定报告错误。不能为了越过检查给整个进程开放所有文件写权限。
应通过实际脚本或服务使用的 Deno 更新运行时,保留原来的路径和权限范围。不要只修改系统默认命令,却继续运行内嵌了旧版本的编译产物。
在隔离目录中分别检查文件不存在和已经存在的情况:前者按约定创建,后者原内容不变;读取可用,返回的描述符仍不能写入。再检查一个未获许可的路径仍被拒绝。它们分别覆盖了创建语义、内容保护、描述符模式及权限边界。


