@openclaw/feishu 2026.9.7 → 2026.10.1-beta.1
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/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
- package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
- package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
- package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
- package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
- package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
- package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
- package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
- package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
- package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
- package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
- package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
- package/dist/api.js +234 -297
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +2 -2
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/subagent-hooks-api.js +1 -1
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
- package/node_modules/@openclaw/fs-safe/README.md +70 -379
- package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
- package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
- package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
- package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
- package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
- package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
- package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
- package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
- package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
- package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
- package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
- package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
- package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
- package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
- package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
- package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
- package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
- package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
- package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
- package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
- package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
- package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
- package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
- package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
- package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
- package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
- package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
- package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
- package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
- package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
- package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
- package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
- package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
- package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
- package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
- package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
- package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
- package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
- package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
- package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
- package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
- package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
- package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
- package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
- package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
- package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
- package/node_modules/@openclaw/fs-safe/package.json +10 -10
- package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/package.json +5 -5
- package/skills/feishu-wiki/SKILL.md +1 -1
- package/dist/.setup/accounts-wRqItHug.mjs +0 -206
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
- package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
- package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
- package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
import fsSync, {} from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import { assertDirectoryIdentitySync, inspectDirectoryIdentitySync, } from "./directory-guard.js";
|
|
2
|
+
import { assertDirectoryIdentitySync, } from "./directory-guard.js";
|
|
4
3
|
import { FsSafeError } from "./errors.js";
|
|
5
4
|
import { openNativeParentAdmission } from "./native-parent-admission.js";
|
|
6
|
-
import { captureNativeFdClose } from "./native-binding.js";
|
|
7
5
|
import { inspectNativeDirectoryObservation } from "./native-directory-observation.js";
|
|
8
|
-
import { createPathSegmentRoute, joinPathSegmentRoute } from "./path-segment-route.js";
|
|
9
6
|
import { isSymlinkOpenError } from "./path.js";
|
|
10
7
|
import { checkedMutationDirectory } from "./pinned-mutation-observation.js";
|
|
11
|
-
import { canReuseParentWithMutationAssertion } from "./root-write-lock-binding.js";
|
|
12
8
|
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
13
9
|
import { createSuppressedError } from "./suppressed-error.js";
|
|
14
|
-
function
|
|
15
|
-
|
|
10
|
+
export function assertWindowsPolicyParentCurrent(parent) {
|
|
11
|
+
inspectFileIdentitySync(() => fsSync.fstatSync(parent.fd, { bigint: true }), parent.guard.stat);
|
|
16
12
|
assertDirectoryIdentitySync(parent.guard.dir, {
|
|
17
13
|
dev: parent.guard.stat.dev,
|
|
18
14
|
ino: parent.guard.stat.ino,
|
|
19
15
|
realPath: parent.guard.realPath,
|
|
20
16
|
});
|
|
21
|
-
return current;
|
|
22
17
|
}
|
|
23
|
-
function
|
|
24
|
-
if (fd === undefined)
|
|
25
|
-
return;
|
|
18
|
+
export function closeWindowsPolicyParentAfterFailure(closeFd, fd, failure, report = false) {
|
|
26
19
|
try {
|
|
27
20
|
closeFd(fd);
|
|
28
21
|
}
|
|
@@ -33,177 +26,37 @@ function closeAfterFailure(closeFd, fd, failure, report = false) {
|
|
|
33
26
|
: error;
|
|
34
27
|
}
|
|
35
28
|
}
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return checkedMutationDirectory(guard.dir, guard.realPath, guard.stat, typeof binding.observeDirectory === "function" ? () => {
|
|
41
|
-
const stat = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), guard.stat);
|
|
42
|
-
let observed;
|
|
43
|
-
try {
|
|
44
|
-
observed = inspectNativeDirectoryObservation(binding, guard.dir, stat);
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
if (error?.code === "OBSERVATION_UNAVAILABLE")
|
|
48
|
-
return undefined;
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
return { canonicalPath: observed.realPath,
|
|
52
|
-
identity: { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink } };
|
|
53
|
-
} : undefined);
|
|
54
|
-
}
|
|
55
|
-
async function openParent(fd, parentPath, relativePath) {
|
|
56
|
-
try {
|
|
57
|
-
const admitted = await openNativeParentAdmission(binding, {
|
|
58
|
-
...rootAdmission,
|
|
59
|
-
root: { fd },
|
|
60
|
-
rootPath: parentPath,
|
|
61
|
-
// Policy fences retain every identity bit even with a legacy numeric root.
|
|
62
|
-
exactRoot: true,
|
|
63
|
-
}, relativePath, "native-directory");
|
|
64
|
-
const guard = { ...admitted.guard, stat: admitted.guard.stat };
|
|
65
|
-
return { fd: admitted.fd, guard, observation: parentObservation(admitted.fd, guard) };
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
if (!isSymlinkOpenError(error))
|
|
69
|
-
throw error;
|
|
70
|
-
throw new FsSafeError(params.mutationAdmission.rejectParentSymlinks ? "symlink" : "path-mismatch", "native write parent changed during policy admission", { cause: error instanceof Error ? error : undefined });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async function authorize(targetPath, mutationPath = targetPath, phase = "parent") {
|
|
74
|
-
const request = Object.freeze({ targetPath, mutationPath, phase });
|
|
75
|
-
return await (session ? session.authorize(request) : params.mutationAdmission.authorize(request));
|
|
76
|
-
}
|
|
77
|
-
// Existing complete parents need one open and one target admission. Exact
|
|
78
|
-
// parent denies apply only to creation, never to merely using a directory.
|
|
79
|
-
let complete;
|
|
80
|
-
try {
|
|
81
|
-
complete = await openParent(rootAdmission.root.fd, params.rootPath, params.relativeParentPath);
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (error?.code !== "ENOENT" || !params.mkdir)
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
if (complete) {
|
|
29
|
+
export function windowsParentObservation(binding, fd, guard) {
|
|
30
|
+
return checkedMutationDirectory(guard.dir, guard.realPath, guard.stat, typeof binding.observeDirectory === "function" ? () => {
|
|
31
|
+
const stat = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), guard.stat);
|
|
32
|
+
let observed;
|
|
88
33
|
try {
|
|
89
|
-
|
|
90
|
-
assertParentCurrent(complete);
|
|
91
|
-
return complete;
|
|
34
|
+
observed = inspectNativeDirectoryObservation(binding, guard.dir, stat);
|
|
92
35
|
}
|
|
93
36
|
catch (error) {
|
|
94
|
-
|
|
37
|
+
if (error?.code === "OBSERVATION_UNAVAILABLE")
|
|
38
|
+
return undefined;
|
|
95
39
|
throw error;
|
|
96
40
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fd: rootAdmission.root.fd,
|
|
103
|
-
guard: { dir: params.rootPath, realPath: params.rootPath, stat: rootStat },
|
|
104
|
-
observation: parentObservation(rootAdmission.root.fd, {
|
|
105
|
-
dir: params.rootPath, realPath: params.rootPath, stat: rootStat,
|
|
106
|
-
}),
|
|
107
|
-
};
|
|
108
|
-
let ownedFd;
|
|
109
|
-
let failure;
|
|
110
|
-
const initialTarget = joinPathSegmentRoute(params.rootPath, route, 0, params.basename);
|
|
111
|
-
if (canReuseParentWithMutationAssertion(params.assertBeforeMutation, params.rootPath, initialTarget)) {
|
|
112
|
-
session = params.mutationAdmission?.beginNativeParentWalk?.() ??
|
|
113
|
-
params.mutationAdmission?.beginSharedParentWalk?.();
|
|
114
|
-
if (session && session.retainedTargetPath !== initialTarget) {
|
|
115
|
-
session.dispose();
|
|
116
|
-
session = undefined;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
41
|
+
return { canonicalPath: observed.realPath,
|
|
42
|
+
identity: { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink } };
|
|
43
|
+
} : undefined);
|
|
44
|
+
}
|
|
45
|
+
export async function openWindowsPolicyParent(binding, params, rootAdmission, fd, parentPath, relativePath) {
|
|
119
46
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
catch (error) {
|
|
130
|
-
if (error?.code !== "ENOENT")
|
|
131
|
-
throw error;
|
|
132
|
-
// The request admits both the prospective leaf and this missing direct
|
|
133
|
-
// child. A deeper denial can leave already admitted ancestors intact.
|
|
134
|
-
const targetPath = session?.retainedTargetPath ??
|
|
135
|
-
joinPathSegmentRoute(parentPath, route, index, params.basename);
|
|
136
|
-
const mutationPath = path.join(parentPath, segment);
|
|
137
|
-
const request = Object.freeze({ targetPath, mutationPath, phase: "parent-create" });
|
|
138
|
-
createReceipt = session?.tryAuthorizeAtParent(request, current.observation);
|
|
139
|
-
if (!createReceipt) {
|
|
140
|
-
createReceipt = await authorize(targetPath, mutationPath, "parent-create");
|
|
141
|
-
assertParentCurrent(current);
|
|
142
|
-
}
|
|
143
|
-
if (params.assertBeforeMutation) {
|
|
144
|
-
// Opaque callbacks retain their post-callback descriptor/path fence.
|
|
145
|
-
params.assertBeforeMutation();
|
|
146
|
-
assertParentCurrent(current);
|
|
147
|
-
}
|
|
148
|
-
// Synchronous session admission already ends at the exact parent fence.
|
|
149
|
-
const mkdirChild = binding.mkdirChildBeneath;
|
|
150
|
-
if (typeof mkdirChild !== "function") {
|
|
151
|
-
throw new FsSafeError("helper-unavailable", "native direct-child parent creation is unavailable");
|
|
152
|
-
}
|
|
153
|
-
createdByMkdir = mkdirChild.call(binding, current.fd, segment, 0o777) === true;
|
|
154
|
-
child = await openParent(current.fd, parentPath, segment);
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
const targetPath = joinPathSegmentRoute(child.guard.realPath, route, index + 1, params.basename);
|
|
158
|
-
if (session && targetPath !== session.retainedTargetPath) {
|
|
159
|
-
session.dispose();
|
|
160
|
-
session = undefined;
|
|
161
|
-
}
|
|
162
|
-
let evidence;
|
|
163
|
-
if (session && createdByMkdir && createReceipt) {
|
|
164
|
-
try {
|
|
165
|
-
evidence = Object.freeze({
|
|
166
|
-
admission: createReceipt,
|
|
167
|
-
parent: parentObservation(current.fd, { ...current.guard,
|
|
168
|
-
stat: inspectFileIdentitySync(() => fsSync.fstatSync(current.fd, { bigint: true }), current.guard.stat),
|
|
169
|
-
}),
|
|
170
|
-
child: child.observation,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
// Incomplete optional evidence returns to ordered admission below.
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Advancement refreshes both named directories after all Root/policy
|
|
178
|
-
// observations; these receipts supply expectations, not cached authority.
|
|
179
|
-
const advanced = evidence && session.advanceCreatedDirectory(evidence);
|
|
180
|
-
if (!advanced) {
|
|
181
|
-
await authorize(targetPath);
|
|
182
|
-
assertParentCurrent(child);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
closeAfterFailure(closeFd, child.fd, { error }, rootAdmission.reportCloseErrors);
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
const previousFd = ownedFd;
|
|
190
|
-
current = child;
|
|
191
|
-
ownedFd = child.fd;
|
|
192
|
-
if (previousFd !== undefined)
|
|
193
|
-
closeFd(previousFd);
|
|
194
|
-
}
|
|
195
|
-
if (ownedFd === undefined) {
|
|
196
|
-
throw new FsSafeError("path-mismatch", "native write parent admission did not complete");
|
|
197
|
-
}
|
|
198
|
-
ownedFd = undefined;
|
|
199
|
-
return current;
|
|
47
|
+
const admitted = await openNativeParentAdmission(binding, {
|
|
48
|
+
...rootAdmission,
|
|
49
|
+
root: { fd },
|
|
50
|
+
rootPath: parentPath,
|
|
51
|
+
// Policy fences retain every identity bit even with a legacy numeric root.
|
|
52
|
+
exactRoot: true,
|
|
53
|
+
}, relativePath, "native-directory");
|
|
54
|
+
const guard = { ...admitted.guard, stat: admitted.guard.stat };
|
|
55
|
+
return { fd: admitted.fd, guard, observation: windowsParentObservation(binding, admitted.fd, guard) };
|
|
200
56
|
}
|
|
201
57
|
catch (error) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
finally {
|
|
206
|
-
session?.dispose();
|
|
207
|
-
closeAfterFailure(closeFd, ownedFd, failure, rootAdmission.reportCloseErrors);
|
|
58
|
+
if (!isSymlinkOpenError(error))
|
|
59
|
+
throw error;
|
|
60
|
+
throw new FsSafeError(params.mutationAdmission.rejectParentSymlinks ? "symlink" : "path-mismatch", "native write parent changed during policy admission", { cause: error instanceof Error ? error : undefined });
|
|
208
61
|
}
|
|
209
62
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import { type AsyncDirectoryGuard } from "./directory-guard.js";
|
|
3
|
+
import type { NativeBinding } from "./native.js";
|
|
4
|
+
import type { NativeRootAdmission } from "./native-parent-admission.js";
|
|
5
|
+
import type { PinnedWriteParams } from "./pinned-write-types.js";
|
|
6
|
+
import { describeStagedDirectory, type PolicyStagedDirectory } from "./staged-directory.js";
|
|
7
|
+
import { type MutationDirectoryObservation } from "./pinned-mutation-observation.js";
|
|
8
|
+
export type NativePolicyParent = {
|
|
9
|
+
fd: number;
|
|
10
|
+
guard: AsyncDirectoryGuard<BigIntStats>;
|
|
11
|
+
observation: MutationDirectoryObservation;
|
|
12
|
+
stagedDirectory?: ReturnType<typeof describeStagedDirectory>;
|
|
13
|
+
policyDirectory?: PolicyStagedDirectory;
|
|
14
|
+
};
|
|
15
|
+
export declare function capturePolicyAwareNativeParent(binding: NativeBinding, params: PinnedWriteParams, rootAdmission: NativeRootAdmission, windows: boolean, directoryFlags: number, fuseChildCreation?: boolean): Promise<NativePolicyParent>;
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import fsSync, {} from "node:fs";
|
|
54
|
+
import path from "node:path";
|
|
55
|
+
import { inspectDirectoryIdentity, inspectDirectoryIdentitySync, } from "./directory-guard.js";
|
|
56
|
+
import { FsSafeError } from "./errors.js";
|
|
57
|
+
import { captureNativeFdClose } from "./native-binding.js";
|
|
58
|
+
import { NativePolicyDirectoryMismatch } from "./native-policy-directory-observation.js";
|
|
59
|
+
import { assertWindowsPolicyParentCurrent, closeWindowsPolicyParentAfterFailure, openWindowsPolicyParent, windowsParentObservation, } from "./native-policy-parent-windows.js";
|
|
60
|
+
import { assertPolicyStagedDirectoryCurrent, assertStagedDirectoryCurrent, describePolicyStagedDirectory, describeStagedDirectory, refreshPolicyStagedDirectoryObservation, } from "./staged-directory.js";
|
|
61
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
62
|
+
import { realpathSync } from "./realpath.js";
|
|
63
|
+
import { errorCauseOptions } from "./root-errors.js";
|
|
64
|
+
import { isNotFoundPathError, isSymlinkOpenError } from "./path.js";
|
|
65
|
+
import { checkedMutationDirectory, } from "./pinned-mutation-observation.js";
|
|
66
|
+
import { createPathSegmentRoute, joinPathSegmentRoute, sameAbsolutePath } from "./path-segment-route.js";
|
|
67
|
+
import { canReuseParentWithMutationAssertion } from "./root-write-lock-binding.js";
|
|
68
|
+
function mkdirPosixPolicyChild(binding, parentFd, basename) {
|
|
69
|
+
if (!basename || basename === "." || basename === ".." || basename.includes("/") ||
|
|
70
|
+
basename.includes("\0") || (process.platform === "win32" && basename.includes("\\"))) {
|
|
71
|
+
throw new FsSafeError("invalid-path", "native parent creation requires one direct-child basename");
|
|
72
|
+
}
|
|
73
|
+
const mkdirChild = binding.mkdirChildBeneath;
|
|
74
|
+
if (typeof mkdirChild === "function") {
|
|
75
|
+
const created = mkdirChild.call(binding, parentFd, basename, 0o777);
|
|
76
|
+
if (typeof created === "boolean")
|
|
77
|
+
return created;
|
|
78
|
+
}
|
|
79
|
+
binding.mkdirBeneath(parentFd, basename, 0o777);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
function normalizePosixParentOpenError(error, params) {
|
|
83
|
+
if (!isSymlinkOpenError(error))
|
|
84
|
+
return error;
|
|
85
|
+
return params.mutationAdmission?.rejectParentSymlinks
|
|
86
|
+
? new FsSafeError("symlink", "symlink path component not allowed", errorCauseOptions(error))
|
|
87
|
+
: new FsSafeError("path-mismatch", "native write parent changed during policy admission", errorCauseOptions(error));
|
|
88
|
+
}
|
|
89
|
+
async function describePosixParent(fd, pathname) {
|
|
90
|
+
const parentPath = realpathSync.native(pathname);
|
|
91
|
+
const stagedDirectory = describeStagedDirectory(fd, parentPath);
|
|
92
|
+
const stat = await inspectDirectoryIdentity(parentPath, stagedDirectory.identity);
|
|
93
|
+
return {
|
|
94
|
+
fd,
|
|
95
|
+
guard: { dir: parentPath, realPath: parentPath, stat },
|
|
96
|
+
stagedDirectory,
|
|
97
|
+
observation: checkedMutationDirectory(parentPath, stagedDirectory.realPath, stat),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function tryDescribePolicyPosixParent(binding, fd, pathname) {
|
|
101
|
+
try {
|
|
102
|
+
const policyDirectory = describePolicyStagedDirectory(fd, pathname, binding);
|
|
103
|
+
return {
|
|
104
|
+
fd,
|
|
105
|
+
guard: {
|
|
106
|
+
dir: policyDirectory.directory.realPath,
|
|
107
|
+
realPath: policyDirectory.directory.realPath,
|
|
108
|
+
stat: policyDirectory.stat,
|
|
109
|
+
},
|
|
110
|
+
stagedDirectory: policyDirectory.directory,
|
|
111
|
+
observation: policyDirectory.observation,
|
|
112
|
+
policyDirectory,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (!(error instanceof FsSafeError) || error instanceof NativePolicyDirectoryMismatch ||
|
|
117
|
+
(error.code !== "path-mismatch" && error.code !== "not-file"))
|
|
118
|
+
throw error;
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export async function capturePolicyAwareNativeParent(binding, params, rootAdmission, windows, directoryFlags, fuseChildCreation = false) {
|
|
123
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
124
|
+
try {
|
|
125
|
+
const rootFd = rootAdmission.root.fd;
|
|
126
|
+
const closeFd = captureNativeFdClose(binding);
|
|
127
|
+
const observationDisposers = windows ? undefined : new Map();
|
|
128
|
+
const disposeObservation = (fd) => {
|
|
129
|
+
const dispose = observationDisposers?.get(fd);
|
|
130
|
+
observationDisposers?.delete(fd);
|
|
131
|
+
dispose?.();
|
|
132
|
+
};
|
|
133
|
+
const observationScope = __addDisposableResource(env_1, {
|
|
134
|
+
[Symbol.dispose]() { for (const dispose of observationDisposers?.values() ?? [])
|
|
135
|
+
dispose(); },
|
|
136
|
+
}, false);
|
|
137
|
+
const capturePolicyParent = (fd, pathname) => {
|
|
138
|
+
const captured = tryDescribePolicyPosixParent(binding, fd, pathname);
|
|
139
|
+
const dispose = captured?.policyDirectory?.disposeObservation;
|
|
140
|
+
if (dispose)
|
|
141
|
+
observationDisposers.set(fd, dispose);
|
|
142
|
+
return captured;
|
|
143
|
+
};
|
|
144
|
+
const assertCurrent = (parent) => windows
|
|
145
|
+
? assertWindowsPolicyParentCurrent(parent)
|
|
146
|
+
: parent.policyDirectory
|
|
147
|
+
? assertPolicyStagedDirectoryCurrent(parent.policyDirectory)
|
|
148
|
+
: assertStagedDirectoryCurrent(parent.stagedDirectory);
|
|
149
|
+
const closeAfterFailure = (fd, failure) => {
|
|
150
|
+
disposeObservation(fd);
|
|
151
|
+
if (windows)
|
|
152
|
+
closeWindowsPolicyParentAfterFailure(closeFd, fd, failure, rootAdmission.reportCloseErrors);
|
|
153
|
+
else
|
|
154
|
+
closeFd(fd);
|
|
155
|
+
};
|
|
156
|
+
let session;
|
|
157
|
+
async function authorize(request) {
|
|
158
|
+
const frozen = Object.freeze(request);
|
|
159
|
+
return await (session ? session.authorize(frozen) : windows
|
|
160
|
+
? params.mutationAdmission.authorize(frozen)
|
|
161
|
+
: params.mutationAdmission?.authorize(frozen));
|
|
162
|
+
}
|
|
163
|
+
// POSIX begins its retained observation before the complete-parent open;
|
|
164
|
+
// Windows starts a walk session only after that fast path misses.
|
|
165
|
+
let segments = windows ? undefined : params.relativeParentPath.split("/").filter(Boolean);
|
|
166
|
+
let route = segments && createPathSegmentRoute(segments);
|
|
167
|
+
let initialTarget = route && joinPathSegmentRoute(params.rootPath, route, 0, params.basename);
|
|
168
|
+
const parentSpelling = segments?.length ? path.join(params.rootPath, ...segments) : params.rootPath;
|
|
169
|
+
let retainedTargetPath = windows ? undefined : params.mutationAdmission?.beginParentWalk?.();
|
|
170
|
+
if (retainedTargetPath && !sameAbsolutePath(retainedTargetPath, initialTarget)) {
|
|
171
|
+
retainedTargetPath = undefined;
|
|
172
|
+
}
|
|
173
|
+
let complete;
|
|
174
|
+
let completeFd;
|
|
175
|
+
try {
|
|
176
|
+
if (windows) {
|
|
177
|
+
complete = await openWindowsPolicyParent(binding, params, rootAdmission, rootFd, params.rootPath, params.relativeParentPath);
|
|
178
|
+
completeFd = complete.fd;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
completeFd = binding.openBeneath(rootFd, params.relativeParentPath, directoryFlags).fd;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (error?.code !== "ENOENT" || !params.mkdir) {
|
|
186
|
+
if (windows)
|
|
187
|
+
throw error;
|
|
188
|
+
await authorize({ targetPath: initialTarget, mutationPath: initialTarget, phase: "parent" });
|
|
189
|
+
throw normalizePosixParentOpenError(error, params);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (completeFd !== undefined) {
|
|
193
|
+
try {
|
|
194
|
+
complete ??= retainedTargetPath ? capturePolicyParent(completeFd, parentSpelling) : undefined;
|
|
195
|
+
if (!complete) {
|
|
196
|
+
retainedTargetPath = undefined;
|
|
197
|
+
complete = await describePosixParent(completeFd, parentSpelling);
|
|
198
|
+
}
|
|
199
|
+
const targetPath = path.join(complete.guard.realPath, params.basename);
|
|
200
|
+
await authorize({ targetPath, mutationPath: targetPath, phase: "parent" });
|
|
201
|
+
assertCurrent(complete);
|
|
202
|
+
return complete;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
closeAfterFailure(completeFd, { error });
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
segments ??= params.relativeParentPath.split("/").filter(Boolean);
|
|
210
|
+
route ??= createPathSegmentRoute(segments);
|
|
211
|
+
let current;
|
|
212
|
+
if (windows) {
|
|
213
|
+
const rootStat = inspectDirectoryIdentitySync(params.rootPath, inspectFileIdentitySync(() => fsSync.fstatSync(rootFd, { bigint: true })));
|
|
214
|
+
const guard = { dir: params.rootPath, realPath: params.rootPath, stat: rootStat };
|
|
215
|
+
current = { fd: rootFd, guard, observation: windowsParentObservation(binding, rootFd, guard) };
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const captured = retainedTargetPath ? capturePolicyParent(rootFd, params.rootPath) : undefined;
|
|
219
|
+
if (!captured)
|
|
220
|
+
retainedTargetPath = undefined;
|
|
221
|
+
current = captured ?? await describePosixParent(rootFd, params.rootPath);
|
|
222
|
+
}
|
|
223
|
+
let ownedFd;
|
|
224
|
+
let currentPath = params.rootPath;
|
|
225
|
+
let failure;
|
|
226
|
+
initialTarget ??= joinPathSegmentRoute(params.rootPath, route, 0, params.basename);
|
|
227
|
+
if (windows && canReuseParentWithMutationAssertion(params.assertBeforeMutation, params.rootPath, initialTarget)) {
|
|
228
|
+
session = params.mutationAdmission?.beginNativeParentWalk?.() ?? params.mutationAdmission?.beginSharedParentWalk?.();
|
|
229
|
+
if (session && session.retainedTargetPath !== initialTarget) {
|
|
230
|
+
session.dispose();
|
|
231
|
+
session = undefined;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const secureDirectoryFlags = directoryFlags | (fsSync.constants.O_NOFOLLOW ?? 0);
|
|
235
|
+
try {
|
|
236
|
+
if (!windows) {
|
|
237
|
+
const targetPath = retainedTargetPath ?? initialTarget;
|
|
238
|
+
await authorize({ targetPath, mutationPath: targetPath, phase: "parent" });
|
|
239
|
+
assertCurrent(current);
|
|
240
|
+
}
|
|
241
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
242
|
+
const segment = segments[index];
|
|
243
|
+
let childPath = windows ? undefined : path.join(currentPath, segment);
|
|
244
|
+
let child;
|
|
245
|
+
let childFd;
|
|
246
|
+
let createdByMkdir = false;
|
|
247
|
+
let createReceipt;
|
|
248
|
+
try {
|
|
249
|
+
if (windows) {
|
|
250
|
+
child = await openWindowsPolicyParent(binding, params, rootAdmission, current.fd, currentPath, segment);
|
|
251
|
+
childFd = child.fd;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
childFd = binding.openBeneath(current.fd, segment, secureDirectoryFlags).fd;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
const missing = windows
|
|
259
|
+
? error?.code === "ENOENT"
|
|
260
|
+
: isNotFoundPathError(error);
|
|
261
|
+
if (!missing)
|
|
262
|
+
throw windows ? error : normalizePosixParentOpenError(error, params);
|
|
263
|
+
const targetPath = (windows ? session?.retainedTargetPath : retainedTargetPath) ??
|
|
264
|
+
joinPathSegmentRoute(currentPath, route, index, params.basename);
|
|
265
|
+
childPath ??= path.join(currentPath, segment);
|
|
266
|
+
const request = Object.freeze({
|
|
267
|
+
targetPath, mutationPath: childPath, phase: "parent-create",
|
|
268
|
+
});
|
|
269
|
+
createReceipt = windows
|
|
270
|
+
? session?.tryAuthorizeAtParent(request, current.observation)
|
|
271
|
+
: params.mutationAdmission?.tryAuthorizeAtParent?.(request, current.observation);
|
|
272
|
+
if (!createReceipt) {
|
|
273
|
+
createReceipt = await authorize(windows ? { ...request } : request);
|
|
274
|
+
assertCurrent(current);
|
|
275
|
+
}
|
|
276
|
+
// POSIX always refreshes here; Windows refreshes only after a live callback.
|
|
277
|
+
if (windows) {
|
|
278
|
+
if (params.assertBeforeMutation) {
|
|
279
|
+
params.assertBeforeMutation();
|
|
280
|
+
assertCurrent(current);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
params.assertBeforeMutation?.();
|
|
285
|
+
assertCurrent(current);
|
|
286
|
+
}
|
|
287
|
+
if (windows) {
|
|
288
|
+
const mkdirChild = binding.mkdirChildBeneath;
|
|
289
|
+
if (typeof mkdirChild !== "function") {
|
|
290
|
+
throw new FsSafeError("helper-unavailable", "native direct-child parent creation is unavailable");
|
|
291
|
+
}
|
|
292
|
+
createdByMkdir = mkdirChild.call(binding, current.fd, segment, 0o777) === true;
|
|
293
|
+
child = await openWindowsPolicyParent(binding, params, rootAdmission, current.fd, currentPath, segment);
|
|
294
|
+
childFd = child.fd;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
if (fuseChildCreation && binding.mkdirOpenChildBeneath) {
|
|
298
|
+
try {
|
|
299
|
+
const child = binding.mkdirOpenChildBeneath(current.fd, segment, 0o777, secureDirectoryFlags);
|
|
300
|
+
childFd = child.fd;
|
|
301
|
+
createdByMkdir = child.created;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
throw normalizePosixParentOpenError(error, params);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
createdByMkdir = mkdirPosixPolicyChild(binding, current.fd, segment);
|
|
309
|
+
try {
|
|
310
|
+
childFd = binding.openBeneath(current.fd, segment, secureDirectoryFlags).fd;
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
throw normalizePosixParentOpenError(error, params);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
if (!child) {
|
|
320
|
+
child = retainedTargetPath ? capturePolicyParent(childFd, childPath) : undefined;
|
|
321
|
+
if (!child) {
|
|
322
|
+
retainedTargetPath = undefined;
|
|
323
|
+
child = await describePosixParent(childFd, childPath);
|
|
324
|
+
}
|
|
325
|
+
if (!sameAbsolutePath(child.guard.realPath, childPath))
|
|
326
|
+
retainedTargetPath = undefined;
|
|
327
|
+
}
|
|
328
|
+
const windowsTarget = windows
|
|
329
|
+
? joinPathSegmentRoute(child.guard.realPath, route, index + 1, params.basename)
|
|
330
|
+
: undefined;
|
|
331
|
+
if (session && windowsTarget !== session.retainedTargetPath) {
|
|
332
|
+
session.dispose();
|
|
333
|
+
session = undefined;
|
|
334
|
+
}
|
|
335
|
+
let childAuthorization;
|
|
336
|
+
if (createdByMkdir && createReceipt &&
|
|
337
|
+
(windows ? session : params.mutationAdmission?.advanceCreatedDirectory)) {
|
|
338
|
+
let evidence;
|
|
339
|
+
try {
|
|
340
|
+
const parent = windows
|
|
341
|
+
? windowsParentObservation(binding, current.fd, { ...current.guard,
|
|
342
|
+
stat: inspectFileIdentitySync(() => fsSync.fstatSync(current.fd, { bigint: true }), current.guard.stat),
|
|
343
|
+
})
|
|
344
|
+
: current.policyDirectory
|
|
345
|
+
? refreshPolicyStagedDirectoryObservation(current.policyDirectory)
|
|
346
|
+
: checkedMutationDirectory(currentPath, current.stagedDirectory.realPath, assertStagedDirectoryCurrent(current.stagedDirectory));
|
|
347
|
+
evidence = Object.freeze({
|
|
348
|
+
admission: createReceipt,
|
|
349
|
+
parent,
|
|
350
|
+
child: child.observation,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Failed optional evidence falls back to the existing ordered admission.
|
|
355
|
+
}
|
|
356
|
+
if (evidence) {
|
|
357
|
+
childAuthorization = windows
|
|
358
|
+
? session.advanceCreatedDirectory(evidence)
|
|
359
|
+
: params.mutationAdmission.advanceCreatedDirectory(evidence);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const targetPath = windowsTarget ?? retainedTargetPath ??
|
|
363
|
+
joinPathSegmentRoute(child.guard.realPath, route, index + 1, params.basename);
|
|
364
|
+
if (!windows || !childAuthorization) {
|
|
365
|
+
const request = {
|
|
366
|
+
targetPath, mutationPath: targetPath, phase: "parent",
|
|
367
|
+
};
|
|
368
|
+
if (!windows) {
|
|
369
|
+
Object.freeze(request);
|
|
370
|
+
// Existing prefixes are not the epoch's nearest parent. Their
|
|
371
|
+
// admission uses the full epoch fence below; offering an unrelated
|
|
372
|
+
// parent receipt would discard otherwise-current target evidence.
|
|
373
|
+
if (createdByMkdir) {
|
|
374
|
+
childAuthorization ??= params.mutationAdmission?.tryAuthorizeAtParent?.(request, child.observation);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!childAuthorization) {
|
|
378
|
+
await authorize(request);
|
|
379
|
+
assertCurrent(child);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
closeAfterFailure(childFd, { error });
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
const previousFd = ownedFd;
|
|
388
|
+
disposeObservation(current.fd);
|
|
389
|
+
current = child;
|
|
390
|
+
ownedFd = child.fd;
|
|
391
|
+
currentPath = child.guard.realPath;
|
|
392
|
+
if (previousFd !== undefined)
|
|
393
|
+
closeFd(previousFd);
|
|
394
|
+
}
|
|
395
|
+
if (ownedFd === undefined) {
|
|
396
|
+
throw new FsSafeError("path-mismatch", "native write parent admission did not complete");
|
|
397
|
+
}
|
|
398
|
+
ownedFd = undefined;
|
|
399
|
+
return current;
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
failure = { error };
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
finally {
|
|
406
|
+
session?.dispose();
|
|
407
|
+
if (ownedFd !== undefined)
|
|
408
|
+
closeAfterFailure(ownedFd, failure);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch (e_1) {
|
|
412
|
+
env_1.error = e_1;
|
|
413
|
+
env_1.hasError = true;
|
|
414
|
+
}
|
|
415
|
+
finally {
|
|
416
|
+
__disposeResources(env_1);
|
|
417
|
+
}
|
|
418
|
+
}
|