59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
"use strict";
|
|
|
|
const fs = require("node:fs");
|
|
const fsp = require("node:fs/promises");
|
|
const { spawnSync } = require("node:child_process");
|
|
|
|
const WINDOWS_DIRECTORY_FLUSH = [
|
|
"$ErrorActionPreference='Stop'",
|
|
"$source='using System; using System.Runtime.InteropServices; public static class AasDirectoryFlush { [DllImport(\"kernel32.dll\", CharSet=CharSet.Unicode, SetLastError=true)] public static extern IntPtr CreateFileW(string n, uint a, uint s, IntPtr p, uint c, uint f, IntPtr t); [DllImport(\"kernel32.dll\", SetLastError=true)] public static extern bool FlushFileBuffers(IntPtr h); [DllImport(\"kernel32.dll\", SetLastError=true)] public static extern bool CloseHandle(IntPtr h); }'",
|
|
"Add-Type -TypeDefinition $source",
|
|
// FlushFileBuffers requires a handle opened with GENERIC_WRITE. Keep all
|
|
// sharing flags so the durability probe does not become an exclusivity lock.
|
|
"$h=[AasDirectoryFlush]::CreateFileW($args[0],0x40000000,7,[IntPtr]::Zero,3,0x02000000,[IntPtr]::Zero)",
|
|
"if($h -eq [IntPtr](-1)){throw 'CreateFileW failed'}",
|
|
"try{if(-not [AasDirectoryFlush]::FlushFileBuffers($h)){throw 'FlushFileBuffers failed'}}finally{[void][AasDirectoryFlush]::CloseHandle($h)}",
|
|
].join(";");
|
|
|
|
function durabilityError(cause) {
|
|
const error = new Error("AAS_DURABILITY_CAPABILITY_UNAVAILABLE", { cause });
|
|
error.code = "AAS_DURABILITY_CAPABILITY_UNAVAILABLE";
|
|
error.category = "filesystem";
|
|
error.details = {};
|
|
return error;
|
|
}
|
|
|
|
function flushWindowsDirectory(directoryPath, cause) {
|
|
if (process.platform !== "win32") throw cause;
|
|
const result = spawnSync("powershell.exe", [
|
|
"-NoProfile", "-NonInteractive", "-Command", WINDOWS_DIRECTORY_FLUSH, directoryPath,
|
|
], { encoding: "utf8", windowsHide: true, timeout: 15000, maxBuffer: 64 * 1024 });
|
|
if (result.status !== 0 || result.error) throw durabilityError(result.error || cause);
|
|
}
|
|
|
|
function fsyncDirectorySync(directoryPath) {
|
|
let descriptor;
|
|
try {
|
|
descriptor = fs.openSync(directoryPath, fs.constants.O_RDONLY);
|
|
fs.fsyncSync(descriptor);
|
|
} catch (error) {
|
|
flushWindowsDirectory(directoryPath, error);
|
|
} finally {
|
|
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
}
|
|
}
|
|
|
|
async function fsyncDirectoryAsync(directoryPath) {
|
|
let handle;
|
|
try {
|
|
handle = await fsp.open(directoryPath, "r");
|
|
await handle.sync();
|
|
} catch (error) {
|
|
flushWindowsDirectory(directoryPath, error);
|
|
} finally {
|
|
if (handle) await handle.close();
|
|
}
|
|
}
|
|
|
|
module.exports = { fsyncDirectoryAsync, fsyncDirectorySync };
|