@openclaw/fs-safe 0.5.6 → 0.6.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 +39 -0
- package/README.md +16 -10
- package/dist/advanced.d.ts +2 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-crc32.d.ts +2 -0
- package/dist/archive-crc32.d.ts.map +1 -0
- package/dist/archive-crc32.js +14 -0
- package/dist/archive-kind.js +2 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +24 -5
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +53 -6
- package/dist/archive-tar-pax.d.ts +8 -0
- package/dist/archive-tar-pax.d.ts.map +1 -0
- package/dist/archive-tar-pax.js +100 -0
- package/dist/archive-tar-runtime.d.ts +1 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-zip-admission.d.ts +7 -0
- package/dist/archive-zip-admission.d.ts.map +1 -0
- package/dist/archive-zip-admission.js +60 -0
- package/dist/archive-zip-count.d.ts +2 -0
- package/dist/archive-zip-count.d.ts.map +1 -0
- package/dist/archive-zip-count.js +140 -0
- package/dist/archive-zip-directory.d.ts +8 -0
- package/dist/archive-zip-directory.d.ts.map +1 -0
- package/dist/archive-zip-directory.js +224 -0
- package/dist/archive-zip-integrity.d.ts.map +1 -1
- package/dist/archive-zip-integrity.js +1 -14
- package/dist/archive-zip-names.d.ts +12 -0
- package/dist/archive-zip-names.d.ts.map +1 -0
- package/dist/archive-zip-names.js +99 -0
- package/dist/archive-zip-preflight.d.ts +1 -1
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +8 -145
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +32 -23
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +25 -19
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +19 -7
- package/dist/native-binding.d.ts +3 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +4 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +22 -6
- package/dist/native-pinned-write-windows.d.ts +8 -0
- package/dist/native-pinned-write-windows.d.ts.map +1 -0
- package/dist/native-pinned-write-windows.js +92 -0
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +136 -127
- package/dist/native-staged-file.d.ts +24 -0
- package/dist/native-staged-file.d.ts.map +1 -0
- package/dist/native-staged-file.js +337 -0
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +4 -4
- package/dist/opened-realpath.d.ts +2 -0
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +12 -7
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -1
- package/dist/permission-exec.d.ts +19 -0
- package/dist/permission-exec.d.ts.map +1 -1
- package/dist/permission-exec.js +57 -11
- package/dist/permissions-public.d.ts +1 -1
- package/dist/permissions-public.d.ts.map +1 -1
- package/dist/permissions-windows.d.ts +3 -0
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +16 -5
- package/dist/permissions.d.ts +5 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/pinned-write.d.ts +6 -0
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +19 -29
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +3 -2
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +78 -72
- package/dist/root-paths.d.ts +11 -14
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +36 -27
- package/dist/root-write-verification.d.ts +11 -0
- package/dist/root-write-verification.d.ts.map +1 -0
- package/dist/root-write-verification.js +91 -0
- package/dist/secret-file.d.ts +1 -6
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +43 -117
- package/dist/secret-read-async.d.ts +1 -1
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +45 -67
- package/dist/secret-read-policy.d.ts +13 -0
- package/dist/secret-read-policy.d.ts.map +1 -0
- package/dist/secret-read-policy.js +28 -0
- package/dist/secret.d.ts +2 -1
- package/dist/secret.d.ts.map +1 -1
- package/dist/secret.js +2 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +25 -16
- package/dist/staged-directory.d.ts +16 -0
- package/dist/staged-directory.d.ts.map +1 -0
- package/dist/staged-directory.js +60 -0
- package/dist/staged-file-types.d.ts +56 -0
- package/dist/staged-file-types.d.ts.map +1 -0
- package/dist/staged-file-types.js +1 -0
- package/dist/staged-file.d.ts +10 -0
- package/dist/staged-file.d.ts.map +1 -0
- package/dist/staged-file.js +15 -0
- package/dist/strict-file-identity.d.ts +6 -0
- package/dist/strict-file-identity.d.ts.map +1 -0
- package/dist/strict-file-identity.js +48 -0
- package/dist/windows-owner.d.ts +3 -0
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +10 -2
- package/docs/advanced.md +11 -1
- package/docs/archive.md +62 -15
- package/docs/config.md +7 -0
- package/docs/contributing.md +45 -7
- package/docs/durability.md +17 -4
- package/docs/errors.md +16 -1
- package/docs/index.md +3 -1
- package/docs/install.md +15 -7
- package/docs/migrating-to-0.5.md +7 -7
- package/docs/migrating-to-0.6.md +43 -0
- package/docs/native-helper.md +15 -5
- package/docs/native.md +40 -11
- package/docs/path-scope.md +28 -2
- package/docs/permissions.md +13 -2
- package/docs/root.md +1 -1
- package/docs/secret-file.md +8 -0
- package/docs/secure-file.md +17 -1
- package/docs/security-model.md +15 -1
- package/docs/sidecar-lock.md +11 -1
- package/docs/staged-file.md +178 -0
- package/docs/testing.md +1 -1
- package/docs/writing.md +19 -0
- package/package.json +15 -8
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
import { getNativeBinding } from "./native.js";
|
|
3
|
-
import { executePermissionCommand } from "./permission-exec.js";
|
|
3
|
+
import { executePermissionCommand, formatPermissionErrorDetail, getPermissionCommandFailure, } from "./permission-exec.js";
|
|
4
4
|
import { normalizeLowercaseStringOrEmpty } from "./string-coerce.js";
|
|
5
5
|
import { resolveWindowsSystemCommand } from "./windows-command.js";
|
|
6
6
|
import { inspectWindowsOwner, resolveWindowsCurrentUserSid, resolveWindowsPrincipalSids, } from "./windows-owner.js";
|
|
@@ -90,7 +90,7 @@ export async function inspectWindowsPermissions(params) {
|
|
|
90
90
|
});
|
|
91
91
|
if (owner.error) {
|
|
92
92
|
const error = `Windows owner inspection failed: ${owner.error}`;
|
|
93
|
-
return { ...unverified, ownerError: owner.error, error };
|
|
93
|
+
return { ...unverified, ownerError: owner.error, error, errorDetail: owner.errorDetail, errorCause: owner.errorCause };
|
|
94
94
|
}
|
|
95
95
|
const acl = await inspectWindowsAcl(params.targetPath, {
|
|
96
96
|
env: params.opts?.env,
|
|
@@ -105,7 +105,7 @@ export async function inspectWindowsPermissions(params) {
|
|
|
105
105
|
...(owner.error ? { ownerError: owner.error } : {}),
|
|
106
106
|
};
|
|
107
107
|
if (!acl.ok)
|
|
108
|
-
return { ...unverified, ...ownerFields, error: acl.error };
|
|
108
|
+
return { ...unverified, ...ownerFields, error: acl.error, errorDetail: acl.errorDetail, errorCause: acl.errorCause };
|
|
109
109
|
return {
|
|
110
110
|
ok: true,
|
|
111
111
|
isSymlink: params.stat.isSymlink,
|
|
@@ -231,7 +231,13 @@ export function summarizeWindowsAcl(entries, env) {
|
|
|
231
231
|
return { trusted, untrustedWorld, untrustedGroup };
|
|
232
232
|
}
|
|
233
233
|
export async function inspectWindowsAcl(targetPath, opts) {
|
|
234
|
-
|
|
234
|
+
let command = "";
|
|
235
|
+
let startedAt = performance.now();
|
|
236
|
+
const exec = async (file, args) => {
|
|
237
|
+
command = file;
|
|
238
|
+
startedAt = performance.now();
|
|
239
|
+
return await (opts?.exec ?? defaultPermissionExec)(file, args);
|
|
240
|
+
};
|
|
235
241
|
try {
|
|
236
242
|
if (opts?.principalTranslationFailed)
|
|
237
243
|
throw new Error("Windows ACL principal SID translation failed");
|
|
@@ -260,7 +266,12 @@ export async function inspectWindowsAcl(targetPath, opts) {
|
|
|
260
266
|
return { ok: true, entries, trusted, untrustedWorld, untrustedGroup };
|
|
261
267
|
}
|
|
262
268
|
catch (err) {
|
|
263
|
-
return {
|
|
269
|
+
return {
|
|
270
|
+
ok: false, entries: [], trusted: [], untrustedWorld: [], untrustedGroup: [],
|
|
271
|
+
error: formatPermissionErrorDetail(String(err)),
|
|
272
|
+
errorDetail: getPermissionCommandFailure(err, command, performance.now() - startedAt),
|
|
273
|
+
errorCause: err,
|
|
274
|
+
};
|
|
264
275
|
}
|
|
265
276
|
}
|
|
266
277
|
export function formatWindowsAclSummary(summary) {
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { PermissionCommandFailure } from "./permission-exec.js";
|
|
1
2
|
import { type PermissionExec } from "./permissions-windows.js";
|
|
3
|
+
export type { PermissionCommandFailure } from "./permission-exec.js";
|
|
2
4
|
export { createIcaclsResetCommand, formatIcaclsResetCommand, formatWindowsAclSummary, inspectWindowsAcl, parseIcaclsOutput, resolveWindowsUserPrincipal, summarizeWindowsAcl, } from "./permissions-windows.js";
|
|
3
5
|
export type { IcaclsResetCommandOptions, PermissionExec, WindowsAclEntry, WindowsAclSummary, WindowsUserInfoProvider, } from "./permissions-windows.js";
|
|
4
6
|
export type PermissionCheck = {
|
|
@@ -20,6 +22,9 @@ export type PermissionCheck = {
|
|
|
20
22
|
ownerError?: string;
|
|
21
23
|
aclSummary?: string;
|
|
22
24
|
error?: string;
|
|
25
|
+
errorDetail?: PermissionCommandFailure;
|
|
26
|
+
/** Original inspection failure, retained separately from serializable diagnostics. */
|
|
27
|
+
errorCause?: unknown;
|
|
23
28
|
};
|
|
24
29
|
export type PermissionCheckOptions = {
|
|
25
30
|
platform?: NodeJS.Platform;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5C,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5C,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,wBAAwB,CAAC;IACvC,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAsB1E;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,sBAAsB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAuD1B;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAKzF;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,MAAM,CAYT;AASD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEvD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE5D"}
|
package/dist/pinned-write.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Readable } from "node:stream";
|
|
2
|
+
import { type AsyncDirectoryGuard } from "./directory-guard.js";
|
|
2
3
|
import type { FileIdentityStat } from "./file-identity.js";
|
|
3
4
|
export type PinnedWriteInput = {
|
|
4
5
|
kind: "buffer";
|
|
@@ -10,6 +11,10 @@ export type PinnedWriteInput = {
|
|
|
10
11
|
};
|
|
11
12
|
export type RenameIdentityMismatchPolicy = "throw" | "verify-content";
|
|
12
13
|
export type RenameIdentityPolicy = "strict" | "verify-content-with-lock";
|
|
14
|
+
export type PublishedWriteIdentity = Readonly<{
|
|
15
|
+
dev: bigint;
|
|
16
|
+
ino: bigint;
|
|
17
|
+
}>;
|
|
13
18
|
export type PinnedWriteParams = {
|
|
14
19
|
rootPath: string;
|
|
15
20
|
relativeParentPath: string;
|
|
@@ -21,6 +26,7 @@ export type PinnedWriteParams = {
|
|
|
21
26
|
input: PinnedWriteInput;
|
|
22
27
|
rootIdentity?: FileIdentityStat;
|
|
23
28
|
onRenameIdentityMismatch?: RenameIdentityMismatchPolicy;
|
|
29
|
+
verifyPublished?: (fd: number, identity: PublishedWriteIdentity, parentGuard: AsyncDirectoryGuard) => Promise<void>;
|
|
24
30
|
};
|
|
25
31
|
export declare function runPinnedWriteHelper(params: PinnedWriteParams): Promise<FileIdentityStat>;
|
|
26
32
|
export declare function runPinnedWriteWithRenamePolicy(params: PinnedWriteParams & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAkE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhI,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAU3D,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAgEzC,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,gBAAgB,CAAC;AAEtE,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,0BAA0B,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,CAAC;IACxB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,wBAAwB,CAAC,EAAE,4BAA4B,CAAC;IAExD,eAAe,CAAC,EAAE,CAChB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,sBAAsB,EAChC,WAAW,EAAE,mBAAmB,KAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAe/F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,iBAAiB,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,GACA,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
|
package/dist/pinned-write.js
CHANGED
|
@@ -115,7 +115,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
115
115
|
: await createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
116
116
|
const targetPath = path.join(parentPath, params.basename);
|
|
117
117
|
if (params.overwrite === false) {
|
|
118
|
-
|
|
118
|
+
const handle = await withAsyncDirectoryGuards([parentGuard], async () => await fs.open(targetPath, fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL, params.mode), {
|
|
119
119
|
onPostGuardFailure: async (openedHandle) => {
|
|
120
120
|
// The parent failed verification, so targetPath may now resolve
|
|
121
121
|
// somewhere else. Close the fd, but do not clean up by path.
|
|
@@ -124,6 +124,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
124
124
|
});
|
|
125
125
|
let created = true;
|
|
126
126
|
try {
|
|
127
|
+
const verificationIdentity = await handle.stat({ bigint: true });
|
|
127
128
|
await handle.chmod(params.mode);
|
|
128
129
|
if (params.input.kind === "buffer") {
|
|
129
130
|
assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
@@ -139,9 +140,10 @@ async function runPinnedWriteFallback(params) {
|
|
|
139
140
|
}
|
|
140
141
|
await syncFileBestEffort(handle);
|
|
141
142
|
const stat = await handle.stat();
|
|
142
|
-
await handle.close().catch(() => undefined);
|
|
143
143
|
await syncDirectoryBestEffort(parentPath);
|
|
144
|
+
// Publication is complete. A failed outer check must not remove its target.
|
|
144
145
|
created = false;
|
|
146
|
+
await params.verifyPublished?.(handle.fd, verificationIdentity, parentGuard);
|
|
145
147
|
return { dev: stat.dev, ino: stat.ino };
|
|
146
148
|
}
|
|
147
149
|
finally {
|
|
@@ -160,10 +162,11 @@ async function runPinnedWriteFallback(params) {
|
|
|
160
162
|
: 0);
|
|
161
163
|
let handle;
|
|
162
164
|
let tempStat;
|
|
163
|
-
let
|
|
165
|
+
let readHandle;
|
|
164
166
|
let renamed = false;
|
|
165
167
|
try {
|
|
166
168
|
handle = await fs.open(tempPath, tempFlags, params.mode);
|
|
169
|
+
let verificationIdentity = await handle.stat({ bigint: true });
|
|
167
170
|
await handle.chmod(params.mode);
|
|
168
171
|
if (params.input.kind === "buffer") {
|
|
169
172
|
assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
@@ -184,14 +187,13 @@ async function runPinnedWriteFallback(params) {
|
|
|
184
187
|
}
|
|
185
188
|
const expectedTempStat = tempStat;
|
|
186
189
|
await syncFileBestEffort(handle);
|
|
187
|
-
|
|
188
|
-
handle = undefined;
|
|
190
|
+
let verifiedIdentity = expectedTempStat;
|
|
189
191
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
190
192
|
await fs.rename(tempPath, targetPath);
|
|
191
193
|
renamed = true;
|
|
192
194
|
await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
|
|
193
195
|
await syncDirectoryBestEffort(parentPath);
|
|
194
|
-
targetStat = await fs.lstat(targetPath);
|
|
196
|
+
const targetStat = await fs.lstat(targetPath);
|
|
195
197
|
if (targetStat.isSymbolicLink()) {
|
|
196
198
|
throw new FsSafeError("path-mismatch", "fallback target changed during write");
|
|
197
199
|
}
|
|
@@ -211,38 +213,26 @@ async function runPinnedWriteFallback(params) {
|
|
|
211
213
|
(process.platform !== "win32" && "O_NOFOLLOW" in fsSync.constants
|
|
212
214
|
? fsSync.constants.O_NOFOLLOW
|
|
213
215
|
: 0);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
// Capture fd-based identity before reading — this is stable across all
|
|
219
|
-
// subsequent lookups (on FUSE and locally), unlike the lstat-based
|
|
220
|
-
// targetStat that triggered this fallback.
|
|
221
|
-
readHandleStat = await readHandle.stat();
|
|
222
|
-
actualHash = sha256Hex(await readHandle.readFile());
|
|
223
|
-
}
|
|
224
|
-
finally {
|
|
225
|
-
await readHandle.close().catch(() => undefined);
|
|
226
|
-
}
|
|
216
|
+
readHandle = await fs.open(targetPath, readFlags);
|
|
217
|
+
const readHandleStat = await readHandle.stat({ bigint: true });
|
|
218
|
+
const actualHash = sha256Hex(await readHandle.readFile());
|
|
227
219
|
if (actualHash !== expectedHash) {
|
|
228
220
|
throw new FsSafeError("path-mismatch", "fallback target changed during write");
|
|
229
221
|
}
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
|
|
222
|
+
// The content-verified destination, not the old temp inode, is now pinned.
|
|
223
|
+
verificationIdentity = readHandleStat;
|
|
224
|
+
// Preserve the helper's legacy numeric return facts, not the private proof.
|
|
225
|
+
verifiedIdentity = { dev: Number(readHandleStat.dev), ino: Number(readHandleStat.ino) };
|
|
234
226
|
}
|
|
235
227
|
});
|
|
228
|
+
await params.verifyPublished?.((readHandle ?? handle).fd, verificationIdentity, parentGuard);
|
|
229
|
+
return { dev: verifiedIdentity.dev, ino: verifiedIdentity.ino };
|
|
236
230
|
}
|
|
237
|
-
|
|
231
|
+
finally {
|
|
232
|
+
await readHandle?.close().catch(() => undefined);
|
|
238
233
|
await handle?.close().catch(() => undefined);
|
|
239
234
|
if (!renamed) {
|
|
240
235
|
await fs.rm(tempPath, { force: true }).catch(() => undefined);
|
|
241
236
|
}
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
if (!targetStat) {
|
|
245
|
-
throw new FsSafeError("path-mismatch", "fallback target was not verified");
|
|
246
237
|
}
|
|
247
|
-
return { dev: targetStat.dev, ino: targetStat.ino };
|
|
248
238
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-directory.d.ts","sourceRoot":"","sources":["../src/private-directory.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"private-directory.d.ts","sourceRoot":"","sources":["../src/private-directory.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
|
@@ -3,11 +3,12 @@ import { getNativeBinding } from "./native.js";
|
|
|
3
3
|
export async function createPrivateDirectory(targetPath, options) {
|
|
4
4
|
const platform = options?.platform ?? process.platform;
|
|
5
5
|
if (platform !== "win32") {
|
|
6
|
-
throw new FsSafeError("helper-unavailable", "private-directory creation is
|
|
6
|
+
throw new FsSafeError("helper-unavailable", "private-directory creation is supported only on Windows");
|
|
7
7
|
}
|
|
8
8
|
const native = getNativeBinding();
|
|
9
9
|
if (!native) {
|
|
10
|
-
throw new FsSafeError("helper-unavailable", "private Windows directory creation requires the
|
|
10
|
+
throw new FsSafeError("helper-unavailable", "private Windows directory creation requires the matching optional native platform package; " +
|
|
11
|
+
"install @openclaw/fs-safe with optional dependencies enabled on a supported platform and use FS_SAFE_NATIVE_MODE=auto or require");
|
|
11
12
|
}
|
|
12
13
|
native.createPrivateDirectory(targetPath);
|
|
13
14
|
}
|
package/dist/root-impl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAW7D,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAyB9E,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}
|
package/dist/root-impl.js
CHANGED
|
@@ -9,7 +9,7 @@ import { sameFileIdentity, sameFileIdentityForCleanup, } from "./file-identity.j
|
|
|
9
9
|
import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
|
|
10
10
|
import { withAsyncDirectoryGuards } from "./guarded-mutation.js";
|
|
11
11
|
import { assertMutationNotDenied, mergeDenyMutationPolicies, } from "./deny-mutations.js";
|
|
12
|
-
import { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
12
|
+
import { resolveOpenedFileRealPathForFd, resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
13
13
|
import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy, } from "./pinned-write.js";
|
|
14
14
|
import { getNativeBinding } from "./native.js";
|
|
15
15
|
import { validatePinnedOperationPayload } from "./pinned-operation.js";
|
|
@@ -25,6 +25,8 @@ import { stringifyJsonDocument } from "./json-stringify.js";
|
|
|
25
25
|
import { walkRoot } from "./root-walk.js";
|
|
26
26
|
import { registerTempPathForExit } from "./temp-cleanup.js";
|
|
27
27
|
import { serializePathWrite } from "./write-queue.js";
|
|
28
|
+
import { verifyAtomicWriteResult } from "./root-write-verification.js";
|
|
29
|
+
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
28
30
|
export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
29
31
|
function logWarn(message) {
|
|
30
32
|
if (process.env.FS_SAFE_DEBUG_WARNINGS === "1") {
|
|
@@ -74,22 +76,27 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
74
76
|
assertNoUnsafeDeviceReadPath(filePath);
|
|
75
77
|
const fsSafeTestHooks = getFsSafeTestHooks();
|
|
76
78
|
let preOpenStat;
|
|
79
|
+
let observedBeforeOpen = false;
|
|
77
80
|
// Reject directories before opening so we never surface EISDIR to callers (e.g. tool
|
|
78
81
|
// results that get sent to messaging channels). See openclaw/openclaw#31186.
|
|
79
82
|
try {
|
|
80
|
-
preOpenStat = await
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
preOpenStat = await inspectFileIdentity(async () => {
|
|
84
|
+
const stat = await fs.lstat(filePath, { bigint: true });
|
|
85
|
+
observedBeforeOpen = true;
|
|
86
|
+
if (stat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
|
|
87
|
+
throw new FsSafeError("symlink", "symlink not allowed");
|
|
88
|
+
}
|
|
89
|
+
if (!stat.isFile() && !stat.isSymbolicLink()) {
|
|
90
|
+
throw new FsSafeError("not-file", "not a file");
|
|
91
|
+
}
|
|
92
|
+
return stat;
|
|
93
|
+
});
|
|
87
94
|
}
|
|
88
95
|
catch (err) {
|
|
89
|
-
if (err instanceof FsSafeError) {
|
|
96
|
+
if (err instanceof FsSafeError || observedBeforeOpen) {
|
|
90
97
|
throw err;
|
|
91
98
|
}
|
|
92
|
-
//
|
|
99
|
+
// Only an initial lookup failure falls through; a failed re-inspection aborts.
|
|
93
100
|
}
|
|
94
101
|
if (preOpenStat) {
|
|
95
102
|
await fsSafeTestHooks?.afterPreOpenLstat?.(filePath);
|
|
@@ -127,37 +134,28 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
127
134
|
if (!stat.isFile()) {
|
|
128
135
|
throw new FsSafeError("not-file", "not a file");
|
|
129
136
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
!sameFileIdentity(stat, preOpenStat)) {
|
|
133
|
-
throw new FsSafeError("path-mismatch", "path changed before open");
|
|
134
|
-
}
|
|
137
|
+
// Keep numeric Stats for the public receipt, never for identity verification.
|
|
138
|
+
const identity = await inspectFileIdentity(() => handle.stat({ bigint: true }), preOpenStat && !preOpenStat.isSymbolicLink() ? preOpenStat : undefined);
|
|
135
139
|
if (options?.hardlinks === "reject" && stat.nlink > 1) {
|
|
136
140
|
throw hardlinkedPathNotAllowedError();
|
|
137
141
|
}
|
|
138
|
-
|
|
139
|
-
const pathStat =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
const pathStat = await fs.lstat(filePath);
|
|
146
|
-
if (pathStat.isSymbolicLink()) {
|
|
142
|
+
await inspectFileIdentity(async () => {
|
|
143
|
+
const pathStat = options?.symlinks === "follow-within-root"
|
|
144
|
+
? await fs.stat(filePath, { bigint: true })
|
|
145
|
+
: await fs.lstat(filePath, { bigint: true });
|
|
146
|
+
if (pathStat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
|
|
147
147
|
throw new FsSafeError("symlink", "symlink not allowed");
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
return pathStat;
|
|
150
|
+
}, identity);
|
|
151
|
+
const realPath = await resolveOpenedFileRealPathForFd(handle.fd, identity, filePath);
|
|
152
|
+
await inspectFileIdentity(async () => {
|
|
153
|
+
const realStat = await fs.stat(realPath, { bigint: true });
|
|
154
|
+
if (options?.hardlinks === "reject" && realStat.nlink > 1n) {
|
|
155
|
+
throw hardlinkedPathNotAllowedError();
|
|
151
156
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const realStat = await fs.stat(realPath);
|
|
155
|
-
if (options?.hardlinks === "reject" && realStat.nlink > 1) {
|
|
156
|
-
throw hardlinkedPathNotAllowedError();
|
|
157
|
-
}
|
|
158
|
-
if (!sameFileIdentity(stat, realStat)) {
|
|
159
|
-
throw new FsSafeError("path-mismatch", "path mismatch");
|
|
160
|
-
}
|
|
157
|
+
return realStat;
|
|
158
|
+
}, identity);
|
|
161
159
|
return openResult({ handle, realPath, stat });
|
|
162
160
|
}
|
|
163
161
|
catch (err) {
|
|
@@ -474,26 +472,13 @@ async function writeTempFileForAtomicReplace(params) {
|
|
|
474
472
|
await tempHandle.writeFile(params.data);
|
|
475
473
|
}
|
|
476
474
|
return {
|
|
477
|
-
|
|
478
|
-
|
|
475
|
+
handle: tempHandle,
|
|
476
|
+
identity: await tempHandle.stat({ bigint: true }),
|
|
479
477
|
};
|
|
480
478
|
}
|
|
481
|
-
|
|
479
|
+
catch (error) {
|
|
482
480
|
await tempHandle.close().catch(() => { });
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
async function verifyAtomicWriteResult(params) {
|
|
486
|
-
const opened = await openVerifiedLocalFile(params.targetPath, { hardlinks: "reject" });
|
|
487
|
-
try {
|
|
488
|
-
if (!sameFileIdentity(opened.stat, params.expectedIdentity)) {
|
|
489
|
-
throw new FsSafeError("path-mismatch", "path changed during write");
|
|
490
|
-
}
|
|
491
|
-
if (!isPathInside(params.root.rootWithSep, opened.realPath)) {
|
|
492
|
-
throw outsideWorkspaceError();
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
finally {
|
|
496
|
-
await opened.handle.close().catch(() => { });
|
|
481
|
+
throw error;
|
|
497
482
|
}
|
|
498
483
|
}
|
|
499
484
|
async function resolveGuardedWritePathInRoot(root, params) {
|
|
@@ -709,9 +694,9 @@ async function writeFileInRoot(root, params) {
|
|
|
709
694
|
});
|
|
710
695
|
}
|
|
711
696
|
async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
712
|
-
let
|
|
697
|
+
let verifyingPublication = false;
|
|
713
698
|
try {
|
|
714
|
-
|
|
699
|
+
await runPinnedWriteWithRenamePolicy({
|
|
715
700
|
rootPath: pinned.rootReal,
|
|
716
701
|
relativeParentPath: pinned.relativeParentPath,
|
|
717
702
|
basename: pinned.basename,
|
|
@@ -722,9 +707,27 @@ async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
|
722
707
|
overwrite: params.overwrite,
|
|
723
708
|
input: { kind: "buffer", data: params.data, encoding: params.encoding },
|
|
724
709
|
rootIdentity: root.rootIdentity,
|
|
710
|
+
verifyPublished: async (fd, expectedIdentity, parentGuard) => {
|
|
711
|
+
verifyingPublication = true;
|
|
712
|
+
try {
|
|
713
|
+
await verifyAtomicWriteResult({
|
|
714
|
+
root,
|
|
715
|
+
targetPath: pinned.targetPath,
|
|
716
|
+
fd,
|
|
717
|
+
expectedIdentity,
|
|
718
|
+
parentGuard,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
emitWriteBoundaryWarning(`post-write verification failed: ${String(error)}`);
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
},
|
|
725
726
|
});
|
|
726
727
|
}
|
|
727
728
|
catch (error) {
|
|
729
|
+
if (verifyingPublication)
|
|
730
|
+
throw error;
|
|
728
731
|
const errorCode = error?.code;
|
|
729
732
|
if (errorCode === "file_lock_stale" || errorCode === "file_lock_timeout") {
|
|
730
733
|
throw error;
|
|
@@ -736,17 +739,6 @@ async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
|
736
739
|
}
|
|
737
740
|
throw normalizePinnedWriteError(error);
|
|
738
741
|
}
|
|
739
|
-
try {
|
|
740
|
-
await verifyAtomicWriteResult({
|
|
741
|
-
root,
|
|
742
|
-
targetPath: pinned.targetPath,
|
|
743
|
-
expectedIdentity: identity,
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
catch (err) {
|
|
747
|
-
emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
|
|
748
|
-
throw err;
|
|
749
|
-
}
|
|
750
742
|
}
|
|
751
743
|
async function copyFileInRoot(root, params) {
|
|
752
744
|
assertValidRootRelativePath(params.relativePath);
|
|
@@ -863,7 +855,7 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode,
|
|
|
863
855
|
targetPath: resolved,
|
|
864
856
|
relativeParentPath: path.posix.dirname(relativePosix) === "." ? "" : path.posix.dirname(relativePosix),
|
|
865
857
|
basename,
|
|
866
|
-
mode
|
|
858
|
+
mode,
|
|
867
859
|
};
|
|
868
860
|
}
|
|
869
861
|
async function resolvePinnedPathInRoot(root, params) {
|
|
@@ -1108,6 +1100,7 @@ async function writeFileFallback(root, params) {
|
|
|
1108
1100
|
const destinationGuard = await createAsyncDirectoryGuard(path.dirname(destinationPath));
|
|
1109
1101
|
let tempPath = null;
|
|
1110
1102
|
let unregisterTempPath = null;
|
|
1103
|
+
let writtenHandle;
|
|
1111
1104
|
try {
|
|
1112
1105
|
tempPath = buildAtomicWriteTempPath(destinationPath);
|
|
1113
1106
|
unregisterTempPath = registerTempPathForExit(tempPath);
|
|
@@ -1115,14 +1108,15 @@ async function writeFileFallback(root, params) {
|
|
|
1115
1108
|
tempPath,
|
|
1116
1109
|
data: params.data,
|
|
1117
1110
|
encoding: params.encoding,
|
|
1118
|
-
mode
|
|
1111
|
+
mode,
|
|
1119
1112
|
});
|
|
1120
|
-
|
|
1113
|
+
writtenHandle = written.handle;
|
|
1114
|
+
unregisterTempPath.setIdentity(written.identity);
|
|
1121
1115
|
const commitTempPath = tempPath;
|
|
1122
1116
|
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
1123
1117
|
await fs.rename(commitTempPath, destinationPath);
|
|
1118
|
+
tempPath = null;
|
|
1124
1119
|
});
|
|
1125
|
-
tempPath = null;
|
|
1126
1120
|
unregisterTempPath();
|
|
1127
1121
|
unregisterTempPath = null;
|
|
1128
1122
|
try {
|
|
@@ -1130,6 +1124,8 @@ async function writeFileFallback(root, params) {
|
|
|
1130
1124
|
root,
|
|
1131
1125
|
targetPath: destinationPath,
|
|
1132
1126
|
expectedIdentity: written.identity,
|
|
1127
|
+
fd: written.handle.fd,
|
|
1128
|
+
parentGuard: destinationGuard,
|
|
1133
1129
|
});
|
|
1134
1130
|
}
|
|
1135
1131
|
catch (err) {
|
|
@@ -1138,6 +1134,7 @@ async function writeFileFallback(root, params) {
|
|
|
1138
1134
|
}
|
|
1139
1135
|
}
|
|
1140
1136
|
finally {
|
|
1137
|
+
await writtenHandle?.close().catch(() => undefined);
|
|
1141
1138
|
if (tempPath) {
|
|
1142
1139
|
await fs.rm(tempPath, { force: true }).catch(() => { });
|
|
1143
1140
|
}
|
|
@@ -1155,19 +1152,22 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1155
1152
|
const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
|
|
1156
1153
|
let created = false;
|
|
1157
1154
|
let createdIdentity;
|
|
1155
|
+
let writtenHandle;
|
|
1156
|
+
let verifyingPublication = false;
|
|
1158
1157
|
try {
|
|
1159
1158
|
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
1160
1159
|
const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
1161
1160
|
created = true;
|
|
1162
1161
|
try {
|
|
1163
1162
|
createdIdentity = await handle.stat();
|
|
1163
|
+
const writtenStat = await handle.stat({ bigint: true });
|
|
1164
1164
|
if (typeof params.data === "string") {
|
|
1165
1165
|
await handle.writeFile(params.data, params.encoding ?? "utf8");
|
|
1166
1166
|
}
|
|
1167
1167
|
else {
|
|
1168
1168
|
await handle.writeFile(params.data);
|
|
1169
1169
|
}
|
|
1170
|
-
return { handle, writtenStat
|
|
1170
|
+
return { handle, writtenStat };
|
|
1171
1171
|
}
|
|
1172
1172
|
catch (error) {
|
|
1173
1173
|
await handle.close().catch(() => undefined);
|
|
@@ -1179,15 +1179,20 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1179
1179
|
await handle.close().catch(() => undefined);
|
|
1180
1180
|
},
|
|
1181
1181
|
});
|
|
1182
|
-
|
|
1182
|
+
writtenHandle = handle;
|
|
1183
|
+
created = false;
|
|
1184
|
+
verifyingPublication = true;
|
|
1183
1185
|
await verifyAtomicWriteResult({
|
|
1184
1186
|
root,
|
|
1185
1187
|
targetPath,
|
|
1186
1188
|
expectedIdentity: writtenStat,
|
|
1189
|
+
fd: handle.fd,
|
|
1190
|
+
parentGuard,
|
|
1187
1191
|
});
|
|
1188
|
-
created = false;
|
|
1189
1192
|
}
|
|
1190
1193
|
catch (err) {
|
|
1194
|
+
if (verifyingPublication)
|
|
1195
|
+
throw err;
|
|
1191
1196
|
if (hasNodeErrorCode(err, "EEXIST")) {
|
|
1192
1197
|
throw new FsSafeError("already-exists", "file already exists", {
|
|
1193
1198
|
cause: err instanceof Error ? err : undefined,
|
|
@@ -1196,6 +1201,7 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1196
1201
|
throw err;
|
|
1197
1202
|
}
|
|
1198
1203
|
finally {
|
|
1204
|
+
await writtenHandle?.close().catch(() => undefined);
|
|
1199
1205
|
if (created && createdIdentity) {
|
|
1200
1206
|
await removePathIfIdentityUnchanged(targetPath, createdIdentity).catch(() => undefined);
|
|
1201
1207
|
}
|
package/dist/root-paths.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
1
2
|
type InvalidPathResult = {
|
|
2
3
|
ok: false;
|
|
3
4
|
error: string;
|
|
4
5
|
};
|
|
6
|
+
type DirectoryResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
path: string;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
error: string;
|
|
12
|
+
diagnostic?: FsSafeError;
|
|
13
|
+
};
|
|
5
14
|
type ResolvePathsWithinRootParams = {
|
|
6
15
|
rootDir: string;
|
|
7
16
|
requestedPaths: string[];
|
|
@@ -39,13 +48,7 @@ export type PathScope = {
|
|
|
39
48
|
}>;
|
|
40
49
|
ensureDir(requestedPath: string, options?: PathScopeResolveOptions & {
|
|
41
50
|
mode?: number;
|
|
42
|
-
}): Promise<
|
|
43
|
-
ok: true;
|
|
44
|
-
path: string;
|
|
45
|
-
} | {
|
|
46
|
-
ok: false;
|
|
47
|
-
error: string;
|
|
48
|
-
}>;
|
|
51
|
+
}): Promise<DirectoryResult>;
|
|
49
52
|
};
|
|
50
53
|
export declare function resolvePathWithinRoot(params: {
|
|
51
54
|
rootDir: string;
|
|
@@ -77,13 +80,7 @@ export declare function ensureDirectoryWithinRoot(params: {
|
|
|
77
80
|
scopeLabel: string;
|
|
78
81
|
defaultDirName?: string;
|
|
79
82
|
mode?: number;
|
|
80
|
-
}): Promise<
|
|
81
|
-
ok: true;
|
|
82
|
-
path: string;
|
|
83
|
-
} | {
|
|
84
|
-
ok: false;
|
|
85
|
-
error: string;
|
|
86
|
-
}>;
|
|
83
|
+
}): Promise<DirectoryResult>;
|
|
87
84
|
export declare function resolvePathsWithinRoot(params: ResolvePathsWithinRootParams): ResolvePathsWithinRootResult;
|
|
88
85
|
export declare function resolveExistingPathsWithinRoot(params: ResolvePathsWithinRootParams): Promise<ResolvePathsWithinRootResult>;
|
|
89
86
|
export declare function resolveStrictExistingPathsWithinRoot(params: ResolvePathsWithinRootParams): Promise<ResolvePathsWithinRootResult>;
|
package/dist/root-paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"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"}
|