@openclaw/fs-safe 0.11.0 → 0.13.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 +119 -0
- package/README.md +15 -8
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +3 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +3 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +4 -3
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +13 -9
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +6 -2
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -19
- package/dist/archive-plan.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +55 -46
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +43 -10
- package/dist/archive-tar-inspect.d.ts.map +1 -1
- package/dist/archive-tar-inspect.js +4 -1
- package/dist/archive-zip-admission.d.ts +1 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +2 -2
- package/dist/archive-zip-directory.d.ts +3 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +13 -2
- package/dist/archive-zip-loader.d.ts +3 -2
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +30 -4
- package/dist/archive-zip-manifest.d.ts +5 -0
- package/dist/archive-zip-manifest.d.ts.map +1 -0
- package/dist/archive-zip-manifest.js +22 -0
- package/dist/archive-zip-names.d.ts +6 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +50 -17
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +3 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/bounded-read.js +2 -2
- package/dist/darwin-acl.d.ts +4 -0
- package/dist/darwin-acl.d.ts.map +1 -0
- package/dist/darwin-acl.js +24 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +8 -2
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +5 -3
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +67 -23
- package/dist/directory-entry-path.d.ts +3 -0
- package/dist/directory-entry-path.d.ts.map +1 -0
- package/dist/directory-entry-path.js +21 -0
- package/dist/directory-guard.d.ts +17 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +134 -48
- package/dist/directory-mode-node.d.ts +12 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +102 -4
- package/dist/effective-uid.d.ts +2 -0
- package/dist/effective-uid.d.ts.map +1 -0
- package/dist/effective-uid.js +25 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +98 -27
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +12 -2
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +36 -6
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +29 -9
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +6 -2
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -65
- package/dist/file-store-copy-source.d.ts +5 -0
- package/dist/file-store-copy-source.d.ts.map +1 -0
- package/dist/file-store-copy-source.js +31 -0
- package/dist/file-store-path.d.ts.map +1 -1
- package/dist/file-store-path.js +4 -1
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +9 -1
- package/dist/file-store-sync-directory.d.ts +16 -0
- package/dist/file-store-sync-directory.d.ts.map +1 -0
- package/dist/file-store-sync-directory.js +349 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +11 -28
- package/dist/filename.d.ts +1 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +85 -23
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +10 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +217 -33
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +16 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts +6 -0
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +67 -19
- package/dist/json-durable-queue-ownership.d.ts +8 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +134 -43
- package/dist/json-durable-queue-paths.d.ts +11 -0
- package/dist/json-durable-queue-paths.d.ts.map +1 -0
- package/dist/json-durable-queue-paths.js +42 -0
- package/dist/json-durable-queue-read.d.ts +6 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -0
- package/dist/json-durable-queue-read.js +61 -0
- package/dist/json-durable-queue.d.ts +1 -1
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +83 -134
- package/dist/json-store.d.ts.map +1 -1
- package/dist/json-store.js +5 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +54 -22
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +4 -0
- package/dist/local-file-descriptor.d.ts +17 -0
- package/dist/local-file-descriptor.d.ts.map +1 -0
- package/dist/local-file-descriptor.js +84 -0
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -7
- package/dist/move-path-cleanup.d.ts +2 -0
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +44 -18
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +31 -6
- package/dist/native-binding.d.ts +21 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-directory-observation.d.ts +17 -0
- package/dist/native-directory-observation.d.ts.map +1 -0
- package/dist/native-directory-observation.js +37 -0
- package/dist/native-parent-admission.d.ts +31 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +124 -0
- package/dist/native-pinned-write-windows.d.ts +0 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +0 -3
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +309 -40
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +15 -5
- package/dist/overwrite-file-handle.d.ts.map +1 -1
- package/dist/overwrite-file-handle.js +5 -1
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -0
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +7 -2
- package/dist/path-prefix.d.ts +7 -0
- package/dist/path-prefix.d.ts.map +1 -0
- package/dist/path-prefix.js +82 -0
- package/dist/path-scope-lexical.d.ts +14 -0
- package/dist/path-scope-lexical.d.ts.map +1 -0
- package/dist/path-scope-lexical.js +37 -0
- package/dist/path-segment-route.d.ts +7 -0
- package/dist/path-segment-route.d.ts.map +1 -0
- package/dist/path-segment-route.js +24 -0
- package/dist/path-suffix-aliases.d.ts +10 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -0
- package/dist/path-suffix-aliases.js +386 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +35 -6
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +14 -3
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +37 -8
- package/dist/pinned-mutation-admission.d.ts +25 -0
- package/dist/pinned-mutation-admission.d.ts.map +1 -0
- package/dist/pinned-mutation-admission.js +425 -0
- package/dist/pinned-mutation-observation.d.ts +34 -0
- package/dist/pinned-mutation-observation.d.ts.map +1 -0
- package/dist/pinned-mutation-observation.js +142 -0
- package/dist/pinned-mutation-shared-route.d.ts +24 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
- package/dist/pinned-mutation-shared-route.js +70 -0
- package/dist/pinned-open.d.ts +6 -0
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +28 -10
- package/dist/pinned-write-types.d.ts +75 -0
- package/dist/pinned-write-types.d.ts.map +1 -0
- package/dist/pinned-write-types.js +1 -0
- package/dist/pinned-write.d.ts +7 -33
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +152 -17
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +2 -0
- package/dist/private-producer-handoff.d.ts +16 -0
- package/dist/private-producer-handoff.d.ts.map +1 -0
- package/dist/private-producer-handoff.js +272 -0
- package/dist/private-temp-workspace.d.ts +2 -39
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +183 -77
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +33 -29
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +56 -45
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +12 -5
- package/dist/replace-file-temp-owner.d.ts +1 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +12 -1
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +24 -24
- package/dist/root-boundary.d.ts +29 -0
- package/dist/root-boundary.d.ts.map +1 -0
- package/dist/root-boundary.js +182 -0
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +89 -16
- package/dist/root-directory-creation.d.ts +13 -0
- package/dist/root-directory-creation.d.ts.map +1 -0
- package/dist/root-directory-creation.js +212 -0
- package/dist/root-directory-list.d.ts +16 -4
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +182 -33
- package/dist/root-directory.d.ts +23 -0
- package/dist/root-directory.d.ts.map +1 -0
- package/dist/root-directory.js +141 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +12 -1
- package/dist/root-file-final-admission.d.ts +21 -0
- package/dist/root-file-final-admission.d.ts.map +1 -0
- package/dist/root-file-final-admission.js +83 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +103 -24
- package/dist/root-impl.d.ts +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +316 -325
- package/dist/root-move-noreplace.d.ts +14 -0
- package/dist/root-move-noreplace.d.ts.map +1 -0
- package/dist/root-move-noreplace.js +202 -0
- package/dist/root-move-preflight.d.ts +8 -0
- package/dist/root-move-preflight.d.ts.map +1 -0
- package/dist/root-move-preflight.js +16 -0
- package/dist/root-observed-path.d.ts +9 -0
- package/dist/root-observed-path.d.ts.map +1 -0
- package/dist/root-observed-path.js +95 -0
- package/dist/root-path-errors.d.ts +12 -0
- package/dist/root-path-errors.d.ts.map +1 -0
- package/dist/root-path-errors.js +13 -0
- package/dist/root-path-existing.d.ts +2 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +63 -22
- package/dist/root-path-observation.d.ts +63 -0
- package/dist/root-path-observation.d.ts.map +1 -0
- package/dist/root-path-observation.js +180 -0
- package/dist/root-path-stat.d.ts +5 -0
- package/dist/root-path-stat.d.ts.map +1 -0
- package/dist/root-path-stat.js +101 -0
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +23 -4
- package/dist/root-path.d.ts +13 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +263 -73
- package/dist/root-paths-lexical.d.ts +9 -0
- package/dist/root-paths-lexical.d.ts.map +1 -0
- package/dist/root-paths-lexical.js +22 -0
- package/dist/root-paths.d.ts +3 -29
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +89 -178
- package/dist/root-read-admission.d.ts +26 -0
- package/dist/root-read-admission.d.ts.map +1 -0
- package/dist/root-read-admission.js +94 -0
- package/dist/root-remove-identity.d.ts +15 -0
- package/dist/root-remove-identity.d.ts.map +1 -0
- package/dist/root-remove-identity.js +89 -0
- package/dist/root-remove-receipt.d.ts +17 -0
- package/dist/root-remove-receipt.d.ts.map +1 -0
- package/dist/root-remove-receipt.js +37 -0
- package/dist/root-remove.d.ts +2 -1
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +172 -10
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +2 -1
- package/dist/root-write-admission.d.ts +68 -0
- package/dist/root-write-admission.d.ts.map +1 -0
- package/dist/root-write-admission.js +322 -0
- package/dist/root-write-compatibility.d.ts +13 -0
- package/dist/root-write-compatibility.d.ts.map +1 -0
- package/dist/root-write-compatibility.js +74 -0
- package/dist/root-write-complete-parent.d.ts +42 -0
- package/dist/root-write-complete-parent.d.ts.map +1 -0
- package/dist/root-write-complete-parent.js +195 -0
- package/dist/root-write-mode.d.ts +2 -0
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +20 -7
- package/dist/root-write-publication.d.ts +24 -0
- package/dist/root-write-publication.d.ts.map +1 -0
- package/dist/root-write-publication.js +85 -0
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +18 -7
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +3 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +62 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +18 -13
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +5 -1
- package/dist/secure-file-windows.d.ts +10 -0
- package/dist/secure-file-windows.d.ts.map +1 -0
- package/dist/secure-file-windows.js +186 -0
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +98 -10
- package/dist/secure-temp-dir.d.ts +6 -3
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +121 -101
- package/dist/secure-temp-repair.d.ts +35 -0
- package/dist/secure-temp-repair.d.ts.map +1 -0
- package/dist/secure-temp-repair.js +106 -0
- package/dist/sibling-staged-file.d.ts +3 -1
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +181 -50
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -12
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +45 -15
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -3
- package/dist/staged-directory.d.ts +14 -5
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +54 -3
- package/dist/standalone-publication-path.d.ts +2 -0
- package/dist/standalone-publication-path.d.ts.map +1 -0
- package/dist/standalone-publication-path.js +6 -0
- package/dist/stat-observation.d.ts +11 -0
- package/dist/stat-observation.d.ts.map +1 -0
- package/dist/stat-observation.js +64 -0
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +39 -2
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +7 -2
- package/dist/temp-target.d.ts +2 -0
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +133 -4
- package/dist/temp-workspace-admission.d.ts +22 -0
- package/dist/temp-workspace-admission.d.ts.map +1 -0
- package/dist/temp-workspace-admission.js +374 -0
- package/dist/temp-workspace-child-admission.d.ts +13 -0
- package/dist/temp-workspace-child-admission.d.ts.map +1 -0
- package/dist/temp-workspace-child-admission.js +95 -0
- package/dist/temp-workspace-descriptor.d.ts +48 -0
- package/dist/temp-workspace-descriptor.d.ts.map +1 -0
- package/dist/temp-workspace-descriptor.js +363 -0
- package/dist/temp-workspace-identity.d.ts +15 -0
- package/dist/temp-workspace-identity.d.ts.map +1 -0
- package/dist/temp-workspace-identity.js +41 -0
- package/dist/temp-workspace-owner.d.ts +7 -6
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +121 -61
- package/dist/temp-workspace-permissions.d.ts +4 -0
- package/dist/temp-workspace-permissions.d.ts.map +1 -0
- package/dist/temp-workspace-permissions.js +32 -0
- package/dist/temp-workspace-types.d.ts +40 -0
- package/dist/temp-workspace-types.d.ts.map +1 -0
- package/dist/temp-workspace-types.js +1 -0
- package/dist/temp.d.ts +1 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/temp.js +1 -1
- package/dist/test-hooks.d.ts +7 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/text-atomic.d.ts.map +1 -1
- package/dist/text-atomic.js +3 -1
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +54 -8
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +20 -16
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +5 -0
- package/dist/windows-path-alias.d.ts +39 -0
- package/dist/windows-path-alias.d.ts.map +1 -0
- package/dist/windows-path-alias.js +153 -0
- package/docs/advanced.md +28 -1
- package/docs/archive.md +29 -2
- package/docs/atomic.md +27 -3
- package/docs/contributing.md +4 -1
- package/docs/copy.md +4 -1
- package/docs/durability.md +18 -2
- package/docs/errors.md +14 -6
- package/docs/file-store.md +30 -3
- package/docs/filename.md +21 -7
- package/docs/guest.md +5 -0
- package/docs/install-path.md +61 -15
- package/docs/install.md +12 -8
- package/docs/json-store.md +5 -1
- package/docs/json.md +11 -0
- package/docs/mutation-policy-proof.md +65 -0
- package/docs/native-helper.md +28 -9
- package/docs/native.md +46 -10
- package/docs/output.md +33 -11
- package/docs/path-prefix.md +64 -0
- package/docs/path-suffix-aliases.md +159 -0
- package/docs/path.md +12 -1
- package/docs/permissions.md +26 -1
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +14 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +21 -4
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +21 -16
- package/docs/security-model.md +76 -9
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +27 -2
- package/docs/temp.md +262 -34
- package/docs/test-hooks.md +14 -0
- package/docs/writing.md +38 -8
- package/package.json +10 -10
package/dist/directory-guard.js
CHANGED
|
@@ -5,61 +5,102 @@ import { sameFileIdentity } from "./file-identity.js";
|
|
|
5
5
|
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { isNotFoundPathError } from "./path.js";
|
|
7
7
|
import { directoryComponentNotDirectoryError } from "./root-errors.js";
|
|
8
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
8
9
|
import { realpathSync } from "./realpath.js";
|
|
10
|
+
import { directoryEntryPath } from "./directory-entry-path.js";
|
|
11
|
+
import { assertStatObservationSync, inspectStatObservationSync, } from "./stat-observation.js";
|
|
9
12
|
export async function readDirectoryIdentity(dir) {
|
|
10
13
|
const guard = await createAsyncDirectoryGuard(dir, { bigint: true });
|
|
11
14
|
return Object.freeze({ dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
|
|
12
15
|
}
|
|
13
16
|
export function assertDirectoryIdentitySync(observedPath, expected) {
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
const operationPath = directoryOperationPath(observedPath);
|
|
18
|
+
const expectedDev = expected.dev;
|
|
19
|
+
const expectedIno = expected.ino;
|
|
20
|
+
const expectedRealPath = expected.realPath;
|
|
21
|
+
if (expectedRealPath !== undefined)
|
|
22
|
+
assertNoWindowsPathAlias(expectedRealPath, "filesystem");
|
|
23
|
+
inspectDirectoryIdentityAtPathSync(operationPath, { dev: expectedDev, ino: expectedIno });
|
|
24
|
+
if (expectedRealPath === undefined)
|
|
25
|
+
return;
|
|
26
|
+
const realPath = realpathSync.native(operationPath);
|
|
27
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
28
|
+
if (realPath !== expectedRealPath) {
|
|
16
29
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
17
30
|
}
|
|
18
31
|
}
|
|
19
32
|
export async function createAsyncDirectoryGuard(dir, options) {
|
|
20
|
-
const
|
|
33
|
+
const operationPath = directoryOperationPath(dir);
|
|
34
|
+
const stat = options?.bigint
|
|
35
|
+
? inspectDirectoryIdentityAtPathSync(operationPath, undefined, options.initial)
|
|
36
|
+
: fsSync.lstatSync(operationPath);
|
|
21
37
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
22
38
|
throw directoryComponentNotDirectoryError();
|
|
23
39
|
}
|
|
24
|
-
|
|
40
|
+
const realPath = realpathSync.native(operationPath);
|
|
41
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
42
|
+
return { dir, realPath, stat };
|
|
25
43
|
}
|
|
26
44
|
export async function assertAsyncDirectoryGuard(guard) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
45
|
+
const dir = guard.dir;
|
|
46
|
+
const operationPath = directoryOperationPath(dir);
|
|
47
|
+
const expectedRealPath = guard.realPath;
|
|
48
|
+
assertNoWindowsPathAlias(expectedRealPath, "filesystem");
|
|
49
|
+
const expectedStat = guard.stat;
|
|
50
|
+
const expectedIdentity = { dev: expectedStat.dev, ino: expectedStat.ino };
|
|
51
|
+
const stat = typeof expectedIdentity.dev === "bigint" && typeof expectedIdentity.ino === "bigint"
|
|
52
|
+
? inspectDirectoryIdentityAtPathSync(operationPath, {
|
|
53
|
+
dev: expectedIdentity.dev,
|
|
54
|
+
ino: expectedIdentity.ino,
|
|
55
|
+
})
|
|
56
|
+
: fsSync.lstatSync(operationPath);
|
|
32
57
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
33
58
|
throw directoryComponentNotDirectoryError();
|
|
34
59
|
}
|
|
35
|
-
if (!sameFileIdentity(stat,
|
|
60
|
+
if (!sameFileIdentity(stat, expectedIdentity)) {
|
|
61
|
+
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
62
|
+
}
|
|
63
|
+
const realPath = realpathSync.native(operationPath);
|
|
64
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
65
|
+
if (realPath !== expectedRealPath) {
|
|
36
66
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
37
67
|
}
|
|
38
68
|
}
|
|
39
69
|
export function createSyncDirectoryGuard(dir) {
|
|
40
|
-
const
|
|
70
|
+
const operationPath = directoryOperationPath(dir);
|
|
71
|
+
const stat = fsSync.lstatSync(operationPath);
|
|
41
72
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
42
73
|
throw directoryComponentNotDirectoryError();
|
|
43
74
|
}
|
|
44
|
-
|
|
75
|
+
const realPath = realpathSync(operationPath);
|
|
76
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
77
|
+
return { dir, realPath, stat };
|
|
45
78
|
}
|
|
46
79
|
export function assertSyncDirectoryGuard(guard) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
80
|
+
const dir = guard.dir;
|
|
81
|
+
const operationPath = directoryOperationPath(dir);
|
|
82
|
+
const expectedRealPath = guard.realPath;
|
|
83
|
+
assertNoWindowsPathAlias(expectedRealPath, "filesystem");
|
|
84
|
+
const expectedStat = guard.stat;
|
|
85
|
+
const expectedIdentity = { dev: expectedStat.dev, ino: expectedStat.ino };
|
|
86
|
+
const stat = typeof expectedIdentity.dev === "bigint" && typeof expectedIdentity.ino === "bigint"
|
|
87
|
+
? inspectDirectoryIdentityAtPathSync(operationPath, { dev: expectedIdentity.dev, ino: expectedIdentity.ino })
|
|
88
|
+
: fsSync.lstatSync(operationPath);
|
|
52
89
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
53
90
|
throw directoryComponentNotDirectoryError();
|
|
54
91
|
}
|
|
55
|
-
const realPath =
|
|
56
|
-
|
|
92
|
+
const realPath = typeof expectedIdentity.ino === "bigint"
|
|
93
|
+
? realpathSync.native(operationPath) : realpathSync(operationPath);
|
|
94
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
95
|
+
if (!sameFileIdentity(stat, expectedIdentity) || realPath !== expectedRealPath) {
|
|
57
96
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
58
97
|
}
|
|
59
98
|
}
|
|
60
99
|
export async function createNearestExistingDirectoryGuard(rootReal, targetPath, options = { bigint: false }) {
|
|
61
|
-
|
|
62
|
-
|
|
100
|
+
assertNoWindowsPathAlias(rootReal, "filesystem");
|
|
101
|
+
assertNoWindowsPathAlias(targetPath, "filesystem");
|
|
102
|
+
let current = resolvePathPreservingWindowsRoot(targetPath);
|
|
103
|
+
const root = resolvePathPreservingWindowsRoot(rootReal);
|
|
63
104
|
while (current !== root) {
|
|
64
105
|
try {
|
|
65
106
|
return await createAsyncDirectoryGuard(current, options);
|
|
@@ -74,8 +115,10 @@ export async function createNearestExistingDirectoryGuard(rootReal, targetPath,
|
|
|
74
115
|
return await createAsyncDirectoryGuard(root, options);
|
|
75
116
|
}
|
|
76
117
|
export function createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
|
|
77
|
-
|
|
78
|
-
|
|
118
|
+
assertNoWindowsPathAlias(rootReal, "filesystem");
|
|
119
|
+
assertNoWindowsPathAlias(targetPath, "filesystem");
|
|
120
|
+
let current = resolvePathPreservingWindowsRoot(targetPath);
|
|
121
|
+
const root = resolvePathPreservingWindowsRoot(rootReal);
|
|
79
122
|
while (current !== root) {
|
|
80
123
|
try {
|
|
81
124
|
return createSyncDirectoryGuard(current);
|
|
@@ -91,34 +134,77 @@ export function createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
|
|
|
91
134
|
}
|
|
92
135
|
// Recovery receipts must retain every identity bit, including on Windows.
|
|
93
136
|
export async function inspectDirectoryIdentity(dir, expected) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
137
|
+
const operationPath = directoryOperationPath(dir);
|
|
138
|
+
const expectedIdentity = expected === undefined
|
|
139
|
+
? undefined
|
|
140
|
+
: { dev: expected.dev, ino: expected.ino };
|
|
141
|
+
return inspectDirectoryIdentityAtPathSync(operationPath, expectedIdentity);
|
|
142
|
+
}
|
|
143
|
+
function directoryOperationPath(dir) {
|
|
144
|
+
assertNoWindowsPathAlias(dir, "filesystem");
|
|
145
|
+
return pathForWindowsFilesystem(directoryEntryPath(dir));
|
|
146
|
+
}
|
|
147
|
+
export function observeDirectoryIdentitySync(dir, options) {
|
|
148
|
+
const entryPath = directoryOperationPath(dir);
|
|
149
|
+
const stat = options?.bigint
|
|
150
|
+
? fsSync.lstatSync(entryPath, { bigint: true })
|
|
151
|
+
: fsSync.lstatSync(entryPath);
|
|
152
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
153
|
+
throw directoryComponentNotDirectoryError();
|
|
154
|
+
return stat;
|
|
155
|
+
}
|
|
156
|
+
export function inspectDirectoryIdentitySync(dir, expected, initial, platform = process.platform) {
|
|
157
|
+
const operationPath = directoryOperationPath(dir);
|
|
158
|
+
const expectedIdentity = expected === undefined
|
|
159
|
+
? undefined
|
|
160
|
+
: { dev: expected.dev, ino: expected.ino };
|
|
161
|
+
return inspectDirectoryIdentityAtPathSync(operationPath, expectedIdentity, initial, platform);
|
|
162
|
+
}
|
|
163
|
+
function inspectDirectoryIdentityAtPathSync(operationPath, expected, initial, platform = process.platform) {
|
|
118
164
|
return inspectFileIdentitySync(() => {
|
|
119
|
-
|
|
165
|
+
// Traversal can supply the first exact observation. A bounded retry still
|
|
166
|
+
// uses the admitted operation path and retains every known identity bit.
|
|
167
|
+
const stat = initial ?? fsSync.lstatSync(operationPath, { bigint: true });
|
|
168
|
+
initial = undefined;
|
|
169
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
170
|
+
throw directoryComponentNotDirectoryError();
|
|
171
|
+
return stat;
|
|
172
|
+
}, expected, platform);
|
|
173
|
+
}
|
|
174
|
+
export function extendDirectoryObservationGuard(observation, dir, realPath) {
|
|
175
|
+
const guard = observation;
|
|
176
|
+
guard.dir = dir;
|
|
177
|
+
guard.realPath = realPath;
|
|
178
|
+
return guard;
|
|
179
|
+
}
|
|
180
|
+
// Only stat/list receipts use this metadata fast path. Recovery and publication
|
|
181
|
+
// callers retain the BigIntStats contract of inspectDirectoryIdentitySync.
|
|
182
|
+
export function inspectDirectoryObservationSync(dir, expected) {
|
|
183
|
+
const entryPath = directoryOperationPath(dir);
|
|
184
|
+
return inspectStatObservationSync(bigint => {
|
|
185
|
+
const stat = bigint ? fsSync.lstatSync(entryPath, { bigint: true }) : fsSync.lstatSync(entryPath);
|
|
120
186
|
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
121
187
|
throw directoryComponentNotDirectoryError();
|
|
122
188
|
return stat;
|
|
123
189
|
}, expected);
|
|
124
190
|
}
|
|
191
|
+
export function assertDirectoryObservationSync(dir, expected) {
|
|
192
|
+
const entryPath = directoryOperationPath(dir);
|
|
193
|
+
return assertStatObservationSync(bigint => {
|
|
194
|
+
const stat = bigint ? fsSync.lstatSync(entryPath, { bigint: true }) : fsSync.lstatSync(entryPath);
|
|
195
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
196
|
+
throw directoryComponentNotDirectoryError();
|
|
197
|
+
return stat;
|
|
198
|
+
}, expected);
|
|
199
|
+
}
|
|
200
|
+
export function assertDirectoryObservationGuardSync(guard) {
|
|
201
|
+
const dir = guard.dir;
|
|
202
|
+
const expectedRealPath = guard.realPath;
|
|
203
|
+
assertNoWindowsPathAlias(expectedRealPath, "filesystem");
|
|
204
|
+
assertDirectoryObservationSync(dir, guard.identity);
|
|
205
|
+
const realPath = realpathSync.native(directoryOperationPath(dir));
|
|
206
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
207
|
+
if (realPath !== expectedRealPath) {
|
|
208
|
+
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { type BigIntStats } from "node:fs";
|
|
2
2
|
import { type DirectoryModeOwner } from "./directory-mode-owner.js";
|
|
3
|
+
export declare function nodeDirectorySearchOnlyFlags(): {
|
|
4
|
+
flags: number;
|
|
5
|
+
proc: boolean;
|
|
6
|
+
} | undefined;
|
|
3
7
|
/** Real Node only: injected filesystem adapters must retain descriptor-chmod semantics. */
|
|
4
8
|
export declare function pinNodeDirectoryForMode(dirPath: string, options?: {
|
|
5
9
|
expectedIdentity?: BigIntStats;
|
|
6
10
|
ownerUid?: number;
|
|
7
11
|
}): Promise<DirectoryModeOwner>;
|
|
12
|
+
/** Synchronous counterpart with the same no-follow, identity, and owner checks. */
|
|
13
|
+
export declare function pinNodeDirectoryForModeSync(dirPath: string, options?: {
|
|
14
|
+
expectedIdentity?: BigIntStats;
|
|
15
|
+
ownerUid?: number;
|
|
16
|
+
}): {
|
|
17
|
+
apply(mode: number, check?: () => void): void;
|
|
18
|
+
close(): void;
|
|
19
|
+
};
|
|
8
20
|
//# sourceMappingURL=directory-mode-node.d.ts.map
|
|
@@ -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;
|
|
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,10 +1,10 @@
|
|
|
1
1
|
import fsSync, { constants } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
-
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
5
|
-
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
4
|
+
import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
|
+
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
|
|
7
|
-
function
|
|
7
|
+
export function nodeDirectorySearchOnlyFlags() {
|
|
8
8
|
if (process.arch !== "x64" && process.arch !== "arm64")
|
|
9
9
|
return undefined;
|
|
10
10
|
// Darwin SDK O_SEARCH = O_EXEC (0x40000000) | O_DIRECTORY, on x64/arm64.
|
|
@@ -41,7 +41,7 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
41
41
|
const handle = await fs.open(dirPath, constants.O_RDONLY | flags).catch(async (error) => {
|
|
42
42
|
if (error.code !== "EACCES")
|
|
43
43
|
throw error;
|
|
44
|
-
const route =
|
|
44
|
+
const route = nodeDirectorySearchOnlyFlags();
|
|
45
45
|
if (!route)
|
|
46
46
|
throw error;
|
|
47
47
|
proc = route.proc;
|
|
@@ -91,3 +91,101 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
91
91
|
throw error;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/** Synchronous counterpart with the same no-follow, identity, and owner checks. */
|
|
95
|
+
export function pinNodeDirectoryForModeSync(dirPath, options = {}) {
|
|
96
|
+
const expected = inspectDirectoryIdentitySync(dirPath, options.expectedIdentity);
|
|
97
|
+
const assertOwner = (stat) => {
|
|
98
|
+
if (options.ownerUid !== undefined && stat.uid !== BigInt(options.ownerUid)) {
|
|
99
|
+
throw new FsSafeError("not-owned", "directory mode target must retain its expected owner");
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
assertOwner(expected);
|
|
103
|
+
if (process.platform === "win32") {
|
|
104
|
+
return {
|
|
105
|
+
apply: (_mode, check) => {
|
|
106
|
+
check?.();
|
|
107
|
+
assertOwner(inspectDirectoryIdentitySync(dirPath, expected));
|
|
108
|
+
check?.();
|
|
109
|
+
},
|
|
110
|
+
close: () => undefined,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if ([constants.O_DIRECTORY, constants.O_NOFOLLOW, constants.O_NONBLOCK, constants.O_RDONLY]
|
|
114
|
+
.some((flag) => typeof flag !== "number")) {
|
|
115
|
+
throw new FsSafeError("helper-unavailable", "no-follow directory mode descriptors are unavailable");
|
|
116
|
+
}
|
|
117
|
+
const flags = constants.O_DIRECTORY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
118
|
+
let fd;
|
|
119
|
+
let proc = false;
|
|
120
|
+
try {
|
|
121
|
+
fd = fsSync.openSync(dirPath, constants.O_RDONLY | flags);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error.code !== "EACCES")
|
|
125
|
+
throw error;
|
|
126
|
+
const route = nodeDirectorySearchOnlyFlags();
|
|
127
|
+
if (!route)
|
|
128
|
+
throw error;
|
|
129
|
+
proc = route.proc;
|
|
130
|
+
fd = fsSync.openSync(dirPath, route.flags | flags);
|
|
131
|
+
}
|
|
132
|
+
let closed = false;
|
|
133
|
+
const inspect = () => {
|
|
134
|
+
if (closed)
|
|
135
|
+
throw new FsSafeError("path-mismatch", "directory mode descriptor is closed");
|
|
136
|
+
const stat = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), expected);
|
|
137
|
+
assertOwnedDirectory(expected, stat);
|
|
138
|
+
assertOwner(stat);
|
|
139
|
+
inspectDirectoryIdentitySync(dirPath, expected);
|
|
140
|
+
return Number(stat.mode & 4095n);
|
|
141
|
+
};
|
|
142
|
+
const procPath = `/proc/self/fd/${fd}`;
|
|
143
|
+
const assertProcAuthority = () => {
|
|
144
|
+
if (fsSync.statfsSync("/proc/self/fd", { bigint: true }).type !== 0x9fa0n) {
|
|
145
|
+
throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
|
|
146
|
+
}
|
|
147
|
+
const opened = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), expected);
|
|
148
|
+
const followed = inspectFileIdentitySync(() => fsSync.statSync(procPath, { bigint: true }), expected);
|
|
149
|
+
assertOwnedDirectory(opened, followed);
|
|
150
|
+
assertOwner(opened);
|
|
151
|
+
assertOwner(followed);
|
|
152
|
+
};
|
|
153
|
+
try {
|
|
154
|
+
inspect();
|
|
155
|
+
return {
|
|
156
|
+
apply: (mode, check) => {
|
|
157
|
+
mode &= 0o7777;
|
|
158
|
+
check?.();
|
|
159
|
+
const currentMode = inspect();
|
|
160
|
+
check?.();
|
|
161
|
+
if (currentMode !== mode) {
|
|
162
|
+
if (proc)
|
|
163
|
+
assertProcAuthority();
|
|
164
|
+
check?.();
|
|
165
|
+
inspect();
|
|
166
|
+
if (proc)
|
|
167
|
+
fsSync.chmodSync(procPath, mode);
|
|
168
|
+
else
|
|
169
|
+
fsSync.fchmodSync(fd, mode);
|
|
170
|
+
if (proc)
|
|
171
|
+
assertProcAuthority();
|
|
172
|
+
}
|
|
173
|
+
const finalMode = inspect();
|
|
174
|
+
check?.();
|
|
175
|
+
if (finalMode !== mode) {
|
|
176
|
+
throw new FsSafeError("path-mismatch", "directory final mode could not be verified");
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
close: () => {
|
|
180
|
+
if (closed)
|
|
181
|
+
return;
|
|
182
|
+
closed = true;
|
|
183
|
+
fsSync.closeSync(fd);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
fsSync.closeSync(fd);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effective-uid.d.ts","sourceRoot":"","sources":["../src/effective-uid.ts"],"names":[],"mappings":"AAUA,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function isValidUid(value) {
|
|
2
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3
|
+
}
|
|
4
|
+
function unavailable(cause) {
|
|
5
|
+
return new Error("Effective user identity is unavailable.", {
|
|
6
|
+
...(cause === undefined ? {} : { cause }),
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function resolveEffectiveUid() {
|
|
10
|
+
const provider = process.geteuid;
|
|
11
|
+
if (typeof provider !== "function") {
|
|
12
|
+
throw unavailable();
|
|
13
|
+
}
|
|
14
|
+
let uid;
|
|
15
|
+
try {
|
|
16
|
+
uid = provider.call(process);
|
|
17
|
+
}
|
|
18
|
+
catch (cause) {
|
|
19
|
+
throw unavailable(cause);
|
|
20
|
+
}
|
|
21
|
+
if (!isValidUid(uid)) {
|
|
22
|
+
throw unavailable();
|
|
23
|
+
}
|
|
24
|
+
return uid;
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;
|
|
1
|
+
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAkDF,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAGD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
|
|
@@ -3,61 +3,132 @@ import { FsSafeError } from "./errors.js";
|
|
|
3
3
|
import { assertSynchronousCallbackResult, composeMutationAssertions, rethrowMutationAuthorityError, } from "./mutation-authority.js";
|
|
4
4
|
import { writeAllToFile } from "./write-file-handle.js";
|
|
5
5
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
|
+
const NO_CALLBACK_ARGUMENTS = Object.freeze([]);
|
|
7
|
+
function defineReceiverValue(receiver, key, value) {
|
|
8
|
+
Object.defineProperty(receiver, key, {
|
|
9
|
+
value, enumerable: true, writable: true, configurable: true,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function copyTransferReceiver(options, maxBytes, sizeHint, signal, onChunk, assertBeforeMutation) {
|
|
13
|
+
const receiver = {};
|
|
14
|
+
const spreadSource = Object(options);
|
|
15
|
+
// Preserve the former late object-spread boundary, including proxy traps and
|
|
16
|
+
// unrelated getter failures. Authority-bearing values stay on their early
|
|
17
|
+
// snapshots, while descriptors still run in original own-key order.
|
|
18
|
+
for (const key of Reflect.ownKeys(spreadSource)) {
|
|
19
|
+
if (!Object.getOwnPropertyDescriptor(spreadSource, key)?.enumerable)
|
|
20
|
+
continue;
|
|
21
|
+
let value;
|
|
22
|
+
switch (key) {
|
|
23
|
+
case "maxBytes":
|
|
24
|
+
value = maxBytes;
|
|
25
|
+
break;
|
|
26
|
+
case "signal":
|
|
27
|
+
value = signal;
|
|
28
|
+
break;
|
|
29
|
+
case "onChunk":
|
|
30
|
+
value = onChunk;
|
|
31
|
+
break;
|
|
32
|
+
case "assertBeforeMutation":
|
|
33
|
+
value = assertBeforeMutation;
|
|
34
|
+
break;
|
|
35
|
+
default: value = Reflect.get(spreadSource, key);
|
|
36
|
+
}
|
|
37
|
+
// Create a data property so an enumerable __proto__ key cannot alter the
|
|
38
|
+
// ordinary receiver prototype, matching object spread.
|
|
39
|
+
defineReceiverValue(receiver, key, value);
|
|
40
|
+
}
|
|
41
|
+
// Match the explicit fields that followed the former spread. Updating an
|
|
42
|
+
// existing key retains its original position; absent keys append in order.
|
|
43
|
+
defineReceiverValue(receiver, "maxBytes", maxBytes);
|
|
44
|
+
defineReceiverValue(receiver, "sizeHint", sizeHint);
|
|
45
|
+
defineReceiverValue(receiver, "targetPosition", 0);
|
|
46
|
+
defineReceiverValue(receiver, "assertBeforeMutation", assertBeforeMutation);
|
|
47
|
+
return receiver;
|
|
48
|
+
}
|
|
6
49
|
/** Copies caller-owned regular files from offset zero without moving either cursor. */
|
|
7
50
|
export async function copyFileHandle(source, target, options = {}) {
|
|
8
|
-
options.
|
|
51
|
+
// Snapshot caller-owned options before the first await. This preserves
|
|
52
|
+
// inherited/non-enumerable callbacks and prevents accessor mutation from
|
|
53
|
+
// replacing cancellation or mutation authority mid-transfer.
|
|
54
|
+
const signal = options.signal;
|
|
55
|
+
signal?.throwIfAborted();
|
|
9
56
|
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
57
|
+
const onChunk = options.onChunk;
|
|
58
|
+
const assertBeforeMutation = composeMutationAssertions(undefined, options.assertBeforeMutation);
|
|
10
59
|
const sourceStat = await inspectFileIdentity(() => {
|
|
11
|
-
|
|
60
|
+
signal?.throwIfAborted();
|
|
12
61
|
return source.stat({ bigint: true });
|
|
13
62
|
});
|
|
14
|
-
|
|
63
|
+
signal?.throwIfAborted();
|
|
15
64
|
if (!sourceStat.isFile())
|
|
16
65
|
throw new FsSafeError("not-file", "copy source handle must be a regular file");
|
|
17
66
|
if (maxBytes !== undefined && Number.isFinite(maxBytes) && sourceStat.size > BigInt(maxBytes)) {
|
|
18
67
|
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
|
|
19
68
|
}
|
|
20
69
|
const targetStat = await inspectFileIdentity(() => {
|
|
21
|
-
|
|
70
|
+
signal?.throwIfAborted();
|
|
22
71
|
return target.stat({ bigint: true });
|
|
23
72
|
});
|
|
24
|
-
|
|
73
|
+
signal?.throwIfAborted();
|
|
25
74
|
if (!targetStat.isFile())
|
|
26
75
|
throw new FsSafeError("not-file", "copy target handle must be a regular file");
|
|
27
76
|
if (sourceStat.dev === targetStat.dev && sourceStat.ino === targetStat.ino) {
|
|
28
77
|
throw new FsSafeError("path-alias", "copy source and target handles refer to the same file");
|
|
29
78
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
79
|
+
const sizeHint = Number(sourceStat.size);
|
|
80
|
+
const callbackThis = copyTransferReceiver(options, maxBytes, sizeHint, signal, onChunk, assertBeforeMutation);
|
|
81
|
+
const beforeWrite = () => {
|
|
82
|
+
signal?.throwIfAborted();
|
|
83
|
+
assertBeforeMutation?.();
|
|
84
|
+
};
|
|
85
|
+
return await transferFileHandleCore(source, target, sizeHint, maxBytes, 0, signal, onChunk, beforeWrite, callbackThis).catch(rethrowMutationAuthorityError);
|
|
37
86
|
}
|
|
38
87
|
// Root.copyIn supplies already-admitted identities and retains its owned target cursor.
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
88
|
+
export function transferFileHandle(source, target, options) {
|
|
89
|
+
try {
|
|
90
|
+
const maxBytes = options.maxBytes;
|
|
91
|
+
const sizeHint = options.sizeHint;
|
|
92
|
+
const targetPosition = options.targetPosition;
|
|
93
|
+
const signal = options.signal;
|
|
94
|
+
const onChunk = options.onChunk;
|
|
95
|
+
const assertBeforeMutation = options.assertBeforeMutation;
|
|
96
|
+
const beforeWrite = signal === undefined && assertBeforeMutation === undefined
|
|
97
|
+
? undefined
|
|
98
|
+
: () => {
|
|
99
|
+
signal?.throwIfAborted();
|
|
100
|
+
if (assertBeforeMutation) {
|
|
101
|
+
Reflect.apply(assertBeforeMutation, options, NO_CALLBACK_ARGUMENTS);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return transferFileHandleCore(source, target, sizeHint, maxBytes, targetPosition, signal, onChunk, beforeWrite, options);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// Keep the former async-function contract for synchronous option getters.
|
|
108
|
+
return Promise.reject(error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function transferFileHandleCore(source, target, sizeHint, maxBytes, targetPosition, signal, onChunk, beforeWrite, callbackThis) {
|
|
112
|
+
const limit = maxBytes ?? Infinity;
|
|
113
|
+
const buffer = Buffer.allocUnsafe(Math.min(512 * 1024, Math.max(64 * 1024, sizeHint), limit + 1));
|
|
42
114
|
let position = 0;
|
|
43
115
|
while (true) {
|
|
44
|
-
|
|
45
|
-
const { bytesRead } = await source.read(buffer, 0, Math.min(buffer.length,
|
|
46
|
-
|
|
47
|
-
if (bytesRead >
|
|
48
|
-
throw new FsSafeError("too-large", `file exceeds limit of ${
|
|
116
|
+
signal?.throwIfAborted();
|
|
117
|
+
const { bytesRead } = await source.read(buffer, 0, Math.min(buffer.length, limit - position + 1), position);
|
|
118
|
+
signal?.throwIfAborted();
|
|
119
|
+
if (bytesRead > limit - position) {
|
|
120
|
+
throw new FsSafeError("too-large", `file exceeds limit of ${limit} bytes`);
|
|
49
121
|
}
|
|
50
122
|
if (bytesRead === 0)
|
|
51
123
|
return position;
|
|
52
124
|
const chunk = buffer.subarray(0, bytesRead);
|
|
53
|
-
if (
|
|
54
|
-
|
|
125
|
+
if (onChunk) {
|
|
126
|
+
const returned = Function.prototype.call.call(onChunk, callbackThis, chunk);
|
|
127
|
+
assertSynchronousCallbackResult(returned, "onChunk");
|
|
128
|
+
}
|
|
55
129
|
await writeAllToFile(target, chunk, {
|
|
56
|
-
position:
|
|
57
|
-
assertBeforeMutation:
|
|
58
|
-
options.signal?.throwIfAborted();
|
|
59
|
-
options.assertBeforeMutation?.();
|
|
60
|
-
},
|
|
130
|
+
position: targetPosition === undefined ? undefined : targetPosition + position,
|
|
131
|
+
assertBeforeMutation: beforeWrite,
|
|
61
132
|
});
|
|
62
133
|
position += bytesRead;
|
|
63
134
|
}
|
package/dist/file-hash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAKnE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAwD3B;AA0DD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAwDD,8FAA8F;AAC9F,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAMlB"}
|