@openclaw/fs-safe 0.8.6 → 0.10.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 +93 -0
- package/README.md +17 -0
- package/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-crc32.d.ts.map +1 -1
- package/dist/archive-crc32.js +6 -1
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +3 -4
- package/dist/archive-durability.d.ts +24 -0
- package/dist/archive-durability.d.ts.map +1 -0
- package/dist/archive-durability.js +180 -0
- package/dist/archive-gzip-tail.d.ts +2 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +22 -4
- package/dist/archive-input.js +4 -4
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +3 -2
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +42 -9
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +4 -2
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +72 -70
- package/dist/archive-staging.d.ts +1 -1
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +24 -19
- package/dist/archive-tar-stream.d.ts +11 -4
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +20 -8
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +2 -1
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/bounded-read.d.ts +7 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +73 -43
- package/dist/clone-metadata.d.ts +19 -0
- package/dist/clone-metadata.d.ts.map +1 -0
- package/dist/clone-metadata.js +32 -0
- package/dist/copy-file-input.d.ts +23 -0
- package/dist/copy-file-input.d.ts.map +1 -0
- package/dist/copy-file-input.js +91 -0
- package/dist/copy-policy.d.ts +3 -0
- package/dist/copy-policy.d.ts.map +1 -0
- package/dist/copy-policy.js +8 -0
- package/dist/copy-publication.d.ts +15 -0
- package/dist/copy-publication.d.ts.map +1 -0
- package/dist/copy-publication.js +30 -0
- package/dist/copy-tree-portable.d.ts +9 -0
- package/dist/copy-tree-portable.d.ts.map +1 -0
- package/dist/copy-tree-portable.js +191 -0
- package/dist/copy.d.ts +16 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +123 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +4 -4
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/error-detail.d.ts.map +1 -1
- package/dist/error-detail.js +4 -1
- package/dist/file-hash.d.ts +6 -2
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +49 -12
- package/dist/file-store-sync-write.d.ts +1 -0
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +9 -6
- package/dist/file-store.d.ts +4 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +10 -1
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +2 -12
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +1 -2
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +1 -0
- package/dist/guarded-mutation.d.ts +2 -0
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +9 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +9 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +9 -13
- package/dist/json-document-store.d.ts +3 -0
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +1 -0
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue-ownership.js +3 -2
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +31 -27
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +22 -24
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +4 -3
- package/dist/move-path-stage.d.ts +8 -0
- package/dist/move-path-stage.d.ts.map +1 -0
- package/dist/move-path-stage.js +55 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +55 -47
- package/dist/mutation-authority.d.ts +8 -0
- package/dist/mutation-authority.d.ts.map +1 -0
- package/dist/mutation-authority.js +36 -0
- package/dist/native-binding.d.ts +24 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +11 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +18 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +1 -0
- package/dist/native-staged-file.d.ts +3 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +35 -8
- package/dist/opened-file-failure.d.ts +1 -1
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +3 -2
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +3 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +38 -48
- package/dist/permissions.js +3 -3
- package/dist/pinned-operation.js +1 -1
- package/dist/pinned-write.d.ts +5 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +50 -30
- package/dist/positional-read.d.ts +9 -0
- package/dist/positional-read.d.ts.map +1 -0
- package/dist/positional-read.js +36 -0
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +9 -3
- package/dist/publish-copy-stage.d.ts +13 -0
- package/dist/publish-copy-stage.d.ts.map +1 -0
- package/dist/publish-copy-stage.js +47 -0
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +26 -25
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +30 -17
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -5
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +6 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +8 -3
- package/dist/root-context.d.ts +3 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +51 -26
- package/dist/root-directory-list.d.ts +24 -0
- package/dist/root-directory-list.d.ts.map +1 -0
- package/dist/root-directory-list.js +201 -0
- package/dist/root-errors.d.ts +1 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +3 -0
- package/dist/root-file.d.ts +2 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +12 -4
- package/dist/root-impl.d.ts +6 -50
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +342 -208
- package/dist/root-options.d.ts +66 -0
- package/dist/root-options.d.ts.map +1 -0
- package/dist/root-options.js +18 -0
- package/dist/root-path-existing.d.ts +5 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +56 -1
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +74 -162
- package/dist/root-symlink-policy.d.ts +13 -0
- package/dist/root-symlink-policy.d.ts.map +1 -0
- package/dist/root-symlink-policy.js +34 -0
- package/dist/root-walk.d.ts +5 -4
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +99 -50
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +1 -4
- package/dist/root.d.ts +3 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts +1 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secret-read-async.js +5 -5
- package/dist/secure-file.d.ts +1 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +23 -13
- package/dist/sibling-staged-file.js +7 -7
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +16 -7
- package/dist/sidecar-lock-acquire.d.ts +1 -1
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +6 -4
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +5 -3
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +16 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -1
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +3 -2
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +9 -4
- package/dist/timing.d.ts +1 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +25 -6
- package/dist/trash.js +2 -2
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +7 -27
- package/dist/windows-owner.d.ts +9 -12
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +24 -58
- package/dist/write-file-handle.d.ts +6 -0
- package/dist/write-file-handle.d.ts.map +1 -0
- package/dist/write-file-handle.js +25 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -2
- package/docs/advanced.md +18 -4
- package/docs/archive.md +57 -9
- package/docs/atomic.md +7 -0
- package/docs/contributing.md +7 -0
- package/docs/copy.md +86 -0
- package/docs/durability.md +17 -2
- package/docs/errors.md +1 -1
- package/docs/file-store.md +48 -3
- package/docs/json-store.md +10 -0
- package/docs/local-roots.md +8 -1
- package/docs/native.md +14 -1
- package/docs/permissions.md +20 -10
- package/docs/positional-read.md +63 -0
- package/docs/reading.md +6 -0
- package/docs/root.md +168 -6
- package/docs/secret-file.md +6 -0
- package/docs/security-model.md +14 -0
- package/docs/sidecar-lock.md +2 -0
- package/docs/timing.md +2 -0
- package/docs/types.md +18 -11
- package/docs/walk.md +54 -5
- package/docs/writing.md +25 -5
- package/package.json +16 -11
package/dist/pinned-write.js
CHANGED
|
@@ -17,6 +17,10 @@ import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
|
17
17
|
import { cleanupPinnedFilePath } from "./replace-file-temp-owner.js";
|
|
18
18
|
import { withSidecarLock } from "./sidecar-lock.js";
|
|
19
19
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
20
|
+
import { writeAllToFile } from "./write-file-handle.js";
|
|
21
|
+
import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
|
|
22
|
+
import { writeCopyFileToFd } from "./copy-file-input.js";
|
|
23
|
+
import { publishCopyStage } from "./publish-copy-stage.js";
|
|
20
24
|
function byteLength(input, encoding) {
|
|
21
25
|
return typeof input === "string"
|
|
22
26
|
? Buffer.byteLength(input, encoding ?? "utf8")
|
|
@@ -36,20 +40,13 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
36
40
|
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
|
-
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
43
|
+
async function writeStreamToHandle(stream, handle, maxBytes, assertBeforeMutation) {
|
|
40
44
|
let bytes = 0;
|
|
41
45
|
for await (const chunk of stream) {
|
|
42
46
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
43
47
|
bytes += buffer.byteLength;
|
|
44
48
|
assertWithinMaxBytes(bytes, maxBytes);
|
|
45
|
-
|
|
46
|
-
while (offset < buffer.byteLength) {
|
|
47
|
-
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
|
|
48
|
-
if (bytesWritten <= 0) {
|
|
49
|
-
throw new FsSafeError("helper-failed", "fallback stream write made no progress");
|
|
50
|
-
}
|
|
51
|
-
offset += bytesWritten;
|
|
52
|
-
}
|
|
49
|
+
await writeAllToFile(handle, buffer, { assertBeforeMutation });
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
52
|
export async function runPinnedWriteHelper(params) {
|
|
@@ -95,7 +92,6 @@ async function runPinnedWriteFallback(params) {
|
|
|
95
92
|
? { dev: params.rootIdentity.dev, ino: params.rootIdentity.ino } : undefined;
|
|
96
93
|
if (exactRoot)
|
|
97
94
|
await inspectDirectoryIdentity(params.rootPath, exactRoot);
|
|
98
|
-
const guardOptions = { bigint: exactRoot !== undefined };
|
|
99
95
|
let parentPath = params.relativeParentPath
|
|
100
96
|
? path.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
101
97
|
: params.rootPath;
|
|
@@ -107,15 +103,23 @@ async function runPinnedWriteFallback(params) {
|
|
|
107
103
|
parentPath = await mkdirPathComponentsWithGuards({
|
|
108
104
|
rootReal: params.rootPath,
|
|
109
105
|
targetPath: parentPath,
|
|
106
|
+
assertBeforeMutation: params.assertBeforeMutation,
|
|
110
107
|
beforeComponent: async (componentPath) => await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("mkdir", componentPath),
|
|
111
108
|
});
|
|
112
109
|
}
|
|
113
110
|
const parentGuard = params.mkdir
|
|
114
|
-
? await createAsyncDirectoryGuard(parentPath,
|
|
115
|
-
: await createNearestExistingDirectoryGuard(params.rootPath, parentPath,
|
|
111
|
+
? await createAsyncDirectoryGuard(parentPath, { bigint: true })
|
|
112
|
+
: await createNearestExistingDirectoryGuard(params.rootPath, parentPath, { bigint: true });
|
|
116
113
|
const targetPath = path.join(parentPath, params.basename);
|
|
117
|
-
if (params.overwrite === false) {
|
|
118
|
-
const
|
|
114
|
+
if (params.overwrite === false && params.input.kind !== "file") {
|
|
115
|
+
const assertBeforeMutation = () => {
|
|
116
|
+
assertFinalSymlinkRejected(targetPath, params.rejectFinalSymlink);
|
|
117
|
+
params.assertBeforeMutation?.();
|
|
118
|
+
};
|
|
119
|
+
const handle = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
120
|
+
assertBeforeMutation();
|
|
121
|
+
return await fs.open(targetPath, fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL, params.mode);
|
|
122
|
+
}, {
|
|
119
123
|
onPostGuardFailure: async (openedHandle) => {
|
|
120
124
|
// The parent failed verification, so targetPath may now resolve
|
|
121
125
|
// somewhere else. Close the fd, but do not clean up by path.
|
|
@@ -129,15 +133,12 @@ async function runPinnedWriteFallback(params) {
|
|
|
129
133
|
createdIdentity = verificationIdentity;
|
|
130
134
|
if (params.input.kind === "buffer") {
|
|
131
135
|
assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
await handle.writeFile(params.input.data);
|
|
137
|
-
}
|
|
136
|
+
await writeAllToFile(handle, params.input.data, {
|
|
137
|
+
encoding: params.input.encoding, assertBeforeMutation,
|
|
138
|
+
});
|
|
138
139
|
}
|
|
139
140
|
else {
|
|
140
|
-
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
141
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes, assertBeforeMutation);
|
|
141
142
|
}
|
|
142
143
|
// Content writes may clear set-ID bits; finalize them through the owned fd.
|
|
143
144
|
await handle.chmod(params.mode);
|
|
@@ -176,20 +177,21 @@ async function runPinnedWriteFallback(params) {
|
|
|
176
177
|
let readHandle;
|
|
177
178
|
let renamed = false;
|
|
178
179
|
try {
|
|
180
|
+
params.assertBeforeMutation?.();
|
|
179
181
|
handle = await fs.open(tempPath, tempFlags, params.mode);
|
|
180
182
|
let verificationIdentity = fsSync.fstatSync(handle.fd, { bigint: true });
|
|
181
183
|
tempIdentity = verificationIdentity;
|
|
182
184
|
if (params.input.kind === "buffer") {
|
|
183
185
|
assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
await writeAllToFile(handle, params.input.data, {
|
|
187
|
+
encoding: params.input.encoding, assertBeforeMutation: params.assertBeforeMutation,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else if (params.input.kind === "file") {
|
|
191
|
+
await writeCopyFileToFd(handle.fd, params.input, params.maxBytes, params.assertBeforeMutation);
|
|
190
192
|
}
|
|
191
193
|
else {
|
|
192
|
-
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
194
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes, params.assertBeforeMutation);
|
|
193
195
|
}
|
|
194
196
|
tempStat = fsSync.fstatSync(handle.fd);
|
|
195
197
|
const tempPathStat = fsSync.lstatSync(tempPath);
|
|
@@ -200,10 +202,28 @@ async function runPinnedWriteFallback(params) {
|
|
|
200
202
|
await handle.chmod(params.mode);
|
|
201
203
|
if (params.sync !== false)
|
|
202
204
|
await syncFileBestEffort(handle);
|
|
205
|
+
if (params.input.kind === "file")
|
|
206
|
+
await params.input.verifySource();
|
|
203
207
|
let verifiedIdentity = expectedTempStat;
|
|
204
208
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
assertFinalSymlinkRejected(targetPath, params.rejectFinalSymlink);
|
|
210
|
+
if (params.overwrite === false) {
|
|
211
|
+
publishCopyStage({
|
|
212
|
+
temporaryPath: tempPath, targetPath, fd: handle.fd,
|
|
213
|
+
identity: tempIdentity, parentGuard,
|
|
214
|
+
assertBeforeMutation: params.assertBeforeMutation,
|
|
215
|
+
onPublished: (identity) => {
|
|
216
|
+
renamed = true;
|
|
217
|
+
params.onPublished?.(identity);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
params.assertBeforeMutation?.();
|
|
223
|
+
await fs.rename(tempPath, targetPath);
|
|
224
|
+
renamed = true;
|
|
225
|
+
params.onPublished?.(verificationIdentity);
|
|
226
|
+
}
|
|
207
227
|
await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
|
|
208
228
|
if (params.sync !== false)
|
|
209
229
|
await syncDirectoryBestEffort(parentPath);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
export type ReadFileWindowOptions = {
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
};
|
|
5
|
+
/** Fills a caller-owned buffer from an explicit position, stopping at EOF. */
|
|
6
|
+
export declare function readFileWindowFully(handle: FileHandle, buffer: Buffer, position: number, { signal }?: ReadFileWindowOptions): Promise<number>;
|
|
7
|
+
/** Synchronous positional read; neither variant advances or closes its descriptor. */
|
|
8
|
+
export declare function readFileWindowFullySync(fd: number, buffer: Buffer, position: number): number;
|
|
9
|
+
//# sourceMappingURL=positional-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"positional-read.d.ts","sourceRoot":"","sources":["../src/positional-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AASF,8EAA8E;AAC9E,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,EAAE,MAAM,EAAE,GAAE,qBAA0B,GACrC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW5F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
// Node's synchronous read length is a signed 32-bit value on supported runtimes.
|
|
3
|
+
const MAX_READ_BYTES = 0x7fffffff;
|
|
4
|
+
function validateWindow(buffer, position) {
|
|
5
|
+
if (!Number.isSafeInteger(position) || position < 0 ||
|
|
6
|
+
!Number.isSafeInteger(position + buffer.length)) {
|
|
7
|
+
throw new RangeError("read position and window end must be non-negative safe integers");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Fills a caller-owned buffer from an explicit position, stopping at EOF. */
|
|
11
|
+
export async function readFileWindowFully(handle, buffer, position, { signal } = {}) {
|
|
12
|
+
validateWindow(buffer, position);
|
|
13
|
+
signal?.throwIfAborted();
|
|
14
|
+
let total = 0;
|
|
15
|
+
while (total < buffer.length) {
|
|
16
|
+
const { bytesRead } = await handle.read(buffer, total, Math.min(buffer.length - total, MAX_READ_BYTES), position + total);
|
|
17
|
+
// Settle the pending read before cancellation lets the caller reuse its buffer or handle.
|
|
18
|
+
signal?.throwIfAborted();
|
|
19
|
+
if (bytesRead === 0)
|
|
20
|
+
break;
|
|
21
|
+
total += bytesRead;
|
|
22
|
+
}
|
|
23
|
+
return total;
|
|
24
|
+
}
|
|
25
|
+
/** Synchronous positional read; neither variant advances or closes its descriptor. */
|
|
26
|
+
export function readFileWindowFullySync(fd, buffer, position) {
|
|
27
|
+
validateWindow(buffer, position);
|
|
28
|
+
let total = 0;
|
|
29
|
+
while (total < buffer.length) {
|
|
30
|
+
const bytesRead = fs.readSync(fd, buffer, total, Math.min(buffer.length - total, MAX_READ_BYTES), position + total);
|
|
31
|
+
if (bytesRead === 0)
|
|
32
|
+
break;
|
|
33
|
+
total += bytesRead;
|
|
34
|
+
}
|
|
35
|
+
return total;
|
|
36
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,SAAS,CACP,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC/C,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAC3D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CAAC;IAC5F,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,IAAI,0BAA0B,CAAC;IACtC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;
|
|
1
|
+
{"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,SAAS,CACP,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC/C,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAC3D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CAAC;IAC5F,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,IAAI,0BAA0B,CAAC;IACtC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;AA4KF,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAExB;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,GAC5B,iBAAiB,CAgGnB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,CAAC,GACvC,CAAC,CAUH"}
|
|
@@ -67,7 +67,7 @@ function throwTempWorkspaceOpenFailure(failure) {
|
|
|
67
67
|
}
|
|
68
68
|
async function ensurePrivateDirectory(dir, mode) {
|
|
69
69
|
await fs.mkdir(dir, { recursive: true, mode });
|
|
70
|
-
const stat =
|
|
70
|
+
const stat = fsSync.statSync(dir);
|
|
71
71
|
if (!stat.isDirectory()) {
|
|
72
72
|
throw new Error(`Temp root must be a directory: ${dir}`);
|
|
73
73
|
}
|
|
@@ -91,7 +91,13 @@ async function createTempWorkspace(options) {
|
|
|
91
91
|
const mode = options.mode ?? 0o600;
|
|
92
92
|
const cleanupSafety = resolveTempWorkspaceCleanupSafety(options.cleanupSafety);
|
|
93
93
|
const requestedRoot = path.resolve(options.rootDir);
|
|
94
|
-
|
|
94
|
+
let root = requestedRoot;
|
|
95
|
+
try {
|
|
96
|
+
root = fsSync.realpathSync.native(requestedRoot);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
root = requestedRoot;
|
|
100
|
+
}
|
|
95
101
|
await ensurePrivateDirectory(root, dirMode);
|
|
96
102
|
const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety);
|
|
97
103
|
let dir;
|
|
@@ -101,7 +107,7 @@ async function createTempWorkspace(options) {
|
|
|
101
107
|
try {
|
|
102
108
|
dir = await fs.mkdtemp(path.join(root, sanitizeTempPrefix(options.prefix)));
|
|
103
109
|
await fs.chmod(dir, dirMode).catch(() => undefined);
|
|
104
|
-
stat =
|
|
110
|
+
stat = fsSync.lstatSync(dir, { bigint: true });
|
|
105
111
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
106
112
|
throw new Error(`Temp workspace must be a directory: ${dir}`);
|
|
107
113
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import type { AnyAsyncDirectoryGuard } from "./directory-guard.js";
|
|
3
|
+
import type { PublishedWriteIdentity } from "./pinned-write.js";
|
|
4
|
+
export declare function publishCopyStage(params: {
|
|
5
|
+
temporaryPath: string;
|
|
6
|
+
targetPath: string;
|
|
7
|
+
fd: number;
|
|
8
|
+
identity: BigIntStats;
|
|
9
|
+
parentGuard: AnyAsyncDirectoryGuard;
|
|
10
|
+
assertBeforeMutation?: () => void;
|
|
11
|
+
onPublished?: (identity: PublishedWriteIdentity) => void;
|
|
12
|
+
}): void;
|
|
13
|
+
//# sourceMappingURL=publish-copy-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-copy-stage.d.ts","sourceRoot":"","sources":["../src/publish-copy-stage.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAIhE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,sBAAsB,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC1D,GAAG,IAAI,CAqCP"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
5
|
+
// Node has no portable no-replace rename. Link only our completed private stage,
|
|
6
|
+
// then unlink it in the same JS turn so local readers never observe two names.
|
|
7
|
+
export function publishCopyStage(params) {
|
|
8
|
+
const { identity, temporaryPath, targetPath } = params;
|
|
9
|
+
const staged = fs.lstatSync(temporaryPath, { bigint: true });
|
|
10
|
+
const opened = fs.fstatSync(params.fd, { bigint: true });
|
|
11
|
+
if (!staged.isFile() || staged.isSymbolicLink() || staged.nlink !== 1n ||
|
|
12
|
+
!sameFileIdentityForCleanup(staged, identity) ||
|
|
13
|
+
!sameFileIdentityForCleanup(opened, identity)) {
|
|
14
|
+
throw new FsSafeError("path-mismatch", "exclusive copy stage changed before publication");
|
|
15
|
+
}
|
|
16
|
+
params.assertBeforeMutation?.();
|
|
17
|
+
fs.linkSync(temporaryPath, targetPath);
|
|
18
|
+
let observerRejected = false;
|
|
19
|
+
let observerError;
|
|
20
|
+
try {
|
|
21
|
+
params.onPublished?.(identity);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
observerRejected = true;
|
|
25
|
+
observerError = error;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const parent = fs.lstatSync(path.dirname(temporaryPath), { bigint: true });
|
|
29
|
+
const current = fs.lstatSync(temporaryPath, { bigint: true });
|
|
30
|
+
if (parent.isSymbolicLink() || !parent.isDirectory() ||
|
|
31
|
+
!sameFileIdentityForCleanup(parent, params.parentGuard.stat) ||
|
|
32
|
+
current.isSymbolicLink() || !current.isFile() ||
|
|
33
|
+
!sameFileIdentityForCleanup(current, identity)) {
|
|
34
|
+
throw new FsSafeError("path-mismatch", "published copy temporary name changed before cleanup");
|
|
35
|
+
}
|
|
36
|
+
// This removes the stage's name, never the published destination or source.
|
|
37
|
+
fs.unlinkSync(temporaryPath);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new FsSafeError("helper-failed", "published copy temporary cleanup failed", {
|
|
41
|
+
cause: observerRejected ? new AggregateError([observerError, error], "observer and cleanup failed") : error,
|
|
42
|
+
details: { publication: "published", path: targetPath, dev: identity.dev, ino: identity.ino, cleanup: "failed" },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (observerRejected)
|
|
46
|
+
throw observerError;
|
|
47
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GACpC,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;
|
|
1
|
+
{"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GACpC,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AA2ND,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,aAAa,CAAC,EAAE,qCAAqC,CAAC;IACtD,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4MtC"}
|
package/dist/publish-file.js
CHANGED
|
@@ -40,8 +40,8 @@ async function openNativeParent(filePath) {
|
|
|
40
40
|
const parentPath = path.dirname(filePath);
|
|
41
41
|
const handle = await fs.open(parentPath, directoryOpenFlags());
|
|
42
42
|
try {
|
|
43
|
-
const pathname =
|
|
44
|
-
const opened =
|
|
43
|
+
const pathname = fsSync.lstatSync(parentPath, { bigint: true });
|
|
44
|
+
const opened = fsSync.fstatSync(handle.fd, { bigint: true });
|
|
45
45
|
if (pathname.isSymbolicLink() || !sameFileIdentity(pathname, opened)) {
|
|
46
46
|
throw new FsSafeError("path-mismatch", "publication parent changed while opening");
|
|
47
47
|
}
|
|
@@ -55,8 +55,8 @@ async function openNativeParent(filePath) {
|
|
|
55
55
|
async function assertPinnedSourceCurrent(params) {
|
|
56
56
|
// Windows file indexes can exceed Number.MAX_SAFE_INTEGER, so publication
|
|
57
57
|
// fences must compare bigint stats instead of rounded numeric identities.
|
|
58
|
-
const opened =
|
|
59
|
-
const current =
|
|
58
|
+
const opened = fsSync.fstatSync(params.handle.fd, { bigint: true });
|
|
59
|
+
const current = fsSync.lstatSync(params.sourcePath, { bigint: true });
|
|
60
60
|
if (!opened.isFile() ||
|
|
61
61
|
current.isSymbolicLink() ||
|
|
62
62
|
!current.isFile() ||
|
|
@@ -90,15 +90,15 @@ async function copyPinnedSource(params) {
|
|
|
90
90
|
throw error;
|
|
91
91
|
}
|
|
92
92
|
let target;
|
|
93
|
-
let createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
|
|
94
|
-
rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
|
|
95
93
|
try {
|
|
94
|
+
let createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
|
|
95
|
+
rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
|
|
96
96
|
fsSync.fchmodSync(nativeFd, 0o600);
|
|
97
97
|
createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
|
|
98
98
|
await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, createdIdentity);
|
|
99
|
-
const identity =
|
|
99
|
+
const identity = fsSync.lstatSync(params.targetPath, { bigint: true });
|
|
100
100
|
target = await fs.open(params.targetPath, sourceOpenFlags());
|
|
101
|
-
const opened =
|
|
101
|
+
const opened = fsSync.fstatSync(target.fd, { bigint: true });
|
|
102
102
|
if (identity.isSymbolicLink() ||
|
|
103
103
|
!identity.isFile() ||
|
|
104
104
|
!sameFileIdentity(createdIdentity, identity) ||
|
|
@@ -106,8 +106,9 @@ async function copyPinnedSource(params) {
|
|
|
106
106
|
throw new FsSafeError("path-mismatch", "native publication target changed after copy");
|
|
107
107
|
}
|
|
108
108
|
const hashed = await hashFileHandle(target, params.native);
|
|
109
|
-
|
|
109
|
+
const completedFd = nativeFd;
|
|
110
110
|
nativeFd = undefined;
|
|
111
|
+
fsSync.closeSync(completedFd);
|
|
111
112
|
return {
|
|
112
113
|
handle: target,
|
|
113
114
|
exactIdentity: opened,
|
|
@@ -126,11 +127,11 @@ async function copyPinnedSource(params) {
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
const target = await fs.open(params.targetPath, "wx+", 0o600);
|
|
129
|
-
const createdIdentity = await target.stat({ bigint: true });
|
|
130
|
-
rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
|
|
131
130
|
try {
|
|
131
|
+
const createdIdentity = fsSync.fstatSync(target.fd, { bigint: true });
|
|
132
|
+
rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
|
|
132
133
|
await target.chmod(0o600);
|
|
133
|
-
const exactIdentity =
|
|
134
|
+
const exactIdentity = fsSync.fstatSync(target.fd, { bigint: true });
|
|
134
135
|
await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, exactIdentity);
|
|
135
136
|
const hash = createHash("sha256");
|
|
136
137
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
@@ -164,7 +165,7 @@ async function removeCreatedTargetIfUnchanged(targetPath, identity) {
|
|
|
164
165
|
return "unknown";
|
|
165
166
|
}
|
|
166
167
|
try {
|
|
167
|
-
const current =
|
|
168
|
+
const current = fsSync.lstatSync(targetPath, { bigint: true });
|
|
168
169
|
if (!current.isSymbolicLink() && sameFileIdentityForCleanup(current, identity)) {
|
|
169
170
|
await fs.rm(targetPath);
|
|
170
171
|
return "removed";
|
|
@@ -195,7 +196,7 @@ export async function publishFileExclusive(params) {
|
|
|
195
196
|
if (params.parentReceipt && path.resolve(params.parentReceipt.path) !== parentPath) {
|
|
196
197
|
throw new FsSafeError("path-mismatch", "publication parent receipt does not match target parent");
|
|
197
198
|
}
|
|
198
|
-
const sourcePathStat =
|
|
199
|
+
const sourcePathStat = fsSync.lstatSync(sourcePath);
|
|
199
200
|
if (sourcePathStat.isSymbolicLink() || !sourcePathStat.isFile()) {
|
|
200
201
|
throw new FsSafeError("not-file", "publication source must be a regular file");
|
|
201
202
|
}
|
|
@@ -212,9 +213,9 @@ export async function publishFileExclusive(params) {
|
|
|
212
213
|
parent = await pinDirectory(params.parentReceipt ?? parentPath, {
|
|
213
214
|
label: "publication parent",
|
|
214
215
|
});
|
|
215
|
-
const sourceIdentity =
|
|
216
|
-
const sourceExactIdentity =
|
|
217
|
-
const sourcePathExactIdentity =
|
|
216
|
+
const sourceIdentity = fsSync.fstatSync(source.fd);
|
|
217
|
+
const sourceExactIdentity = fsSync.fstatSync(source.fd, { bigint: true });
|
|
218
|
+
const sourcePathExactIdentity = fsSync.lstatSync(sourcePath, { bigint: true });
|
|
218
219
|
if (sourcePathExactIdentity.isSymbolicLink() ||
|
|
219
220
|
!sourcePathExactIdentity.isFile() ||
|
|
220
221
|
!sameFileIdentity(sourcePathExactIdentity, sourceExactIdentity) ||
|
|
@@ -241,14 +242,14 @@ export async function publishFileExclusive(params) {
|
|
|
241
242
|
// A failed post-rename fence must not delete the only remaining name.
|
|
242
243
|
failure.preserveTarget = true;
|
|
243
244
|
await getFsSafeTestHooks()?.afterPublishTargetCreated?.("rename-noreplace", targetPath, sourceExactIdentity);
|
|
244
|
-
const targetExactIdentity =
|
|
245
|
+
const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
|
|
245
246
|
if (targetExactIdentity.isSymbolicLink() ||
|
|
246
247
|
!targetExactIdentity.isFile() ||
|
|
247
248
|
!sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
|
|
248
249
|
throw new FsSafeError("path-mismatch", "no-replace publication target changed");
|
|
249
250
|
}
|
|
250
251
|
try {
|
|
251
|
-
|
|
252
|
+
fsSync.lstatSync(sourcePath);
|
|
252
253
|
throw new FsSafeError("path-mismatch", "no-replace publication source still exists");
|
|
253
254
|
}
|
|
254
255
|
catch (error) {
|
|
@@ -257,7 +258,7 @@ export async function publishFileExclusive(params) {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
syncFileBestEffortSync(sourceNativeParent.handle.fd);
|
|
260
|
-
const targetIdentity =
|
|
261
|
+
const targetIdentity = fsSync.lstatSync(targetPath);
|
|
261
262
|
return {
|
|
262
263
|
method: "rename-noreplace",
|
|
263
264
|
identity: targetIdentity,
|
|
@@ -278,14 +279,14 @@ export async function publishFileExclusive(params) {
|
|
|
278
279
|
}
|
|
279
280
|
rememberCreatedTarget(failure, sourceExactIdentity, "hardlink-verify");
|
|
280
281
|
await getFsSafeTestHooks()?.afterPublishTargetCreated?.("hardlink", targetPath, sourceExactIdentity);
|
|
281
|
-
const targetExactIdentity =
|
|
282
|
+
const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
|
|
282
283
|
if (targetExactIdentity.isSymbolicLink() ||
|
|
283
284
|
!targetExactIdentity.isFile() ||
|
|
284
285
|
!sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
|
|
285
286
|
throw new FsSafeError("path-mismatch", "hardlink publication target changed");
|
|
286
287
|
}
|
|
287
288
|
await assertPinnedSourceCurrent({ sourcePath, handle: source, identity: sourceExactIdentity });
|
|
288
|
-
const targetIdentity =
|
|
289
|
+
const targetIdentity = fsSync.lstatSync(targetPath);
|
|
289
290
|
return {
|
|
290
291
|
method: "hardlink",
|
|
291
292
|
identity: targetIdentity,
|
|
@@ -316,9 +317,9 @@ export async function publishFileExclusive(params) {
|
|
|
316
317
|
failure,
|
|
317
318
|
});
|
|
318
319
|
target = copied.handle;
|
|
319
|
-
targetIdentity =
|
|
320
|
-
const targetPathStat =
|
|
321
|
-
const targetPathExactStat =
|
|
320
|
+
targetIdentity = fsSync.fstatSync(target.fd);
|
|
321
|
+
const targetPathStat = fsSync.lstatSync(targetPath);
|
|
322
|
+
const targetPathExactStat = fsSync.lstatSync(targetPath, { bigint: true });
|
|
322
323
|
const copiedBack = await hashFileHandle(target, native);
|
|
323
324
|
const sourceAfter = await hashFileHandle(source, native);
|
|
324
325
|
if (targetPathStat.isSymbolicLink() ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AA0FF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AAoID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
|
+
import fs, {} from "node:fs/promises";
|
|
2
3
|
import { FsSafeError } from "./errors.js";
|
|
3
4
|
import { sameFileIdentity } from "./file-identity.js";
|
|
4
5
|
import { readOwnedCopySource, readOwnedCopySourceSync } from "./replace-file-copy-source.js";
|
|
@@ -27,11 +28,15 @@ function assertPinnedDestination(pathname, opened, dest, hardlinks) {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
async function openPinnedDestination(fsModule, dest, hardlinks) {
|
|
30
|
-
|
|
31
|
+
let preview;
|
|
32
|
+
try {
|
|
33
|
+
preview = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
31
36
|
if (notFound(error))
|
|
32
37
|
return null;
|
|
33
38
|
throw error;
|
|
34
|
-
}
|
|
39
|
+
}
|
|
35
40
|
if (!preview)
|
|
36
41
|
return null;
|
|
37
42
|
if (preview.isSymbolicLink()) {
|
|
@@ -39,8 +44,8 @@ async function openPinnedDestination(fsModule, dest, hardlinks) {
|
|
|
39
44
|
}
|
|
40
45
|
const handle = await fsModule.open(dest, OPEN_READ_WRITE_FLAGS);
|
|
41
46
|
try {
|
|
42
|
-
const opened = await handle.stat();
|
|
43
|
-
const current = await fsModule.lstat(dest);
|
|
47
|
+
const opened = fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
|
|
48
|
+
const current = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
|
|
44
49
|
assertPinnedDestination(current, opened, dest, hardlinks);
|
|
45
50
|
return { handle, stat: opened };
|
|
46
51
|
}
|
|
@@ -76,17 +81,21 @@ function openPinnedDestinationSync(fsModule, dest, hardlinks) {
|
|
|
76
81
|
export async function assertDestinationHardlinkPolicy(fsModule, dest, policy) {
|
|
77
82
|
if (policy !== "reject")
|
|
78
83
|
return;
|
|
79
|
-
|
|
84
|
+
let preview;
|
|
85
|
+
try {
|
|
86
|
+
preview = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
80
89
|
if (notFound(error))
|
|
81
|
-
return
|
|
90
|
+
return;
|
|
82
91
|
throw error;
|
|
83
|
-
}
|
|
92
|
+
}
|
|
84
93
|
if (!preview || preview.isSymbolicLink() || !preview.isFile())
|
|
85
94
|
return;
|
|
86
95
|
const handle = await fsModule.open(dest, OPEN_READ_FLAGS);
|
|
87
96
|
try {
|
|
88
|
-
const opened = await handle.stat();
|
|
89
|
-
const current = await fsModule.lstat(dest);
|
|
97
|
+
const opened = fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
|
|
98
|
+
const current = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
|
|
90
99
|
if (current.isSymbolicLink() || !current.isFile() || !sameFileIdentity(current, opened)) {
|
|
91
100
|
throw new FsSafeError("path-mismatch", `Atomic replace destination changed while opening: ${dest}`);
|
|
92
101
|
}
|
|
@@ -191,8 +200,8 @@ function restoreFailure(writeError, cleanup, restoreError) {
|
|
|
191
200
|
? `Atomic copy fallback failed; original destination restored: ${primary.message}`
|
|
192
201
|
: `Atomic copy fallback failed and original restoration failed: ${primary.message}`, { cause, details });
|
|
193
202
|
}
|
|
194
|
-
async function replacePinnedWithRestore(handle, replacement, maxRestoreBytes, replacementMode) {
|
|
195
|
-
const originalMode = (await handle.stat()).mode;
|
|
203
|
+
async function replacePinnedWithRestore(fsModule, handle, replacement, maxRestoreBytes, replacementMode) {
|
|
204
|
+
const originalMode = (fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat()).mode;
|
|
196
205
|
const original = await readBounded(handle, maxRestoreBytes);
|
|
197
206
|
try {
|
|
198
207
|
await writeAll(handle, replacement);
|
|
@@ -250,15 +259,19 @@ export async function copyFallbackReplace(params) {
|
|
|
250
259
|
const pinned = await openPinnedDestination(params.fsModule, params.dest, params.destinationHardlinks);
|
|
251
260
|
if (pinned) {
|
|
252
261
|
destHandle = pinned.handle;
|
|
253
|
-
await replacePinnedWithRestore(destHandle, replacement, params.maxRestoreBytes, source.mode);
|
|
262
|
+
await replacePinnedWithRestore(params.fsModule, destHandle, replacement, params.maxRestoreBytes, source.mode);
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
265
|
if (!destHandle) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
266
|
+
let destStat = null;
|
|
267
|
+
try {
|
|
268
|
+
destStat = params.fsModule === fs
|
|
269
|
+
? syncFs.lstatSync(params.dest) : await params.fsModule.lstat(params.dest);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (!notFound(error))
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
262
275
|
if (destStat?.isSymbolicLink()) {
|
|
263
276
|
throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${params.dest}`);
|
|
264
277
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAOnD,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AACvF,KAAK,oBAAoB,GAAG,IAAI,CAC9B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAClE,CAAC;AA0DF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAOnD,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AACvF,KAAK,oBAAoB,GAAG,IAAI,CAC9B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAClE,CAAC;AA0DF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BjD;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8BxC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
|
+
import fs, {} from "node:fs/promises";
|
|
2
3
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
5
|
import { sameFileIdentity } from "./file-identity.js";
|
|
@@ -57,15 +58,19 @@ function readAllSync(fsModule, fd) {
|
|
|
57
58
|
return Buffer.concat(chunks, position);
|
|
58
59
|
}
|
|
59
60
|
export async function readOwnedCopySource(params) {
|
|
60
|
-
assertSourcePreview(
|
|
61
|
+
assertSourcePreview(params.fsModule === fs
|
|
62
|
+
? syncFs.lstatSync(params.src) : await params.fsModule.lstat(params.src), params.src);
|
|
61
63
|
const handle = await openSource(params.fsModule, params.src);
|
|
62
64
|
try {
|
|
63
65
|
if (params.expectedIdentity) {
|
|
64
|
-
const exact = await inspectFileIdentity(() =>
|
|
65
|
-
|
|
66
|
+
const exact = await inspectFileIdentity(() => params.fsModule === fs
|
|
67
|
+
? syncFs.fstatSync(handle.fd, { bigint: true }) : handle.stat({ bigint: true }), params.expectedIdentity);
|
|
68
|
+
await inspectFileIdentity(() => params.fsModule === fs
|
|
69
|
+
? syncFs.lstatSync(params.src, { bigint: true }) : params.fsModule.lstat(params.src, { bigint: true }), exact);
|
|
66
70
|
}
|
|
67
|
-
const opened = await handle.stat();
|
|
68
|
-
const current =
|
|
71
|
+
const opened = params.fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
|
|
72
|
+
const current = params.fsModule === fs
|
|
73
|
+
? syncFs.lstatSync(params.src) : await params.fsModule.lstat(params.src);
|
|
69
74
|
if (!opened.isFile() || current.isSymbolicLink() || !sameFileIdentity(opened, current)) {
|
|
70
75
|
throw new FsSafeError("path-mismatch", `Copy fallback source changed while opening: ${params.src}`);
|
|
71
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,
|
|
1
|
+
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAkDN;IAED,WAAW,IAAI,IAAI,CAGlB;IAED,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,IAAI,CAkCP;CACF"}
|