mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(windows): admit volume roots with existing write handles
This commit is contained in:
@@ -67,7 +67,10 @@ std::unique_ptr<File> openFile(const std::wstring& requested, DWORD access, bool
|
||||
result->requestedPath = requested;
|
||||
const auto& path = result->path;
|
||||
require(directory || path.size() > 3, "Target is not a regular file", requested, ERROR_DIRECTORY);
|
||||
result->handles.push_back(openComponent(path.substr(0, 3), 0, true));
|
||||
// The volume root cannot be replaced or deleted. Match the later root
|
||||
// reopen's write sharing here so a root writer cannot block admission.
|
||||
result->handles.push_back(openComponent(path.substr(0, 3), 0, true,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE));
|
||||
wchar_t filesystem[64]{};
|
||||
DWORD flags = 0;
|
||||
require(GetVolumeInformationByHandleW(result->handles.front().get(), nullptr, 0, nullptr, nullptr,
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn } from "node:child_process";
|
||||
import { once } from "node:events";
|
||||
import { createInterface } from "node:readline";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const withDeadline = async (promise, milliseconds, description) => {
|
||||
let timer;
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise((_, reject) => {
|
||||
timer = setTimeout(
|
||||
() =>
|
||||
reject(new Error(`${description} exceeded ${milliseconds} ms.`)),
|
||||
milliseconds,
|
||||
);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
|
||||
export async function verifyVolumeRootAppendLease({
|
||||
fixturePath,
|
||||
volumeRoot,
|
||||
verifyAdmission,
|
||||
}) {
|
||||
const lease = spawn(
|
||||
resolve(fixturePath),
|
||||
["volume-root-append-lease", volumeRoot],
|
||||
{ stdio: ["pipe", "pipe", "pipe"], windowsHide: true },
|
||||
);
|
||||
const lines = createInterface({ input: lease.stdout });
|
||||
let leaseStderr = "";
|
||||
lease.stderr.setEncoding("utf8");
|
||||
lease.stderr.on("data", (chunk) => {
|
||||
if (leaseStderr.length < 8192) leaseStderr += chunk;
|
||||
});
|
||||
|
||||
const closed = once(lease, "close").then(([code, signal]) => ({
|
||||
code,
|
||||
signal,
|
||||
}));
|
||||
const leaseLine = once(lines, "line").then(([line]) => ({ line }));
|
||||
const leaseError = once(lease, "error").then(([error]) => {
|
||||
throw error;
|
||||
});
|
||||
let verificationFailure;
|
||||
try {
|
||||
const ready = await withDeadline(
|
||||
Promise.race([leaseLine, closed, leaseError]),
|
||||
10_000,
|
||||
"Volume-root append lease readiness",
|
||||
);
|
||||
assert.equal(
|
||||
"line" in ready,
|
||||
true,
|
||||
`Volume-root append lease exited before readiness (code ${ready.code}, signal ${ready.signal}): ${leaseStderr}`,
|
||||
);
|
||||
const observation = JSON.parse(ready.line);
|
||||
assert.equal(
|
||||
observation.writeAccessGranted,
|
||||
true,
|
||||
`Windows test token cannot hold FILE_ADD_SUBDIRECTORY on ${volumeRoot} (Win32 ${observation.win32Code}).`,
|
||||
);
|
||||
await verifyAdmission();
|
||||
} catch (error) {
|
||||
verificationFailure = error;
|
||||
}
|
||||
|
||||
lines.close();
|
||||
if (
|
||||
lease.exitCode === null &&
|
||||
lease.signalCode === null &&
|
||||
!lease.stdin.destroyed
|
||||
)
|
||||
lease.stdin.end("x");
|
||||
|
||||
let releaseFailure;
|
||||
try {
|
||||
const result = await withDeadline(
|
||||
closed,
|
||||
10_000,
|
||||
"Volume-root append lease release",
|
||||
).catch(async (error) => {
|
||||
lease.kill();
|
||||
try {
|
||||
return await withDeadline(
|
||||
closed,
|
||||
5_000,
|
||||
"Volume-root append lease termination",
|
||||
);
|
||||
} catch (terminationError) {
|
||||
throw new AggregateError(
|
||||
[error, terminationError],
|
||||
`Volume-root append lease could not be released: ${leaseStderr}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
assert.equal(
|
||||
result.code,
|
||||
0,
|
||||
`Volume-root append lease exited with code ${result.code}, signal ${result.signal}: ${leaseStderr}`,
|
||||
);
|
||||
} catch (error) {
|
||||
releaseFailure = error;
|
||||
}
|
||||
|
||||
if (verificationFailure !== undefined && releaseFailure !== undefined)
|
||||
throw new AggregateError(
|
||||
[verificationFailure, releaseFailure],
|
||||
"Volume-root append lease verification and cleanup both failed.",
|
||||
);
|
||||
if (verificationFailure !== undefined) throw verificationFailure;
|
||||
if (releaseFailure !== undefined) throw releaseFailure;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { dirname, join, parse, resolve } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { verifyWindowsProtocolInput } from "./lib/windows-protocol-input.mjs";
|
||||
import { verifyWindowsJobMemoryLimit } from "./lib/windows-job-memory-limit.mjs";
|
||||
import { verifyVolumeRootAppendLease } from "./lib/windows-volume-root-append-lease.mjs";
|
||||
|
||||
if (process.platform !== "win32")
|
||||
throw new Error(
|
||||
@@ -251,6 +252,17 @@ process.stdout.write(JSON.stringify({executable:basename(process.execPath),abiVe
|
||||
}
|
||||
const unrelatedFile = join(workspace, "unrelated-file");
|
||||
await writeFile(unrelatedFile, "preserve unrelated writes");
|
||||
if (process.argv[3] !== undefined) {
|
||||
await verifyVolumeRootAppendLease({
|
||||
fixturePath: process.argv[3],
|
||||
volumeRoot: parse(source).root,
|
||||
verifyAdmission: () => {
|
||||
const rootGuard = native.call("open", [source]);
|
||||
native.call("close", [rootGuard.handle]);
|
||||
},
|
||||
});
|
||||
report.controls.volumeRootAdmissionWithAppendHandle = true;
|
||||
}
|
||||
const admitted = native.call("open", [source]);
|
||||
try {
|
||||
await assert.rejects(
|
||||
|
||||
+17
@@ -97,6 +97,23 @@ int wmain(int count, wchar_t** args) {
|
||||
? "true" : "false", observed);
|
||||
return 0;
|
||||
}
|
||||
if (count == 3 && std::wstring(args[1]) == L"volume-root-append-lease") {
|
||||
// A real directory append right participates in Windows' write-share
|
||||
// check. The lease performs no mutation; it stays open while the native
|
||||
// admission probe requests the same volume root for directory listing.
|
||||
HANDLE root = CreateFileW(args[2], FILE_ADD_SUBDIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
if (root == INVALID_HANDLE_VALUE) {
|
||||
std::printf("{\"writeAccessGranted\":false,\"win32Code\":%lu}\n", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
std::puts("{\"writeAccessGranted\":true}");
|
||||
std::fflush(stdout);
|
||||
const int release = std::getchar();
|
||||
CloseHandle(root);
|
||||
return release == 'x' || release == EOF ? 0 : 10;
|
||||
}
|
||||
if (count > 1 && std::wstring(args[1]) == L"breakaway") {
|
||||
wchar_t executable[32768]; GetModuleFileNameW(nullptr, executable, 32768);
|
||||
std::wstring line = L"\"" + std::wstring(executable) + L"\" exit";
|
||||
|
||||
Reference in New Issue
Block a user