Skip to content

Commit c31b1ba

Browse files
committed
dofs: Guard read-only root mounts
Treat every absolute path as a descendant when the read-only mount root is the filesystem root.
1 parent 4aae82a commit c31b1ba

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

packages/dofs/src/fs/mount-guard.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ describe("mount-guard helpers", () => {
110110
});
111111
});
112112

113+
it("treats every path as a descendant of a read-only root mount", async () => {
114+
await withDB((db) => {
115+
stageMount(db, "/", "read-only");
116+
117+
expect(() => assertNotReadOnly(db, "/child")).toThrowError(
118+
expect.objectContaining({ code: "EROFS" }),
119+
);
120+
});
121+
});
122+
113123
it("read-write mounts do not register as read-only", async () => {
114124
await withDB(async (db) => {
115125
stageMount(db, "/workspace/rw", "read-write");
@@ -120,6 +130,21 @@ describe("mount-guard helpers", () => {
120130
});
121131

122132
describe("writeFile under a read-only mount", () => {
133+
it("rejects direct and symlinked writes under a read-only root mount", async () => {
134+
await withDB((db) => {
135+
mkdir(db, "/actual", {}, () => 0);
136+
symlink(db, "/actual", "/link", () => 0);
137+
stageMount(db, "/", "read-only");
138+
139+
expect(() => writeFileSync(db, "/direct.txt", new Uint8Array([1]), {}, () => 0)).toThrowError(
140+
expect.objectContaining({ code: "EROFS" }),
141+
);
142+
expect(() =>
143+
writeFileSync(db, "/link/through.txt", new Uint8Array([1]), {}, () => 0),
144+
).toThrowError(expect.objectContaining({ code: "EROFS" }));
145+
});
146+
});
147+
123148
it("rejects a streaming write under the mount root with EROFS", async () => {
124149
await withDB(async (db) => {
125150
// Materialise the directory before flipping the mount to

packages/dofs/src/fs/mount-guard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function getReadOnlyMountRoots(db: Database): readonly string[] {
5252
// vectors. Mirrors the predicate that lived in
5353
// GuardedWorkspaceFilesystem before the data-layer move.
5454
function isAtOrBelowRoot(path: string, root: string): boolean {
55-
return path === root || path.startsWith(`${root}/`);
55+
return root === "/" || path === root || path.startsWith(`${root}/`);
5656
}
5757

5858
function overlapsRoot(path: string, root: string): boolean {

0 commit comments

Comments
 (0)