@openclaw/fs-safe 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +28 -6
- package/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-kind.d.ts +0 -1
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +5 -17
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +6 -7
- package/dist/archive-tar-stream.d.ts +3 -0
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +56 -37
- package/dist/archive-tar-wasm.d.ts +16 -4
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +134 -34
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +5 -6
- package/dist/clone-metadata.d.ts +1 -0
- package/dist/clone-metadata.d.ts.map +1 -1
- package/dist/clone-metadata.js +6 -2
- package/dist/create-directory.d.ts +20 -0
- package/dist/create-directory.d.ts.map +1 -0
- package/dist/create-directory.js +130 -0
- package/dist/create-file-async.d.ts +7 -0
- package/dist/create-file-async.d.ts.map +1 -0
- package/dist/create-file-async.js +121 -0
- package/dist/create-file.d.ts +8 -0
- package/dist/create-file.d.ts.map +1 -0
- package/dist/create-file.js +190 -0
- package/dist/create-owned-file.d.ts +8 -0
- package/dist/create-owned-file.d.ts.map +1 -0
- package/dist/create-owned-file.js +16 -0
- package/dist/create.d.ts +4 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +2 -0
- package/dist/creation-darwin.d.ts +7 -0
- package/dist/creation-darwin.d.ts.map +1 -0
- package/dist/creation-darwin.js +79 -0
- package/dist/creation-file-state.d.ts +19 -0
- package/dist/creation-file-state.d.ts.map +1 -0
- package/dist/creation-file-state.js +118 -0
- package/dist/creation-path.d.ts +21 -0
- package/dist/creation-path.d.ts.map +1 -0
- package/dist/creation-path.js +71 -0
- package/dist/creation-permissions.d.ts +19 -0
- package/dist/creation-permissions.d.ts.map +1 -0
- package/dist/creation-permissions.js +125 -0
- package/dist/directory-durability.d.ts +1 -1
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +22 -80
- package/dist/directory-guard.d.ts +3 -0
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-mode-node.d.ts +2 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +8 -0
- package/dist/directory-receipt.d.ts +24 -0
- package/dist/directory-receipt.d.ts.map +1 -0
- package/dist/directory-receipt.js +127 -0
- package/dist/file-cleanup.d.ts +19 -0
- package/dist/file-cleanup.d.ts.map +1 -0
- package/dist/file-cleanup.js +78 -0
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -6
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +3 -9
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +2 -5
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +36 -7
- package/dist/move-path.js +1 -1
- package/dist/native-binding.d.ts +11 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-fallback-warning.d.ts +4 -0
- package/dist/native-fallback-warning.d.ts.map +1 -0
- package/dist/native-fallback-warning.js +11 -0
- package/dist/native-operations.d.ts +0 -2
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +0 -24
- package/dist/native-parent-admission.d.ts +2 -0
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +3 -2
- package/dist/native-pinned-write-windows.d.ts +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +173 -28
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +19 -3
- package/dist/native-policy-parent-windows.d.ts.map +1 -1
- package/dist/native-policy-parent-windows.js +15 -6
- package/dist/native-staged-file.d.ts +3 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +86 -39
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +10 -4
- package/dist/pinned-write-input.d.ts +4 -0
- package/dist/pinned-write-input.d.ts.map +1 -0
- package/dist/pinned-write-input.js +25 -0
- package/dist/pinned-write-mode.d.ts +5 -0
- package/dist/pinned-write-mode.d.ts.map +1 -0
- package/dist/pinned-write-mode.js +24 -0
- package/dist/pinned-write-staged.d.ts +6 -0
- package/dist/pinned-write-staged.d.ts.map +1 -0
- package/dist/pinned-write-staged.js +187 -0
- package/dist/pinned-write-types.d.ts +3 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +35 -145
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +18 -4
- package/dist/private-producer-handoff-sync.d.ts +14 -0
- package/dist/private-producer-handoff-sync.d.ts.map +1 -0
- package/dist/private-producer-handoff-sync.js +114 -0
- package/dist/private-producer-handoff.d.ts +22 -4
- package/dist/private-producer-handoff.d.ts.map +1 -1
- package/dist/private-producer-handoff.js +140 -77
- package/dist/publish-copy-stage.d.ts +2 -1
- package/dist/publish-copy-stage.d.ts.map +1 -1
- package/dist/publish-copy-stage.js +16 -7
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +2 -2
- package/dist/replace-file-temp-owner.d.ts +0 -7
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +3 -54
- package/dist/root-create-input.d.ts +2 -1
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +13 -4
- package/dist/root-directory-creation.d.ts +3 -3
- package/dist/root-directory-creation.d.ts.map +1 -1
- package/dist/root-directory-creation.js +15 -3
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +28 -7
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +22 -13
- package/dist/root-options.d.ts +12 -4
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-stat.d.ts.map +1 -1
- package/dist/root-path-stat.js +59 -7
- package/dist/root-write-publication.js +1 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secure-file-windows.d.ts +6 -0
- package/dist/secure-file-windows.d.ts.map +1 -1
- package/dist/secure-file-windows.js +34 -117
- package/dist/secure-file.js +2 -2
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +2 -1
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +6 -6
- package/dist/staged-file-settlement.d.ts +17 -0
- package/dist/staged-file-settlement.d.ts.map +1 -0
- package/dist/staged-file-settlement.js +57 -0
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +2 -1
- package/dist/windows-security-bridge.cs +336 -0
- package/dist/windows-security-bridge.ps1 +15 -0
- package/dist/windows-security-command.d.ts +26 -0
- package/dist/windows-security-command.d.ts.map +1 -0
- package/dist/windows-security-command.js +363 -0
- package/dist/windows-security-facts.d.ts +6 -0
- package/dist/windows-security-facts.d.ts.map +1 -0
- package/dist/windows-security-facts.js +108 -0
- package/docs/advanced.md +3 -1
- package/docs/archive.md +61 -37
- package/docs/config.md +6 -2
- package/docs/contributing.md +44 -4
- package/docs/copy.md +2 -0
- package/docs/creation.md +128 -0
- package/docs/durability.md +24 -0
- package/docs/install.md +31 -7
- package/docs/migrating-to-0.5.md +15 -6
- package/docs/migrating-to-0.6.md +9 -4
- package/docs/native-helper.md +22 -9
- package/docs/native.md +38 -7
- package/docs/permissions.md +37 -14
- package/docs/root.md +34 -0
- package/docs/secret-file.md +11 -2
- package/docs/secure-file.md +9 -4
- package/docs/sidecar-lock.md +5 -4
- package/docs/staged-file.md +5 -0
- package/docs/writing.md +71 -5
- package/package.json +18 -15
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
import { getNativeBinding } from "./native.js";
|
|
4
|
+
import { getFsSafeNativeConfig } from "./native-config.js";
|
|
5
|
+
import { warnNativeFallback } from "./native-fallback-warning.js";
|
|
6
|
+
import { inspectWindowsDirectoryCommand, inspectWindowsDirectoryCommandSync, protectPrivateWindowsFileCommand, protectPrivateWindowsFileCommandSync, verifyPrivateWindowsFileCommand, verifyPrivateWindowsFileCommandSync, } from "./windows-security-command.js";
|
|
7
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
8
|
+
import { assertDarwinCreationDirectoryAcl, assertDarwinPrivateCreationAvailable, assertDarwinPrivateDirectoryMode } from "./creation-darwin.js";
|
|
9
|
+
export function resolveCreationPermissions(options, directory) {
|
|
10
|
+
const privatePath = options.private;
|
|
11
|
+
const mode = options.mode;
|
|
12
|
+
if (privatePath !== undefined && typeof privatePath !== "boolean") {
|
|
13
|
+
throw new TypeError("private must be a boolean");
|
|
14
|
+
}
|
|
15
|
+
if (mode !== undefined && (!Number.isInteger(mode) || mode < 0 || mode > 0o7777)) {
|
|
16
|
+
throw new RangeError("mode must contain only permission bits");
|
|
17
|
+
}
|
|
18
|
+
if (privatePath && mode !== undefined && (mode & 0o7077) !== 0) {
|
|
19
|
+
throw new FsSafeError("insecure-permissions", "private creation requires owner-only permission bits");
|
|
20
|
+
}
|
|
21
|
+
if (privatePath)
|
|
22
|
+
assertDarwinPrivateCreationAvailable();
|
|
23
|
+
if (privatePath && directory)
|
|
24
|
+
assertDarwinPrivateDirectoryMode(mode ?? 0o700);
|
|
25
|
+
return { private: privatePath === true, mode: mode ?? (privatePath ? directory ? 0o700 : 0o600 : undefined) };
|
|
26
|
+
}
|
|
27
|
+
function assertPrivatePosixDirectory(stat) {
|
|
28
|
+
if (typeof process.getuid !== "function" || stat.uid !== BigInt(process.getuid())) {
|
|
29
|
+
throw new FsSafeError("not-owned", "private directory must belong to the current user");
|
|
30
|
+
}
|
|
31
|
+
if ((stat.mode & 3647n) !== 0n) {
|
|
32
|
+
throw new FsSafeError("insecure-permissions", "existing directory is not private");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function unavailable() {
|
|
36
|
+
throw new FsSafeError("helper-unavailable", "private Windows creation requires an up-to-date native helper");
|
|
37
|
+
}
|
|
38
|
+
export function assertPrivateFileCreationAvailable() {
|
|
39
|
+
const native = getNativeBinding();
|
|
40
|
+
if (process.platform !== "win32" || getFsSafeNativeConfig().mode !== "require")
|
|
41
|
+
return;
|
|
42
|
+
if (typeof native?.createPrivateDirectoryWithParentIdentity !== "function" ||
|
|
43
|
+
typeof native.inspectWindowsDirectory !== "function" ||
|
|
44
|
+
typeof native.protectPrivateWindowsFile !== "function" ||
|
|
45
|
+
typeof native.verifyPrivateWindowsFile !== "function")
|
|
46
|
+
unavailable();
|
|
47
|
+
}
|
|
48
|
+
function commandFallback() {
|
|
49
|
+
if (getFsSafeNativeConfig().mode === "require")
|
|
50
|
+
unavailable();
|
|
51
|
+
warnNativeFallback("windows-private-creation", "Private Windows creation uses a slower built-in system command.");
|
|
52
|
+
}
|
|
53
|
+
/** Full Windows identity, with security validation when requested; no repairs. */
|
|
54
|
+
export function inspectCreationDirectorySync(targetPath, privatePath) {
|
|
55
|
+
assertNoWindowsPathAlias(targetPath, "filesystem");
|
|
56
|
+
const native = getNativeBinding();
|
|
57
|
+
if (typeof native?.inspectWindowsDirectory === "function") {
|
|
58
|
+
return native.inspectWindowsDirectory(targetPath, privatePath).identity;
|
|
59
|
+
}
|
|
60
|
+
commandFallback();
|
|
61
|
+
return inspectWindowsDirectoryCommandSync(targetPath, privatePath).identity;
|
|
62
|
+
}
|
|
63
|
+
export async function inspectCreationDirectory(targetPath, privatePath) {
|
|
64
|
+
assertNoWindowsPathAlias(targetPath, "filesystem");
|
|
65
|
+
const native = getNativeBinding();
|
|
66
|
+
if (typeof native?.inspectWindowsDirectory === "function") {
|
|
67
|
+
return native.inspectWindowsDirectory(targetPath, privatePath).identity;
|
|
68
|
+
}
|
|
69
|
+
commandFallback();
|
|
70
|
+
return (await inspectWindowsDirectoryCommand(targetPath, privatePath)).identity;
|
|
71
|
+
}
|
|
72
|
+
export function assertPrivateDirectorySync(targetPath) {
|
|
73
|
+
const stat = inspectDirectoryIdentitySync(targetPath);
|
|
74
|
+
if (process.platform === "win32")
|
|
75
|
+
inspectCreationDirectorySync(targetPath, true);
|
|
76
|
+
else {
|
|
77
|
+
assertPrivatePosixDirectory(stat);
|
|
78
|
+
assertDarwinCreationDirectoryAcl(targetPath, stat);
|
|
79
|
+
}
|
|
80
|
+
inspectDirectoryIdentitySync(targetPath, stat);
|
|
81
|
+
}
|
|
82
|
+
export async function assertPrivateDirectory(targetPath) {
|
|
83
|
+
const stat = inspectDirectoryIdentitySync(targetPath);
|
|
84
|
+
if (process.platform === "win32")
|
|
85
|
+
await inspectCreationDirectory(targetPath, true);
|
|
86
|
+
else {
|
|
87
|
+
assertPrivatePosixDirectory(stat);
|
|
88
|
+
assertDarwinCreationDirectoryAcl(targetPath, stat);
|
|
89
|
+
}
|
|
90
|
+
inspectDirectoryIdentitySync(targetPath, stat);
|
|
91
|
+
}
|
|
92
|
+
export function protectCreatedFileSync(fd, targetPath, parentIdentity) {
|
|
93
|
+
const native = getNativeBinding();
|
|
94
|
+
if (typeof native?.protectPrivateWindowsFile === "function") {
|
|
95
|
+
return native.protectPrivateWindowsFile(fd, targetPath, parentIdentity).identity;
|
|
96
|
+
}
|
|
97
|
+
commandFallback();
|
|
98
|
+
return protectPrivateWindowsFileCommandSync(fd, targetPath, parentIdentity).identity;
|
|
99
|
+
}
|
|
100
|
+
export async function protectCreatedFile(fd, targetPath, parentIdentity) {
|
|
101
|
+
const native = getNativeBinding();
|
|
102
|
+
if (typeof native?.protectPrivateWindowsFile === "function") {
|
|
103
|
+
return native.protectPrivateWindowsFile(fd, targetPath, parentIdentity).identity;
|
|
104
|
+
}
|
|
105
|
+
commandFallback();
|
|
106
|
+
return (await protectPrivateWindowsFileCommand(fd, targetPath, parentIdentity)).identity;
|
|
107
|
+
}
|
|
108
|
+
export function verifyCreatedFileSync(fd, targetPath, identity, parentIdentity, links = 1) {
|
|
109
|
+
const native = getNativeBinding();
|
|
110
|
+
if (typeof native?.verifyPrivateWindowsFile === "function") {
|
|
111
|
+
native.verifyPrivateWindowsFile(fd, targetPath, identity, parentIdentity, links);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
commandFallback();
|
|
115
|
+
verifyPrivateWindowsFileCommandSync(fd, targetPath, identity, parentIdentity, links);
|
|
116
|
+
}
|
|
117
|
+
export async function verifyCreatedFile(fd, targetPath, identity, parentIdentity, links = 1) {
|
|
118
|
+
const native = getNativeBinding();
|
|
119
|
+
if (typeof native?.verifyPrivateWindowsFile === "function") {
|
|
120
|
+
native.verifyPrivateWindowsFile(fd, targetPath, identity, parentIdentity, links);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
commandFallback();
|
|
124
|
+
await verifyPrivateWindowsFileCommand(fd, targetPath, identity, parentIdentity, links);
|
|
125
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-durability.d.ts","sourceRoot":"","sources":["../src/directory-durability.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,OAAO,
|
|
1
|
+
{"version":3,"file":"directory-durability.d.ts","sourceRoot":"","sources":["../src/directory-durability.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAa3D,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,GAAG;IACvD,UAAU,EAAE,oBAAoB,GAAG;QAAE,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB,CAAC,EAAE,gBAAgB,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAkHF,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,CAe1B;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,oBAAoB,CAkCtB;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAMvE;AAiCD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA6FlC"}
|
|
@@ -3,7 +3,8 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ensureAbsoluteDirectory } from "./absolute-path.js";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { assertDirectoryReceiptCurrentSync, copyRetainedDirectoryReceipt, createDirectoryReceiptSync, directoryReceiptAuthority, directoryReceiptIdentity, ownDirectoryReceipt, } from "./directory-receipt.js";
|
|
7
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
8
|
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
|
|
8
9
|
import { realpathSync } from "./realpath.js";
|
|
9
10
|
function directoryOpenFlags() {
|
|
@@ -38,81 +39,23 @@ function unsupportedOutcome(error) {
|
|
|
38
39
|
const code = error.code;
|
|
39
40
|
return code ? { status: "unsupported", code } : { status: "unsupported" };
|
|
40
41
|
}
|
|
41
|
-
function assertDirectory(identity, pathname, label) {
|
|
42
|
-
if (identity.isSymbolicLink() || !identity.isDirectory()) {
|
|
43
|
-
throw new FsSafeError("not-file", `${label} must be a real directory: ${pathname}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function ownDirectoryReceipt(receipt) {
|
|
47
|
-
const pathname = receipt.path;
|
|
48
|
-
const realPath = receipt.realPath;
|
|
49
|
-
const identity = receipt.identity;
|
|
50
|
-
return { path: pathname, realPath, identity };
|
|
51
|
-
}
|
|
52
|
-
function assertDirectoryReceiptPaths(receipt, label) {
|
|
53
|
-
assertNoWindowsPathAlias(receipt.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
54
|
-
assertNoWindowsPathAlias(receipt.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
55
|
-
}
|
|
56
42
|
async function createDirectoryReceipt(directoryPath, label) {
|
|
57
|
-
|
|
58
|
-
const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
|
|
59
|
-
assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
60
|
-
const operationPath = pathForWindowsFilesystem(resolvedPath);
|
|
61
|
-
const identity = fsSync.lstatSync(operationPath);
|
|
62
|
-
assertDirectory(identity, resolvedPath, label);
|
|
63
|
-
const realPath = realpathSync.native(operationPath);
|
|
64
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
65
|
-
return {
|
|
66
|
-
path: resolvedPath,
|
|
67
|
-
realPath,
|
|
68
|
-
identity,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function createDirectoryReceiptSync(directoryPath, label) {
|
|
72
|
-
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
73
|
-
const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
|
|
74
|
-
assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
75
|
-
const operationPath = pathForWindowsFilesystem(resolvedPath);
|
|
76
|
-
const identity = fsSync.lstatSync(operationPath);
|
|
77
|
-
assertDirectory(identity, resolvedPath, label);
|
|
78
|
-
const realPath = realpathSync(operationPath);
|
|
79
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
80
|
-
return {
|
|
81
|
-
path: resolvedPath,
|
|
82
|
-
realPath,
|
|
83
|
-
identity,
|
|
84
|
-
};
|
|
43
|
+
return createDirectoryReceiptSync(directoryPath, label, realpathSync.native);
|
|
85
44
|
}
|
|
86
45
|
async function assertDirectoryReceiptCurrent(receipt, label) {
|
|
87
|
-
|
|
88
|
-
const operationPath = pathForWindowsFilesystem(receipt.path);
|
|
89
|
-
const currentIdentity = fsSync.lstatSync(operationPath);
|
|
90
|
-
assertDirectory(currentIdentity, receipt.path, label);
|
|
91
|
-
const realPath = realpathSync.native(operationPath);
|
|
92
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
93
|
-
if (!sameFileIdentity(receipt.identity, currentIdentity) ||
|
|
94
|
-
realPath !== receipt.realPath) {
|
|
95
|
-
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
|
|
96
|
-
}
|
|
46
|
+
assertDirectoryReceiptCurrentSync(receipt, label, realpathSync.native);
|
|
97
47
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
realPath !== receipt.realPath) {
|
|
107
|
-
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
|
|
108
|
-
}
|
|
48
|
+
function assertOpenDirectoryIdentity(descriptor, receipt, label) {
|
|
49
|
+
inspectFileIdentitySync(() => {
|
|
50
|
+
const opened = fsSync.fstatSync(descriptor, { bigint: true });
|
|
51
|
+
if (!opened.isDirectory()) {
|
|
52
|
+
throw new FsSafeError("not-file", `${label} must be a real directory: ${receipt.path}`);
|
|
53
|
+
}
|
|
54
|
+
return opened;
|
|
55
|
+
}, directoryReceiptAuthority(receipt).identity);
|
|
109
56
|
}
|
|
110
57
|
async function assertOpenDirectoryCurrent(handle, receipt, label) {
|
|
111
|
-
|
|
112
|
-
assertDirectory(openedIdentity, receipt.path, label);
|
|
113
|
-
if (!sameFileIdentity(receipt.identity, openedIdentity)) {
|
|
114
|
-
throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
|
|
115
|
-
}
|
|
58
|
+
assertOpenDirectoryIdentity(handle.fd, receipt, label);
|
|
116
59
|
await assertDirectoryReceiptCurrent(receipt, label);
|
|
117
60
|
}
|
|
118
61
|
class PinnedDirectoryImpl {
|
|
@@ -124,7 +67,7 @@ class PinnedDirectoryImpl {
|
|
|
124
67
|
constructor(handle, receipt, label) {
|
|
125
68
|
this.#handle = handle;
|
|
126
69
|
this.#authority = receipt;
|
|
127
|
-
this.receipt =
|
|
70
|
+
this.receipt = copyRetainedDirectoryReceipt(receipt);
|
|
128
71
|
this.#label = label;
|
|
129
72
|
}
|
|
130
73
|
async assertCurrent() {
|
|
@@ -213,11 +156,7 @@ export function syncDirectorySync(directory, options = {}) {
|
|
|
213
156
|
return unsupportedOutcome(error);
|
|
214
157
|
}
|
|
215
158
|
try {
|
|
216
|
-
|
|
217
|
-
assertDirectory(openedIdentity, receipt.path, label);
|
|
218
|
-
if (!sameFileIdentity(receipt.identity, openedIdentity)) {
|
|
219
|
-
throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
|
|
220
|
-
}
|
|
159
|
+
assertOpenDirectoryIdentity(descriptor, receipt, label);
|
|
221
160
|
assertDirectoryReceiptCurrentSync(receipt, label);
|
|
222
161
|
try {
|
|
223
162
|
fsSync.fsyncSync(descriptor);
|
|
@@ -273,13 +212,16 @@ export async function ensureDurableDirectory(options) {
|
|
|
273
212
|
assertNoWindowsPathAlias(requestedDirectoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
274
213
|
const directoryPath = resolvePathPreservingWindowsRoot(requestedDirectoryPath);
|
|
275
214
|
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
215
|
+
const expectedInput = options.expectedExistingIdentity;
|
|
216
|
+
const expectedIdentity = expectedInput && directoryReceiptIdentity(expectedInput);
|
|
276
217
|
const ancestorReceipt = await findExistingAncestorReceipt(directoryPath, label);
|
|
277
218
|
const targetExists = ancestorReceipt.path === directoryPath;
|
|
278
|
-
|
|
279
|
-
if (expectedExistingIdentity &&
|
|
280
|
-
(!targetExists || !sameFileIdentity(expectedExistingIdentity, ancestorReceipt.identity))) {
|
|
219
|
+
if (expectedIdentity && !targetExists) {
|
|
281
220
|
throw new FsSafeError("path-mismatch", `${label} changed before durable directory pinning: ${directoryPath}`);
|
|
282
221
|
}
|
|
222
|
+
if (expectedIdentity) {
|
|
223
|
+
inspectFileIdentitySync(() => directoryReceiptAuthority(ancestorReceipt).identity, expectedIdentity);
|
|
224
|
+
}
|
|
283
225
|
const ancestor = await pinDirectory(ancestorReceipt, { label });
|
|
284
226
|
const pinnedDirectories = [ancestor];
|
|
285
227
|
try {
|
|
@@ -336,7 +278,7 @@ export async function ensureDurableDirectory(options) {
|
|
|
336
278
|
}
|
|
337
279
|
await ancestor.assertCurrent();
|
|
338
280
|
await assertDirectoryReceiptCurrent(finalReceipt, label);
|
|
339
|
-
return
|
|
281
|
+
return Object.assign(copyRetainedDirectoryReceipt(finalReceipt), { parentSync });
|
|
340
282
|
}
|
|
341
283
|
finally {
|
|
342
284
|
await Promise.all(pinnedDirectories.toReversed().map(async (directory) => directory.close()));
|
|
@@ -34,6 +34,9 @@ export declare function assertAsyncDirectoryGuard(guard: AnyAsyncDirectoryGuard)
|
|
|
34
34
|
export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
|
|
35
35
|
export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard | AnyAsyncDirectoryGuard): void;
|
|
36
36
|
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
|
|
37
|
+
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
|
|
38
|
+
bigint: true;
|
|
39
|
+
}): Promise<AsyncDirectoryGuard<BigIntStats>>;
|
|
37
40
|
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
|
|
38
41
|
bigint: boolean;
|
|
39
42
|
}): Promise<AnyAsyncDirectoryGuard>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type BigIntStats } from "node:fs";
|
|
2
2
|
import { type DirectoryModeOwner } from "./directory-mode-owner.js";
|
|
3
|
+
/** Darwin descriptor inspection avoids requesting directory-content reads. */
|
|
4
|
+
export declare function nodeDarwinDirectoryMetadataFlags(): number;
|
|
3
5
|
export declare function nodeDirectorySearchOnlyFlags(): {
|
|
4
6
|
flags: number;
|
|
5
7
|
proc: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
|
|
1
|
+
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,8EAA8E;AAC9E,wBAAgB,gCAAgC,IAAI,MAAM,CAMzD;AAED,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
|
|
@@ -4,6 +4,14 @@ import { FsSafeError } from "./errors.js";
|
|
|
4
4
|
import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
5
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
|
|
7
|
+
/** Darwin descriptor inspection avoids requesting directory-content reads. */
|
|
8
|
+
export function nodeDarwinDirectoryMetadataFlags() {
|
|
9
|
+
if (process.platform !== "darwin" || (process.arch !== "x64" && process.arch !== "arm64")) {
|
|
10
|
+
throw new FsSafeError("helper-unavailable", "Darwin directory metadata descriptors are unavailable");
|
|
11
|
+
}
|
|
12
|
+
// Darwin SDK O_EVTONLY; Node omits this flag from its exported constants.
|
|
13
|
+
return 0x8000 | constants.O_DIRECTORY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
14
|
+
}
|
|
7
15
|
export function nodeDirectorySearchOnlyFlags() {
|
|
8
16
|
if (process.arch !== "x64" && process.arch !== "arm64")
|
|
9
17
|
return undefined;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type BigIntStats, type Stats } from "node:fs";
|
|
2
|
+
import type { DirectoryReceipt } from "./directory-durability.js";
|
|
3
|
+
import type { FileIdentityStat } from "./file-identity.js";
|
|
4
|
+
type ExactIdentity = Readonly<Pick<BigIntStats, "dev" | "ino">>;
|
|
5
|
+
declare const numericFields: readonly ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
6
|
+
declare const timeFields: readonly ["atime", "mtime", "ctime", "birthtime"];
|
|
7
|
+
type DirectoryMetadata = Pick<Stats, typeof numericFields[number] | `${typeof timeFields[number]}Ms`>;
|
|
8
|
+
type DirectoryProvenance = Readonly<{
|
|
9
|
+
identity: ExactIdentity;
|
|
10
|
+
metadata: Readonly<DirectoryMetadata>;
|
|
11
|
+
}>;
|
|
12
|
+
type DirectoryAuthority = DirectoryProvenance & Readonly<{
|
|
13
|
+
path: string;
|
|
14
|
+
realPath: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function directoryReceiptIdentity(identity: FileIdentityStat): ExactIdentity;
|
|
17
|
+
export declare function directoryReceiptAuthority(receipt: DirectoryReceipt): DirectoryAuthority;
|
|
18
|
+
export declare function ownDirectoryReceipt(receipt: DirectoryReceipt): DirectoryReceipt;
|
|
19
|
+
export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt): DirectoryReceipt;
|
|
20
|
+
export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats): DirectoryReceipt;
|
|
21
|
+
export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string): DirectoryReceipt;
|
|
22
|
+
export declare function assertDirectoryReceiptCurrentSync(receipt: DirectoryReceipt, label: string, canonicalize?: (pathname: string) => string): void;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=directory-receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-receipt.d.ts","sourceRoot":"","sources":["../src/directory-receipt.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAQ3D,KAAK,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;AAChE,QAAA,MAAM,aAAa,YAAI,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAClH,QAAA,MAAM,UAAU,YAAI,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AACrE,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACtG,KAAK,mBAAmB,GAAG,QAAQ,CAAC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;CACvC,CAAC,CAAC;AACH,KAAK,kBAAkB,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAOH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,aAAa,CAuBlF;AA8BD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,kBAAkB,CAEvF;AAQD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAE/E;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAGxF;AA0BD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,WAAW,GACrB,gBAAgB,CAOlB;AAED,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAqB,GACxD,gBAAgB,CASlB;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,MAAM,EACb,YAAY,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAqB,GACxD,IAAI,CAUN"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
|
+
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { realpathSync } from "./realpath.js";
|
|
5
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
6
|
+
const numericFields = ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
7
|
+
const timeFields = ["atime", "mtime", "ctime", "birthtime"];
|
|
8
|
+
// Live pins retain private authority. New admissions snapshot the supplied
|
|
9
|
+
// fields and accept exact provenance only while its public identity is intact.
|
|
10
|
+
const authorities = new WeakMap();
|
|
11
|
+
const identities = new WeakMap();
|
|
12
|
+
export function directoryReceiptIdentity(identity) {
|
|
13
|
+
if (!identity) {
|
|
14
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity is missing");
|
|
15
|
+
}
|
|
16
|
+
const known = identities.get(identity);
|
|
17
|
+
const dev = identity.dev;
|
|
18
|
+
const ino = identity.ino;
|
|
19
|
+
if (known) {
|
|
20
|
+
const matches = (value, expected) => typeof value === "bigint" ? value === expected : value === Number(expected);
|
|
21
|
+
if (!matches(dev, known.identity.dev) || !matches(ino, known.identity.ino)) {
|
|
22
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity changed");
|
|
23
|
+
}
|
|
24
|
+
return known.identity;
|
|
25
|
+
}
|
|
26
|
+
const exact = (value) => {
|
|
27
|
+
if ((typeof value !== "bigint" && !Number.isSafeInteger(value)) ||
|
|
28
|
+
(process.platform === "win32" && (value === 0 || value === 0n))) {
|
|
29
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity could not be verified");
|
|
30
|
+
}
|
|
31
|
+
return BigInt(value);
|
|
32
|
+
};
|
|
33
|
+
return Object.freeze({ dev: exact(dev), ino: exact(ino) });
|
|
34
|
+
}
|
|
35
|
+
function snapshotDirectoryReceipt(receipt) {
|
|
36
|
+
const pathname = receipt?.path;
|
|
37
|
+
const realPath = receipt?.realPath;
|
|
38
|
+
const stat = receipt?.identity;
|
|
39
|
+
if (typeof pathname !== "string" || typeof realPath !== "string" || stat == null) {
|
|
40
|
+
throw new FsSafeError("path-mismatch", "directory receipt is incomplete");
|
|
41
|
+
}
|
|
42
|
+
assertNoWindowsPathAlias(pathname, "filesystem");
|
|
43
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
44
|
+
const identity = directoryReceiptIdentity(stat);
|
|
45
|
+
const known = authorities.get(receipt);
|
|
46
|
+
if (known && (pathname !== known.path || realPath !== known.realPath ||
|
|
47
|
+
identity.dev !== known.identity.dev || identity.ino !== known.identity.ino)) {
|
|
48
|
+
throw new FsSafeError("path-mismatch", "directory receipt changed before admission");
|
|
49
|
+
}
|
|
50
|
+
const metadata = known?.metadata ?? identities.get(stat)?.metadata ?? snapshotDirectoryMetadata(stat, identity);
|
|
51
|
+
return Object.freeze({ path: pathname, realPath, identity, metadata });
|
|
52
|
+
}
|
|
53
|
+
function snapshotDirectoryMetadata(stat, identity) {
|
|
54
|
+
const metadata = {};
|
|
55
|
+
for (const field of numericFields) {
|
|
56
|
+
metadata[field] = field === "dev" || field === "ino" ? Number(identity[field]) : stat[field];
|
|
57
|
+
}
|
|
58
|
+
for (const field of timeFields)
|
|
59
|
+
metadata[`${field}Ms`] = stat[`${field}Ms`];
|
|
60
|
+
return Object.freeze(metadata);
|
|
61
|
+
}
|
|
62
|
+
export function directoryReceiptAuthority(receipt) {
|
|
63
|
+
return authorities.get(receipt) ?? snapshotDirectoryReceipt(receipt);
|
|
64
|
+
}
|
|
65
|
+
function rememberReceipt(receipt, authority) {
|
|
66
|
+
authorities.set(receipt, authority);
|
|
67
|
+
identities.set(receipt.identity, authority);
|
|
68
|
+
return receipt;
|
|
69
|
+
}
|
|
70
|
+
export function ownDirectoryReceipt(receipt) {
|
|
71
|
+
return receiptFromAuthority(snapshotDirectoryReceipt(receipt));
|
|
72
|
+
}
|
|
73
|
+
export function copyRetainedDirectoryReceipt(receipt) {
|
|
74
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
75
|
+
return receiptFromAuthority(authority);
|
|
76
|
+
}
|
|
77
|
+
function receiptFromAuthority(authority) {
|
|
78
|
+
return rememberReceipt({
|
|
79
|
+
path: authority.path,
|
|
80
|
+
realPath: authority.realPath,
|
|
81
|
+
identity: Object.assign(Object.create(fs.Stats.prototype), authority.metadata),
|
|
82
|
+
}, authority);
|
|
83
|
+
}
|
|
84
|
+
function numericDirectoryMetadata(exact) {
|
|
85
|
+
// Keep metadata and authority from one observation. A second pathname stat
|
|
86
|
+
// can describe a replacement even when a later fence sees the original again.
|
|
87
|
+
const metadata = {};
|
|
88
|
+
for (const field of numericFields) {
|
|
89
|
+
metadata[field] = Number(exact[field]);
|
|
90
|
+
}
|
|
91
|
+
for (const field of timeFields) {
|
|
92
|
+
const nanoseconds = exact[`${field}Ns`];
|
|
93
|
+
const remainder = ((nanoseconds % 1000000000n) + 1000000000n) % 1000000000n;
|
|
94
|
+
metadata[`${field}Ms`] = Number((nanoseconds - remainder) / 1000000000n) * 1_000 + Number(remainder) / 1_000_000;
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze(metadata);
|
|
97
|
+
}
|
|
98
|
+
// The caller already admitted the exact observation and its paths.
|
|
99
|
+
export function createDirectoryReceiptFromIdentity(pathname, realPath, exactStat) {
|
|
100
|
+
return receiptFromAuthority(Object.freeze({
|
|
101
|
+
path: pathname,
|
|
102
|
+
realPath,
|
|
103
|
+
identity: Object.freeze({ dev: exactStat.dev, ino: exactStat.ino }),
|
|
104
|
+
metadata: numericDirectoryMetadata(exactStat),
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
export function createDirectoryReceiptSync(directoryPath, label, canonicalize = realpathSync) {
|
|
108
|
+
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
109
|
+
const pathname = resolvePathPreservingWindowsRoot(directoryPath);
|
|
110
|
+
assertNoWindowsPathAlias(pathname, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
111
|
+
const exact = inspectDirectoryIdentitySync(pathname);
|
|
112
|
+
const operationPath = pathForWindowsFilesystem(pathname);
|
|
113
|
+
const realPath = canonicalize(operationPath);
|
|
114
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
115
|
+
return createDirectoryReceiptFromIdentity(pathname, realPath, exact);
|
|
116
|
+
}
|
|
117
|
+
export function assertDirectoryReceiptCurrentSync(receipt, label, canonicalize = realpathSync) {
|
|
118
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
119
|
+
assertNoWindowsPathAlias(authority.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
120
|
+
assertNoWindowsPathAlias(authority.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
121
|
+
inspectDirectoryIdentitySync(authority.path, authority.identity);
|
|
122
|
+
const realPath = canonicalize(pathForWindowsFilesystem(authority.path));
|
|
123
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
124
|
+
if (realPath !== authority.realPath) {
|
|
125
|
+
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${authority.path}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import fs, { type FileHandle } from "node:fs/promises";
|
|
3
|
+
import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
|
|
4
|
+
export declare function hasErrorCode(error: unknown, expected: string): boolean;
|
|
5
|
+
type OwnedPathCleanupStatus = "removed" | "name-absent" | "preserved";
|
|
6
|
+
export declare function removeOwnedPath(params: {
|
|
7
|
+
fsModule: Pick<typeof fs, "lstat" | "unlink">;
|
|
8
|
+
pathname: string;
|
|
9
|
+
identity?: BigIntStats;
|
|
10
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
11
|
+
export declare function cleanupPinnedFilePath(params: {
|
|
12
|
+
pathname: string;
|
|
13
|
+
handle: Pick<FileHandle, "fd">;
|
|
14
|
+
identity?: BigIntStats;
|
|
15
|
+
parentGuard: AnyAsyncDirectoryGuard;
|
|
16
|
+
throwOnCleanupError?: boolean;
|
|
17
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=file-cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-cleanup.d.ts","sourceRoot":"","sources":["../src/file-cleanup.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAI9F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStE;AAED,KAAK,sBAAsB,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAEtE,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAoBlC;AAGD,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAgClC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import syncFs, {} from "node:fs";
|
|
2
|
+
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { assertAsyncDirectoryGuard } from "./directory-guard.js";
|
|
4
|
+
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
6
|
+
export function hasErrorCode(error, expected) {
|
|
7
|
+
if ((typeof error !== "object" || error === null) && typeof error !== "function") {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return Reflect.get(error, "code") === expected;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function removeOwnedPath(params) {
|
|
18
|
+
if (!params.identity)
|
|
19
|
+
return "preserved";
|
|
20
|
+
try {
|
|
21
|
+
const current = params.fsModule === fs
|
|
22
|
+
? syncFs.lstatSync(params.pathname, { bigint: true })
|
|
23
|
+
: await params.fsModule.lstat(params.pathname, { bigint: true });
|
|
24
|
+
if (current.isSymbolicLink() ||
|
|
25
|
+
!current.isFile() ||
|
|
26
|
+
current.nlink !== 1n ||
|
|
27
|
+
!sameFileIdentityForCleanup(current, params.identity)) {
|
|
28
|
+
return "preserved";
|
|
29
|
+
}
|
|
30
|
+
await params.fsModule.unlink(params.pathname);
|
|
31
|
+
return "removed";
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
35
|
+
return "name-absent";
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Borrowed handle: the caller retains it until guarded cleanup finishes.
|
|
40
|
+
export async function cleanupPinnedFilePath(params) {
|
|
41
|
+
if (!params.identity)
|
|
42
|
+
return "preserved";
|
|
43
|
+
try {
|
|
44
|
+
const guard = params.parentGuard;
|
|
45
|
+
if ([guard.stat.dev, guard.stat.ino].some((value) => typeof value === "number" && !Number.isSafeInteger(value)))
|
|
46
|
+
return "preserved";
|
|
47
|
+
await assertAsyncDirectoryGuard(guard);
|
|
48
|
+
const parent = syncFs.lstatSync(guard.dir, { bigint: true });
|
|
49
|
+
if (parent.isSymbolicLink() || !parent.isDirectory() ||
|
|
50
|
+
!sameFileIdentityForCleanup(parent, guard.stat))
|
|
51
|
+
return "preserved";
|
|
52
|
+
const opened = syncFs.fstatSync(params.handle.fd, { bigint: true });
|
|
53
|
+
if (!opened.isFile() || opened.nlink !== 1n ||
|
|
54
|
+
!sameFileIdentityForCleanup(opened, params.identity))
|
|
55
|
+
return "preserved";
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Unverifiable authority must preserve the path and the original write failure.
|
|
59
|
+
if (params.throwOnCleanupError &&
|
|
60
|
+
!(error instanceof FsSafeError && error.category === "policy") &&
|
|
61
|
+
!hasErrorCode(error, "ENOENT") && !hasErrorCode(error, "ENOTDIR") &&
|
|
62
|
+
!hasErrorCode(error, "ELOOP"))
|
|
63
|
+
throw error;
|
|
64
|
+
return "preserved";
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return await removeOwnedPath({
|
|
68
|
+
fsModule: fs,
|
|
69
|
+
pathname: params.pathname,
|
|
70
|
+
identity: params.identity,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (params.throwOnCleanupError)
|
|
75
|
+
throw error;
|
|
76
|
+
return "preserved";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type FailureKind = "identity" | "resolution" | `open:${string}` | `exclusive-create:${string}` | `unlinked:${string}` | `changed:${string}` | `stat-leaf-missing:${string}`;
|
|
1
|
+
type FailureKind = "identity" | "resolution" | `open:${string}` | `exclusive-create:${string}` | `unlinked:${string}` | `changed:${string}` | `stat-leaf-missing:${string}` | `stat-leaf-changed:${string}`;
|
|
2
2
|
export declare function recordFileObservationFailure(error: unknown, kind: FailureKind): void;
|
|
3
3
|
export declare function isFileObservationFailure(error: unknown, kind: FailureKind): boolean;
|
|
4
4
|
export declare function fileObservation(): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-observation.d.ts","sourceRoot":"","sources":["../src/file-observation.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,MAAM,EAAE,GAAG,oBAAoB,MAAM,EAAE,GAAG,YAAY,MAAM,EAAE,GAAG,WAAW,MAAM,EAAE,GAAG,qBAAqB,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"file-observation.d.ts","sourceRoot":"","sources":["../src/file-observation.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,MAAM,EAAE,GAAG,oBAAoB,MAAM,EAAE,GAAG,YAAY,MAAM,EAAE,GAAG,WAAW,MAAM,EAAE,GAAG,qBAAqB,MAAM,EAAE,GAAG,qBAAqB,MAAM,EAAE,CAAC;AAG5M,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAMpF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAEnF;AAGD,wBAAgB,eAAe;IAG3B,GAAG,CAAC,CAAC,aAAa,MAAM,CAAC,GAAG,CAAC;IAC7B,GAAG,QAAQ,OAAO,QAAQ,WAAW,GAAG,OAAO;EAElD"}
|