@openclaw/feishu 2026.9.8 → 2026.10.1-beta.2
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,19 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
|
-
import fs
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { inspectAtomicIdentity, runAsync, runSync, wait } from "./atomic-io.js";
|
|
3
4
|
import { readBoundedAsync, readBoundedSync } from "./bounded-read.js";
|
|
4
5
|
import { FsSafeError } from "./errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import { readOwnedCopySource, readOwnedCopySourceSync } from "./replace-file-copy-source.js";
|
|
6
|
+
import { readOwnedCopySource } from "./replace-file-copy-source.js";
|
|
7
7
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
8
8
|
import { hasErrorCode } from "./file-cleanup.js";
|
|
9
|
-
import { writeAtomicDestination
|
|
9
|
+
import { writeAtomicDestination } from "./replace-file-buffer.js";
|
|
10
10
|
import { AtomicMutation } from "./replace-file-mutation.js";
|
|
11
|
-
import { captureAtomicDestination
|
|
11
|
+
import { captureAtomicDestination } from "./replace-file-destination.js";
|
|
12
12
|
const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in syncFs.constants;
|
|
13
13
|
const NOFOLLOW = SUPPORTS_NOFOLLOW ? syncFs.constants.O_NOFOLLOW : 0;
|
|
14
14
|
const OPEN_READ_FLAGS = resolveReadOpenFlags();
|
|
15
15
|
const OPEN_READ_WRITE_FLAGS = syncFs.constants.O_RDWR | NOFOLLOW;
|
|
16
16
|
const OPEN_WRITE_EXCLUSIVE_FLAGS = syncFs.constants.O_WRONLY | syncFs.constants.O_CREAT | syncFs.constants.O_EXCL | NOFOLLOW;
|
|
17
|
-
function closeSyncAfterAdmissionFailure(fsModule, fd, error) {
|
|
18
|
-
try {
|
|
19
|
-
fsModule.closeSync(fd);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// Preserve the already-selected admission failure.
|
|
23
|
-
}
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
17
|
function admitDestinationKind(pathname, opened, dest, admission) {
|
|
27
18
|
if (admission === "hardlinks") {
|
|
28
19
|
if (pathname.isSymbolicLink() || !pathname.isFile()) {
|
|
@@ -37,27 +28,17 @@ function admitDestinationKind(pathname, opened, dest, admission) {
|
|
|
37
28
|
}
|
|
38
29
|
return pathname;
|
|
39
30
|
}
|
|
40
|
-
function
|
|
41
|
-
if ((admission === "hardlinks" || hardlinks === "reject") && opened.nlink > 1n) {
|
|
42
|
-
throw new FsSafeError("hardlink", `Hardlinked ${admission === "hardlinks" ? "atomic replace" : "copy fallback"} destination not allowed: ${dest}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function inspectPinnedDestinationSync(fsModule, fd, dest, admission, hardlinks) {
|
|
46
|
-
const opened = inspectFileIdentitySync(() => fsModule.fstatSync(fd, { bigint: true }));
|
|
47
|
-
inspectFileIdentitySync(() => admitDestinationKind(fsModule.lstatSync(dest, { bigint: true }), opened, dest, admission), opened);
|
|
48
|
-
assertDestinationLinks(opened, dest, admission, hardlinks);
|
|
49
|
-
}
|
|
50
|
-
async function openPinnedDestination(fsModule, dest, admission, hardlinks, mutation) {
|
|
31
|
+
function* openPinnedDestination(io, dest, admission, hardlinks, mutation) {
|
|
51
32
|
let preview;
|
|
52
33
|
try {
|
|
53
|
-
preview =
|
|
34
|
+
preview = yield* io.lstat(dest);
|
|
54
35
|
}
|
|
55
36
|
catch (error) {
|
|
56
37
|
if (hasErrorCode(error, "ENOENT"))
|
|
57
38
|
return null;
|
|
58
39
|
throw error;
|
|
59
40
|
}
|
|
60
|
-
if (!preview)
|
|
41
|
+
if (io.asynchronous && !preview)
|
|
61
42
|
return null;
|
|
62
43
|
if (admission === "hardlinks" && (preview.isSymbolicLink() || !preview.isFile()))
|
|
63
44
|
return null;
|
|
@@ -65,20 +46,24 @@ async function openPinnedDestination(fsModule, dest, admission, hardlinks, mutat
|
|
|
65
46
|
throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${dest}`);
|
|
66
47
|
}
|
|
67
48
|
mutation?.assert();
|
|
68
|
-
const
|
|
49
|
+
const file = yield* io.open(dest, admission === "restore" ? OPEN_READ_WRITE_FLAGS : OPEN_READ_FLAGS);
|
|
69
50
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
51
|
+
const synchronous = io.asyncFs === fs;
|
|
52
|
+
const openedInspection = inspectAtomicIdentity(io, () => file.statExact(), undefined, synchronous);
|
|
53
|
+
const opened = (io.asynchronous && !synchronous ? (yield openedInspection) : openedInspection);
|
|
54
|
+
const currentInspection = inspectAtomicIdentity(io, () => io.lstatExact(dest), opened, synchronous, stat => admitDestinationKind(stat, opened, dest, admission));
|
|
55
|
+
if (io.asynchronous && !synchronous)
|
|
56
|
+
yield currentInspection;
|
|
57
|
+
if ((admission === "hardlinks" || hardlinks === "reject") && opened.nlink > 1n) {
|
|
58
|
+
throw new FsSafeError("hardlink", `Hardlinked ${admission === "hardlinks" ? "atomic replace" : "copy fallback"} destination not allowed: ${dest}`);
|
|
76
59
|
}
|
|
77
|
-
return
|
|
60
|
+
return file;
|
|
78
61
|
}
|
|
79
62
|
catch (error) {
|
|
80
63
|
try {
|
|
81
|
-
|
|
64
|
+
const closing = file.close();
|
|
65
|
+
if (io.asynchronous)
|
|
66
|
+
yield closing;
|
|
82
67
|
}
|
|
83
68
|
catch {
|
|
84
69
|
// Preserve the already-selected admission failure.
|
|
@@ -86,72 +71,40 @@ async function openPinnedDestination(fsModule, dest, admission, hardlinks, mutat
|
|
|
86
71
|
throw error;
|
|
87
72
|
}
|
|
88
73
|
}
|
|
89
|
-
function
|
|
90
|
-
let preview;
|
|
91
|
-
try {
|
|
92
|
-
preview = fsModule.lstatSync(dest);
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
if (hasErrorCode(error, "ENOENT"))
|
|
96
|
-
return null;
|
|
97
|
-
throw error;
|
|
98
|
-
}
|
|
99
|
-
if (admission === "hardlinks" && (preview.isSymbolicLink() || !preview.isFile()))
|
|
100
|
-
return null;
|
|
101
|
-
if (admission === "restore" && preview.isSymbolicLink()) {
|
|
102
|
-
throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${dest}`);
|
|
103
|
-
}
|
|
104
|
-
mutation?.assert();
|
|
105
|
-
const fd = fsModule.openSync(dest, admission === "restore" ? OPEN_READ_WRITE_FLAGS : OPEN_READ_FLAGS);
|
|
106
|
-
try {
|
|
107
|
-
inspectPinnedDestinationSync(fsModule, fd, dest, admission, hardlinks);
|
|
108
|
-
return fd;
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
closeSyncAfterAdmissionFailure(fsModule, fd, error);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
export async function assertDestinationHardlinkPolicy(fsModule, dest, policy) {
|
|
74
|
+
export function* assertDestinationHardlinkPolicy(io, dest, policy) {
|
|
115
75
|
if (policy !== "reject")
|
|
116
76
|
return;
|
|
117
|
-
const
|
|
118
|
-
if (!
|
|
77
|
+
const file = yield* openPinnedDestination(io, dest, "hardlinks");
|
|
78
|
+
if (!file)
|
|
119
79
|
return;
|
|
120
80
|
try {
|
|
121
|
-
|
|
81
|
+
const closing = file.close();
|
|
82
|
+
if (io.asynchronous)
|
|
83
|
+
yield closing;
|
|
122
84
|
}
|
|
123
|
-
catch {
|
|
124
|
-
//
|
|
85
|
+
catch (error) {
|
|
86
|
+
// Only asynchronous admission pins have best-effort successful close.
|
|
87
|
+
if (!io.asynchronous)
|
|
88
|
+
throw error;
|
|
125
89
|
}
|
|
126
90
|
}
|
|
127
|
-
|
|
128
|
-
if (policy !== "reject")
|
|
129
|
-
return;
|
|
130
|
-
const fd = openPinnedDestinationSync(fsModule, dest, "hardlinks");
|
|
131
|
-
if (fd !== null)
|
|
132
|
-
fsModule.closeSync(fd);
|
|
133
|
-
}
|
|
134
|
-
function restoreReadOptions(stat, maxBytes) {
|
|
135
|
-
return {
|
|
136
|
-
initialSize: Number.isSafeInteger(stat.size) && stat.size >= 0 ? stat.size : undefined,
|
|
137
|
-
createLimitError: () => new FsSafeError("too-large", `Atomic replace restore snapshot exceeds maxRestoreBytes (${maxBytes})`),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
async function readRestoreSnapshot(handle, maxBytes, stat) {
|
|
141
|
-
let position = 0;
|
|
142
|
-
return await readBoundedAsync(maxBytes, async (buffer, length) => {
|
|
143
|
-
const { bytesRead } = await handle.read(buffer, 0, length, position);
|
|
144
|
-
position += bytesRead;
|
|
145
|
-
return bytesRead;
|
|
146
|
-
}, restoreReadOptions(stat, maxBytes));
|
|
147
|
-
}
|
|
148
|
-
function readRestoreSnapshotSync(fsModule, fd, maxBytes, stat) {
|
|
91
|
+
function* readRestoreSnapshot(io, file, maxBytes, stat) {
|
|
149
92
|
let position = 0;
|
|
150
|
-
|
|
151
|
-
const
|
|
93
|
+
function* read(buffer, length) {
|
|
94
|
+
const result = file.read(buffer, 0, length, position);
|
|
95
|
+
const bytesRead = io.asynchronous
|
|
96
|
+
? (yield result).bytesRead
|
|
97
|
+
: result;
|
|
152
98
|
position += bytesRead;
|
|
153
99
|
return bytesRead;
|
|
154
|
-
}
|
|
100
|
+
}
|
|
101
|
+
const options = {
|
|
102
|
+
initialSize: Number.isSafeInteger(stat.size) && stat.size >= 0 ? stat.size : undefined,
|
|
103
|
+
createLimitError: () => new FsSafeError("too-large", `Atomic replace restore snapshot exceeds maxRestoreBytes (${maxBytes})`),
|
|
104
|
+
};
|
|
105
|
+
return io.asynchronous
|
|
106
|
+
? yield* wait(readBoundedAsync(maxBytes, (buffer, length) => runAsync(read(buffer, length)), options))
|
|
107
|
+
: readBoundedSync(maxBytes, (buffer, length) => runSync(read(buffer, length)), options);
|
|
155
108
|
}
|
|
156
109
|
function restoreFailure(writeError, cleanup, restoreError) {
|
|
157
110
|
const primary = writeError instanceof Error ? writeError : new Error(String(writeError));
|
|
@@ -163,25 +116,29 @@ function restoreFailure(writeError, cleanup, restoreError) {
|
|
|
163
116
|
? `Atomic copy fallback failed; original destination restored: ${primary.message}`
|
|
164
117
|
: `Atomic copy fallback failed and original restoration failed: ${primary.message}`, { cause, details });
|
|
165
118
|
}
|
|
166
|
-
|
|
167
|
-
const originalStat =
|
|
119
|
+
function* replacePinnedWithRestore(io, file, replacement, maxRestoreBytes, replacementMode, destination, mutation) {
|
|
120
|
+
const originalStat = yield* file.stat();
|
|
168
121
|
const originalMode = originalStat.mode;
|
|
169
|
-
const original =
|
|
122
|
+
const original = yield* readRestoreSnapshot(io, file, maxRestoreBytes, originalStat);
|
|
170
123
|
try {
|
|
171
|
-
|
|
124
|
+
yield* writeAtomicDestination(file, replacement, destination);
|
|
172
125
|
if (destination)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
126
|
+
yield* destination.verify();
|
|
127
|
+
const chmod = file.chmod(replacementMode);
|
|
128
|
+
if (io.asynchronous)
|
|
129
|
+
yield chmod;
|
|
130
|
+
yield* file.sync();
|
|
176
131
|
}
|
|
177
132
|
catch (writeError) {
|
|
178
133
|
mutation.rethrowRefusal();
|
|
179
134
|
try {
|
|
180
|
-
|
|
135
|
+
yield* writeAtomicDestination(file, original, destination, true);
|
|
181
136
|
if (destination)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
137
|
+
yield* destination.verify(true);
|
|
138
|
+
const chmod = file.chmod(originalMode);
|
|
139
|
+
if (io.asynchronous)
|
|
140
|
+
yield chmod;
|
|
141
|
+
yield* file.sync();
|
|
185
142
|
throw restoreFailure(writeError, "restored");
|
|
186
143
|
}
|
|
187
144
|
catch (restoreError) {
|
|
@@ -193,61 +150,31 @@ async function replacePinnedWithRestore(fsModule, handle, replacement, maxRestor
|
|
|
193
150
|
}
|
|
194
151
|
}
|
|
195
152
|
}
|
|
196
|
-
function
|
|
197
|
-
const originalStat = fsModule.fstatSync(fd);
|
|
198
|
-
const originalMode = originalStat.mode;
|
|
199
|
-
const original = readRestoreSnapshotSync(fsModule, fd, maxRestoreBytes, originalStat);
|
|
200
|
-
try {
|
|
201
|
-
writeAtomicDestinationSync(fsModule, fd, replacement, destination?.beforeWrite, destination?.writing);
|
|
202
|
-
destination?.verify();
|
|
203
|
-
fchmodSync?.(fd, replacementMode);
|
|
204
|
-
fsModule.fsyncSync(fd);
|
|
205
|
-
}
|
|
206
|
-
catch (writeError) {
|
|
207
|
-
mutation?.rethrowRefusal();
|
|
208
|
-
try {
|
|
209
|
-
writeAtomicDestinationSync(fsModule, fd, original, destination?.beforeRestore, destination?.writing);
|
|
210
|
-
destination?.verifyRestore();
|
|
211
|
-
fchmodSync?.(fd, originalMode);
|
|
212
|
-
fsModule.fsyncSync(fd);
|
|
213
|
-
throw restoreFailure(writeError, "restored");
|
|
214
|
-
}
|
|
215
|
-
catch (restoreError) {
|
|
216
|
-
mutation?.rethrowRefusal();
|
|
217
|
-
if (restoreError instanceof FsSafeError && restoreError.details?.cleanup === "restored") {
|
|
218
|
-
throw restoreError;
|
|
219
|
-
}
|
|
220
|
-
throw restoreFailure(writeError, "restore-failed", restoreError);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
export async function copyFallbackReplace(params) {
|
|
153
|
+
export function* copyFallbackReplace(io, params) {
|
|
225
154
|
const mutation = params.mutation ?? new AtomicMutation({});
|
|
226
|
-
const source =
|
|
227
|
-
fsModule: params.fsModule,
|
|
155
|
+
const source = yield* readOwnedCopySource(io, {
|
|
228
156
|
src: params.src,
|
|
229
157
|
expectedIdentity: params.expectedSourceIdentity,
|
|
230
158
|
});
|
|
231
159
|
const { replacement } = source;
|
|
232
|
-
let
|
|
160
|
+
let file = null;
|
|
233
161
|
let closeRequiredForSuccess = false;
|
|
234
162
|
let completed = false;
|
|
235
163
|
let destination;
|
|
236
164
|
try {
|
|
237
165
|
if (params.restore === "restore-original") {
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
166
|
+
file = yield* openPinnedDestination(io, params.dest, "restore", params.destinationHardlinks, mutation);
|
|
167
|
+
if (file) {
|
|
168
|
+
if (mutation.active) {
|
|
169
|
+
destination = yield* captureAtomicDestination(io, file, params.dest, mutation, params.destinationHardlinks === "reject");
|
|
170
|
+
}
|
|
171
|
+
yield* replacePinnedWithRestore(io, file, replacement, params.maxRestoreBytes, source.mode, destination, mutation);
|
|
244
172
|
}
|
|
245
173
|
}
|
|
246
|
-
if (!
|
|
174
|
+
if (!file) {
|
|
247
175
|
let destStat = null;
|
|
248
176
|
try {
|
|
249
|
-
destStat = params.
|
|
250
|
-
? syncFs.lstatSync(params.dest) : await params.fsModule.lstat(params.dest);
|
|
177
|
+
destStat = yield* io.lstat(params.dest);
|
|
251
178
|
}
|
|
252
179
|
catch (error) {
|
|
253
180
|
if (!hasErrorCode(error, "ENOENT"))
|
|
@@ -257,112 +184,47 @@ export async function copyFallbackReplace(params) {
|
|
|
257
184
|
throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${params.dest}`);
|
|
258
185
|
}
|
|
259
186
|
if (destStat) {
|
|
260
|
-
|
|
187
|
+
yield* assertDestinationHardlinkPolicy(io, params.dest, params.destinationHardlinks);
|
|
261
188
|
mutation.assert();
|
|
262
|
-
|
|
189
|
+
yield* io.remove(params.dest);
|
|
263
190
|
mutation.removed(params.dest);
|
|
264
191
|
}
|
|
265
192
|
mutation.assert();
|
|
266
|
-
|
|
193
|
+
file = yield* io.open(params.dest, OPEN_WRITE_EXCLUSIVE_FLAGS, source.mode & 0o777);
|
|
267
194
|
if (mutation.active) {
|
|
268
|
-
destination =
|
|
195
|
+
destination = yield* captureAtomicDestination(io, file, params.dest, mutation, params.destinationHardlinks === "reject");
|
|
269
196
|
destination.writing();
|
|
270
|
-
await writeAtomicDestination(destHandle, replacement, destination.beforeWrite, destination.assertBeforeMutation);
|
|
271
|
-
await destination.verify();
|
|
272
197
|
}
|
|
273
|
-
|
|
274
|
-
|
|
198
|
+
if (destination || !io.asynchronous) {
|
|
199
|
+
yield* writeAtomicDestination(file, replacement, destination);
|
|
200
|
+
if (destination)
|
|
201
|
+
yield* destination.verify();
|
|
275
202
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
await destHandle.sync();
|
|
203
|
+
else {
|
|
204
|
+
yield file.writeFile(replacement);
|
|
279
205
|
}
|
|
206
|
+
const chmod = file.chmod(source.mode);
|
|
207
|
+
if (io.asynchronous)
|
|
208
|
+
yield chmod;
|
|
209
|
+
if (params.sync)
|
|
210
|
+
yield* file.sync();
|
|
280
211
|
closeRequiredForSuccess = !params.sync;
|
|
281
212
|
}
|
|
282
213
|
if (destination)
|
|
283
|
-
|
|
284
|
-
destination?.published();
|
|
285
|
-
completed = true;
|
|
286
|
-
}
|
|
287
|
-
finally {
|
|
288
|
-
if (destHandle) {
|
|
289
|
-
try {
|
|
290
|
-
await destHandle.close();
|
|
291
|
-
}
|
|
292
|
-
catch (closeError) {
|
|
293
|
-
if (closeRequiredForSuccess && completed) {
|
|
294
|
-
throw closeError;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
export function copyFallbackReplaceSync(params) {
|
|
301
|
-
const mutation = params.mutation ?? new AtomicMutation({});
|
|
302
|
-
const source = readOwnedCopySourceSync({
|
|
303
|
-
fsModule: params.fsModule,
|
|
304
|
-
src: params.src,
|
|
305
|
-
expectedIdentity: params.expectedSourceIdentity,
|
|
306
|
-
});
|
|
307
|
-
const { replacement } = source;
|
|
308
|
-
let destFd;
|
|
309
|
-
let closeRequiredForSuccess = false;
|
|
310
|
-
let completed = false;
|
|
311
|
-
let destination;
|
|
312
|
-
try {
|
|
313
|
-
if (params.restore === "restore-original") {
|
|
314
|
-
const pinned = openPinnedDestinationSync(params.fsModule, params.dest, "restore", params.destinationHardlinks, mutation);
|
|
315
|
-
if (pinned !== null) {
|
|
316
|
-
destFd = pinned;
|
|
317
|
-
if (mutation.active)
|
|
318
|
-
destination = captureAtomicDestinationSync(params.fsModule, pinned, params.dest, mutation, params.destinationHardlinks === "reject");
|
|
319
|
-
replacePinnedWithRestoreSync(params.fsModule, destFd, replacement, params.maxRestoreBytes, source.mode, params.fchmodSync, destination, mutation);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (destFd === undefined) {
|
|
323
|
-
let destStat = null;
|
|
324
|
-
try {
|
|
325
|
-
destStat = params.fsModule.lstatSync(params.dest);
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
if (!hasErrorCode(error, "ENOENT"))
|
|
329
|
-
throw error;
|
|
330
|
-
}
|
|
331
|
-
if (destStat?.isSymbolicLink()) {
|
|
332
|
-
throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${params.dest}`);
|
|
333
|
-
}
|
|
334
|
-
if (destStat) {
|
|
335
|
-
assertDestinationHardlinkPolicySync(params.fsModule, params.dest, params.destinationHardlinks);
|
|
336
|
-
mutation.assert();
|
|
337
|
-
params.fsModule.rmSync(params.dest, { force: true });
|
|
338
|
-
mutation.removed(params.dest);
|
|
339
|
-
}
|
|
340
|
-
mutation.assert();
|
|
341
|
-
destFd = params.fsModule.openSync(params.dest, OPEN_WRITE_EXCLUSIVE_FLAGS, source.mode & 0o777);
|
|
342
|
-
if (mutation.active)
|
|
343
|
-
destination = captureAtomicDestinationSync(params.fsModule, destFd, params.dest, mutation, params.destinationHardlinks === "reject");
|
|
344
|
-
destination?.writing();
|
|
345
|
-
writeAtomicDestinationSync(params.fsModule, destFd, replacement, destination?.beforeWrite);
|
|
346
|
-
destination?.verify();
|
|
347
|
-
params.fchmodSync?.(destFd, source.mode);
|
|
348
|
-
if (params.sync) {
|
|
349
|
-
params.fsModule.fsyncSync(destFd);
|
|
350
|
-
}
|
|
351
|
-
closeRequiredForSuccess = !params.sync;
|
|
352
|
-
}
|
|
353
|
-
destination?.verify();
|
|
214
|
+
yield* destination.verify();
|
|
354
215
|
destination?.published();
|
|
355
216
|
completed = true;
|
|
356
217
|
}
|
|
357
218
|
finally {
|
|
358
|
-
if (
|
|
219
|
+
if (file) {
|
|
359
220
|
try {
|
|
360
|
-
|
|
221
|
+
const closing = file.close();
|
|
222
|
+
if (io.asynchronous)
|
|
223
|
+
yield closing;
|
|
361
224
|
}
|
|
362
225
|
catch (closeError) {
|
|
363
|
-
if (closeRequiredForSuccess && completed)
|
|
226
|
+
if (closeRequiredForSuccess && completed)
|
|
364
227
|
throw closeError;
|
|
365
|
-
}
|
|
366
228
|
}
|
|
367
229
|
}
|
|
368
230
|
}
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
export declare function readOwnedCopySource(params: {
|
|
5
|
-
fsModule: AsyncSourceFileSystem;
|
|
1
|
+
import type { BigIntStats } from "node:fs";
|
|
2
|
+
import { type AtomicIo, type Procedure } from "./atomic-io.js";
|
|
3
|
+
export declare function readOwnedCopySource(io: AtomicIo, params: {
|
|
6
4
|
src: string;
|
|
7
5
|
expectedIdentity?: BigIntStats;
|
|
8
|
-
}):
|
|
6
|
+
}): Procedure<{
|
|
9
7
|
replacement: Buffer;
|
|
10
8
|
mode: number;
|
|
11
9
|
}>;
|
|
12
|
-
export declare function readOwnedCopySourceSync(params: {
|
|
13
|
-
fsModule: SyncSourceFileSystem;
|
|
14
|
-
src: string;
|
|
15
|
-
expectedIdentity?: BigIntStats;
|
|
16
|
-
}): {
|
|
17
|
-
replacement: Buffer;
|
|
18
|
-
mode: number;
|
|
19
|
-
};
|
|
20
|
-
export {};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs, {} from "node:fs/promises";
|
|
3
|
-
import { readBoundedSync } from "./bounded-read.js";
|
|
1
|
+
import { inspectAtomicIdentity } from "./atomic-io.js";
|
|
4
2
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
5
3
|
import { FsSafeError } from "./errors.js";
|
|
6
|
-
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
4
|
import { hasErrorCode } from "./file-cleanup.js";
|
|
8
5
|
const OPEN_READ_FLAGS = resolveReadOpenFlags();
|
|
9
6
|
function assertSourcePreview(source, src) {
|
|
@@ -25,77 +22,39 @@ function assertOpenedSource(opened, current, src) {
|
|
|
25
22
|
throw new FsSafeError("hardlink", `Hardlinked copy fallback source not allowed: ${src}`);
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
async function openSource(fsModule, src) {
|
|
34
|
-
try {
|
|
35
|
-
return await fsModule.open(src, OPEN_READ_FLAGS);
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
if (hasErrorCode(error, "ELOOP")) {
|
|
39
|
-
throw sourceSymlinkError(src, error);
|
|
40
|
-
}
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function openSourceSync(fsModule, src) {
|
|
45
|
-
try {
|
|
46
|
-
return fsModule.openSync(src, OPEN_READ_FLAGS);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
if (hasErrorCode(error, "ELOOP")) {
|
|
50
|
-
throw sourceSymlinkError(src, error);
|
|
51
|
-
}
|
|
52
|
-
throw error;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function readAllSync(fsModule, fd, size) {
|
|
56
|
-
let position = 0;
|
|
57
|
-
return readBoundedSync(Infinity, (buffer, length) => {
|
|
58
|
-
const bytesRead = fsModule.readSync(fd, buffer, 0, length, position);
|
|
59
|
-
position += bytesRead;
|
|
60
|
-
return bytesRead;
|
|
61
|
-
}, { initialSize: Number.isSafeInteger(size) && size >= 0 ? size : undefined });
|
|
62
|
-
}
|
|
63
|
-
export async function readOwnedCopySource(params) {
|
|
64
|
-
assertSourcePreview(params.fsModule === fs
|
|
65
|
-
? syncFs.lstatSync(params.src) : await params.fsModule.lstat(params.src), params.src);
|
|
66
|
-
const handle = await openSource(params.fsModule, params.src);
|
|
25
|
+
export function* readOwnedCopySource(io, params) {
|
|
26
|
+
assertSourcePreview(yield* io.lstat(params.src), params.src);
|
|
27
|
+
const handle = yield* openSource(io, params.src);
|
|
67
28
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
29
|
+
const openedInspection = inspectAtomicIdentity(io, () => handle.statExact(), params.expectedIdentity);
|
|
30
|
+
const opened = (io.asynchronous ? (yield openedInspection) : openedInspection);
|
|
31
|
+
const currentInspection = inspectAtomicIdentity(io, () => io.lstatExact(params.src), opened);
|
|
32
|
+
const current = (io.asynchronous ? (yield currentInspection) : currentInspection);
|
|
72
33
|
assertOpenedSource(opened, current, params.src);
|
|
73
|
-
|
|
34
|
+
const replacement = yield* handle.readFile(io.asynchronous ? undefined : Number(opened.size));
|
|
35
|
+
return { replacement, mode: Number(opened.mode) };
|
|
74
36
|
}
|
|
75
37
|
finally {
|
|
76
38
|
try {
|
|
77
|
-
|
|
39
|
+
const closing = handle.close();
|
|
40
|
+
if (io.asynchronous)
|
|
41
|
+
yield closing;
|
|
78
42
|
}
|
|
79
43
|
catch {
|
|
80
|
-
//
|
|
44
|
+
// Source close never replaces the selected read or admission result.
|
|
81
45
|
}
|
|
82
46
|
}
|
|
83
47
|
}
|
|
84
|
-
|
|
85
|
-
assertSourcePreview(params.fsModule.lstatSync(params.src), params.src);
|
|
86
|
-
const fd = openSourceSync(params.fsModule, params.src);
|
|
48
|
+
function* openSource(io, src) {
|
|
87
49
|
try {
|
|
88
|
-
|
|
89
|
-
const current = inspectFileIdentitySync(() => params.fsModule.lstatSync(params.src, { bigint: true }), opened);
|
|
90
|
-
assertOpenedSource(opened, current, params.src);
|
|
91
|
-
return { replacement: readAllSync(params.fsModule, fd, Number(opened.size)), mode: Number(opened.mode) };
|
|
50
|
+
return yield* io.open(src, OPEN_READ_FLAGS);
|
|
92
51
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// Best-effort close after reading a copy-fallback source.
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (hasErrorCode(error, "ELOOP")) {
|
|
54
|
+
throw new FsSafeError("symlink", `Refusing copy fallback from non-file source: ${src}`, {
|
|
55
|
+
cause: error,
|
|
56
|
+
});
|
|
99
57
|
}
|
|
58
|
+
throw error;
|
|
100
59
|
}
|
|
101
60
|
}
|
|
@@ -1,46 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs, { type FileHandle } from "node:fs/promises";
|
|
3
|
-
import { type DirectoryModeOwner } from "./directory-mode-node.js";
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
4
2
|
import type { AtomicMutation } from "./replace-file-mutation.js";
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
export declare function syncDirectoryBestEffort(fsModule: Pick<typeof fs, "open">, dirPath: string): Promise<void>;
|
|
9
|
-
export declare function syncDirectoryBestEffortSync(fsModule: Pick<typeof syncFs, "openSync" | "fsyncSync" | "closeSync">, dirPath: string): void;
|
|
10
|
-
export declare function pinDirectoryForMode(params: {
|
|
11
|
-
fsModule: AsyncTempFileSystem;
|
|
12
|
-
dirPath: string;
|
|
13
|
-
/** Compatibility for best-effort directory modes; admission and close still fail closed. */
|
|
14
|
-
ignoreChmodError?: boolean;
|
|
15
|
-
mutation?: AtomicMutation;
|
|
16
|
-
}): Promise<DirectoryModeOwner | undefined>;
|
|
17
|
-
export declare function applyDirectoryMode(params: Parameters<typeof pinDirectoryForMode>[0] & {
|
|
18
|
-
mode: number;
|
|
19
|
-
}): Promise<void>;
|
|
20
|
-
export declare function applyDirectoryModeSync(params: {
|
|
21
|
-
fsModule: SyncTempFileSystem;
|
|
3
|
+
import { type AtomicFile, type AtomicIo, type Procedure } from "./atomic-io.js";
|
|
4
|
+
export declare function syncDirectoryBestEffort(io: AtomicIo, dirPath: string): Procedure<void>;
|
|
5
|
+
export declare function applyDirectoryMode(io: AtomicIo, params: {
|
|
22
6
|
dirPath: string;
|
|
23
7
|
mode: number;
|
|
24
|
-
|
|
8
|
+
ignoreChmodError?: boolean;
|
|
25
9
|
mutation?: AtomicMutation;
|
|
26
|
-
}): void
|
|
27
|
-
export declare function writeTempFile(params: {
|
|
28
|
-
fsModule: AsyncTempFileSystem;
|
|
10
|
+
}): Procedure<void>;
|
|
11
|
+
export declare function writeTempFile(io: AtomicIo, params: {
|
|
29
12
|
tempPath: string;
|
|
30
13
|
content: string | Uint8Array;
|
|
31
14
|
mode: number;
|
|
32
15
|
sync: boolean;
|
|
33
16
|
onIdentity?: (identity: BigIntStats) => void;
|
|
34
17
|
mutation?: AtomicMutation;
|
|
35
|
-
}):
|
|
36
|
-
|
|
18
|
+
}): Procedure<{
|
|
19
|
+
file: AtomicFile;
|
|
37
20
|
identity: BigIntStats;
|
|
38
21
|
}>;
|
|
39
|
-
export declare function writeTempFileSync(params: Omit<Parameters<typeof writeTempFile>[0], "fsModule"> & {
|
|
40
|
-
fsModule: SyncTempFileSystem;
|
|
41
|
-
fchmodSync?: SyncFchmod;
|
|
42
|
-
}): {
|
|
43
|
-
fd: number;
|
|
44
|
-
identity: BigIntStats;
|
|
45
|
-
};
|
|
46
|
-
export {};
|