feat: complete WorkBuddy parity and Windows release workflow

This commit is contained in:
xiaohan.yuan committed 2026-10-18 12:45:21 +08:00
1 parent 143516b3a8
commit d5512df549
434 files changed
+54977 -1854

No files matched your search

+332
View File
@@ -0,0 +1,332 @@
#pragma once
#include <bcrypt.h>
#include <map>
#include <winternl.h>
// Host-owned transaction channel. No workspace handles cross into an AppContainer.
namespace workspace_write {
static size_t fileLimit = 8 * 1024 * 1024;
using CreateFileFn = NTSTATUS(NTAPI*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
using SetFileFn = NTSTATUS(NTAPI*)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
using ErrorFn = ULONG(NTAPI*)(NTSTATUS);
template<class Function> static Function nativeFunction(const char* name) {
const auto address = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), name);
require(address != nullptr, "Resolve native transaction API");
Function result = nullptr;
static_assert(sizeof(result) == sizeof(address)); memcpy(&result, &address, sizeof(result));
return result;
}
static bool nativeOk(NTSTATUS status) {
if (status >= 0) return true;
SetLastError(nativeFunction<ErrorFn>("RtlNtStatusToDosError")(status));
return false;
}
static HANDLE openRelative(HANDLE parent, const std::wstring& name, ACCESS_MASK access, bool directory, bool create = false) {
UNICODE_STRING text = {}; text.Buffer = const_cast<PWSTR>(name.data());
text.Length = static_cast<USHORT>(name.size() * sizeof(wchar_t)); text.MaximumLength = text.Length;
OBJECT_ATTRIBUTES attributes = {}; attributes.Length = sizeof(attributes); attributes.RootDirectory = parent;
attributes.ObjectName = &text; attributes.Attributes = 0x1040; // OBJ_CASE_INSENSITIVE | OBJ_DONT_REPARSE
IO_STATUS_BLOCK status = {}; HANDLE handle = INVALID_HANDLE_VALUE;
const ULONG options = 0x00200000 | 0x20 | (directory ? 1 : 0); // OPEN_REPARSE_POINT, synchronous, optional directory
const auto result = nativeFunction<CreateFileFn>("NtCreateFile")(&handle, access | SYNCHRONIZE, &attributes, &status, nullptr,
FILE_ATTRIBUTE_NORMAL, directory ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ, create ? 2 : 1, options, nullptr, 0);
return nativeOk(result) ? handle : INVALID_HANDLE_VALUE;
}
struct Root {
std::vector<std::unique_ptr<Handle>> held;
fs::path path;
HANDLE handle = nullptr;
std::wstring rootFinal;
std::string rootIdentity;
explicit Root(const std::wstring& input) {
if (input.size() < 3 || input.size() > 1024 || !iswalpha(input[0]) || input[1] != L':' || input[2] != L'\\') throw std::runtime_error("Workspace requires a local drive path");
path = input.substr(0, 3);
if (GetDriveTypeW(path.c_str()) != DRIVE_FIXED) throw std::runtime_error("Workspace requires a fixed local volume");
auto first = std::make_unique<Handle>(CreateFileW(path.c_str(), FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr));
require(first->value != INVALID_HANDLE_VALUE, "Pin transaction volume");
wchar_t filesystem[32] = {};
require(GetVolumeInformationByHandleW(first->value, nullptr, 0, nullptr, nullptr, nullptr, filesystem, 32) != FALSE, "Inspect transaction volume");
if (_wcsicmp(filesystem, L"NTFS") != 0) throw std::runtime_error("Workspace transaction requires NTFS");
handle = first->value; held.push_back(std::move(first));
if (input.size() > 3) for (const auto& name : workspace_read::parts(input.substr(3))) {
auto next = std::make_unique<Handle>(openRelative(handle, name, FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES, true));
require(next->value != INVALID_HANDLE_VALUE, "Pin transaction root ancestor");
workspace_read::information(next->value);
handle = next->value; held.push_back(std::move(next)); path /= name;
}
rootFinal = workspace_read::finalPath(handle); rootIdentity = workspace_read::identity(handle);
}
std::wstring relative(HANDLE target) const {
const auto actual = workspace_read::finalPath(target);
if (actual.size() <= rootFinal.size() || _wcsnicmp(actual.c_str(), rootFinal.c_str(), rootFinal.size()) != 0 || actual[rootFinal.size()] != L'\\') throw std::runtime_error("Transaction handle escaped root");
return actual.substr(rootFinal.size() + 1);
}
};
static std::string hex(const BYTE* bytes, size_t size) {
static const char* digits = "0123456789abcdef";
std::string result; result.reserve(size * 2);
for (size_t index = 0; index < size; ++index) { result += digits[bytes[index] >> 4]; result += digits[bytes[index] & 15]; }
return result;
}
static std::vector<BYTE> unhex(const std::string& text, size_t limit) {
if (text.size() % 2 || text.size() > limit * 2) throw std::runtime_error("Invalid transaction field length");
std::vector<BYTE> result; result.reserve(text.size() / 2);
auto digit = [](char ch) -> BYTE {
if (ch >= '0' && ch <= '9') return static_cast<BYTE>(ch - '0');
if (ch >= 'a' && ch <= 'f') return static_cast<BYTE>(ch - 'a' + 10);
throw std::runtime_error("Invalid transaction hex field");
};
for (size_t index = 0; index < text.size(); index += 2) result.push_back(static_cast<BYTE>((digit(text[index]) << 4) | digit(text[index + 1])));
return result;
}
static std::wstring pathField(const std::string& text) {
const auto bytes = unhex(text, 4096);
const int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<const char*>(bytes.data()), static_cast<int>(bytes.size()), nullptr, 0);
if (size <= 0 || size > 1024) throw std::runtime_error("Invalid transaction path encoding");
std::wstring result(size, 0);
require(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<const char*>(bytes.data()), static_cast<int>(bytes.size()), result.data(), size) == size, "Decode transaction path");
return result;
}
static std::wstring key(std::wstring value) {
std::transform(value.begin(), value.end(), value.begin(), [](wchar_t ch) { return static_cast<wchar_t>(towlower(ch)); });
return value;
}
static std::string hash(const std::vector<BYTE>& bytes) {
BCRYPT_ALG_HANDLE algorithm = nullptr;
if (BCryptOpenAlgorithmProvider(&algorithm, BCRYPT_SHA256_ALGORITHM, nullptr, 0) < 0) throw std::runtime_error("Open transaction SHA256");
BYTE result[32] = {};
const auto status = BCryptHash(algorithm, nullptr, 0, const_cast<BYTE*>(bytes.data()), static_cast<ULONG>(bytes.size()), result, sizeof(result));
BCryptCloseAlgorithmProvider(algorithm, 0);
if (status < 0) throw std::runtime_error("Hash transaction contents");
return hex(result, sizeof(result));
}
static std::vector<BYTE> contents(HANDLE handle) {
const auto info = workspace_read::information(handle);
const auto size = (static_cast<ULONGLONG>(info.nFileSizeHigh) << 32) | info.nFileSizeLow;
if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || size > fileLimit) throw std::runtime_error("Transaction file type or size denied");
std::vector<BYTE> bytes(static_cast<size_t>(size)); DWORD count = 0;
if (!bytes.empty()) require(ReadFile(handle, bytes.data(), static_cast<DWORD>(bytes.size()), &count, nullptr) && count == bytes.size(), "Read pinned transaction file");
return bytes;
}
static void reply(const std::string& text) {
const auto line = text + '\n'; DWORD count = 0;
require(WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), line.data(), static_cast<DWORD>(line.size()), &count, nullptr) && count == line.size(), "Write transaction response");
}
struct Session {
struct MissingParent : std::runtime_error { MissingParent() : std::runtime_error("Transaction parent missing") {} };
Root root;
std::map<std::wstring, std::unique_ptr<Handle>> directories;
std::map<std::wstring, std::string> known;
explicit Session(const std::wstring& path, const std::string& expected) : root(path) {
if (expected != "-" && expected != root.rootIdentity) throw std::runtime_error("Workspace root identity changed");
}
void validate(HANDLE handle, const std::wstring& relative, bool directory) {
for (const auto& ancestor : root.held) workspace_read::information(ancestor->value);
const auto info = workspace_read::information(handle);
if (directory != ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) throw std::runtime_error("Transaction target type changed");
if (_wcsicmp(root.relative(handle).c_str(), relative.c_str()) != 0) throw std::runtime_error("Transaction final name changed");
}
HANDLE parent(const std::vector<std::wstring>& names) {
HANDLE parentHandle = root.handle; std::wstring relative;
workspace_read::information(parentHandle);
for (size_t index = 0; index + 1 < names.size(); ++index) {
if (!relative.empty()) relative += L'\\'; relative += names[index];
const auto name = key(relative);
if (directories.count(name)) { parentHandle = directories[name]->value; workspace_read::information(parentHandle); continue; }
if (directories.size() >= 10000) throw std::runtime_error("Transaction directory pin limit");
auto handle = std::make_unique<Handle>(openRelative(parentHandle, names[index], FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES, true));
if (handle->value == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)) throw MissingParent();
require(handle->value != INVALID_HANDLE_VALUE, "Pin transaction ancestor");
validate(handle->value, relative, true);
parentHandle = handle->value;
directories.emplace(name, std::move(handle));
}
return parentHandle;
}
void unpin(const std::wstring& relative) {
const auto prefix = key(relative);
for (auto entry = directories.begin(); entry != directories.end();) {
if (entry->first == prefix || entry->first.compare(0, prefix.size() + 1, prefix + L'\\') == 0) entry = directories.erase(entry);
else ++entry;
}
}
std::string execute(const std::vector<std::string>& fields) {
if (fields.size() != 5) throw std::runtime_error("Invalid transaction frame");
const auto& operation = fields[0]; const auto relative = pathField(fields[1]);
const auto names = workspace_read::parts(relative);
const auto name = key(relative);
HANDLE directory = nullptr;
try { directory = parent(names); }
catch (const MissingParent&) {
if (operation == "cleanup") return "{\"ok\":true}";
if (operation == "read" || operation == "stat") return "{\"ok\":true,\"kind\":\"missing\"}";
throw;
}
const auto absolute = names.back();
const auto& nonce = fields[4];
if (operation == "write" || operation == "cleanup") {
if (nonce.size() != 32 || nonce.find_first_not_of("0123456789abcdef") != std::string::npos) throw std::runtime_error("Invalid transaction mutation identity");
}
const auto temporaryName = L".ycode-sandbox-" + std::wstring(nonce.begin(), nonce.end());
const auto temporary = temporaryName + L"-new.tmp";
const auto previous = temporaryName + L"-old.tmp";
if (operation == "cleanup") {
const auto separator = fields[2].find(':');
if (separator == std::string::npos) throw std::runtime_error("Invalid transaction cleanup hashes");
const std::pair<fs::path, std::string> candidates[] = {{previous, fields[2].substr(0, separator)}, {temporary, fields[2].substr(separator + 1)}};
for (const auto& candidate : candidates) {
Handle stale(openRelative(directory, candidate.first.wstring(), GENERIC_READ | DELETE, false));
if (stale.value == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)) continue;
require(stale.value != INVALID_HANDLE_VALUE, "Open transaction recovery scratch");
validate(stale.value, (fs::path(relative).parent_path() / candidate.first.filename()).wstring(), false);
if (hash(contents(stale.value)) != candidate.second) throw std::runtime_error("Transaction scratch changed externally");
FILE_DISPOSITION_INFO disposition = {TRUE};
require(SetFileInformationByHandle(stale.value, FileDispositionInfo, &disposition, sizeof(disposition)) != FALSE, "Remove transaction recovery scratch");
}
return "{\"ok\":true}";
}
if (operation == "mkdir") {
Handle created(openRelative(directory, absolute, FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES, true, true));
require(created.value != INVALID_HANDLE_VALUE, "Create pinned transaction directory");
validate(created.value, relative, true);
const auto identity = workspace_read::identity(created.value);
directories[name] = std::make_unique<Handle>(created.value); created.value = nullptr;
known.erase(name);
return "{\"ok\":true,\"identity\":\"" + identity + "\"}";
}
if (operation == "rmdir") unpin(relative);
const bool mutation = operation == "write" || operation == "unlink" || operation == "rmdir";
Handle target(openRelative(directory, absolute, GENERIC_READ | (mutation ? DELETE : 0), operation == "rmdir"));
const DWORD error = target.value == INVALID_HANDLE_VALUE ? GetLastError() : ERROR_SUCCESS;
const bool missing = error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
if (target.value == INVALID_HANDLE_VALUE && !missing) { SetLastError(error); require(false, "Open pinned transaction target"); }
if (operation == "read" || operation == "stat") {
if (missing) { known.erase(name); return "{\"ok\":true,\"kind\":\"missing\"}"; }
const bool isDirectory = (workspace_read::information(target.value).dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
validate(target.value, relative, isDirectory);
const auto identity = workspace_read::identity(target.value); known[name] = identity;
if (isDirectory && !directories.count(name)) {
if (directories.size() >= 10000) throw std::runtime_error("Transaction directory pin limit");
auto pinned = std::make_unique<Handle>(openRelative(directory, absolute, FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES, true));
require(pinned->value != INVALID_HANDLE_VALUE, "Pin inspected transaction directory");
validate(pinned->value, relative, true);
if (workspace_read::identity(pinned->value) != identity) throw std::runtime_error("Inspected transaction directory identity changed");
directories[name] = std::move(pinned);
}
if (operation == "read" && isDirectory) throw std::runtime_error("Transaction target is a directory");
std::string result = "{\"ok\":true,\"kind\":\"" + std::string(isDirectory ? "directory" : "file") + "\",\"identity\":\"" + identity + "\"";
if (operation == "read") result += ",\"data\":\"" + workspace_read::base64(contents(target.value)) + "\"";
return result + '}';
}
if (operation == "list") {
if (missing) throw std::runtime_error("Transaction directory missing");
validate(target.value, relative, true);
std::string result = "{\"ok\":true,\"entries\":["; bool first = true; size_t count = 0;
std::vector<BYTE> buffer(65536);
for (;;) {
if (!GetFileInformationByHandleEx(target.value, FileIdBothDirectoryInfo, buffer.data(), static_cast<DWORD>(buffer.size()))) {
if (GetLastError() == ERROR_NO_MORE_FILES) break;
require(false, "Enumerate transaction directory");
}
size_t offset = 0;
for (;;) {
const auto entry = reinterpret_cast<FILE_ID_BOTH_DIR_INFO*>(buffer.data() + offset);
if (entry->FileNameLength > buffer.size() - offset - offsetof(FILE_ID_BOTH_DIR_INFO, FileName)) throw std::runtime_error("Invalid transaction directory record");
const std::wstring child(entry->FileName, entry->FileNameLength / sizeof(wchar_t));
if (child != L"." && child != L"..") {
if (++count > 10000 || !workspace_read::component(child)) throw std::runtime_error("Transaction directory entry denied");
if (!first) result += ','; first = false;
result += "\"" + jsonText(child) + "\"";
}
if (entry->NextEntryOffset == 0) break;
if (entry->NextEntryOffset < sizeof(FILE_ID_BOTH_DIR_INFO) || entry->NextEntryOffset > buffer.size() - offset - sizeof(FILE_ID_BOTH_DIR_INFO)) throw std::runtime_error("Invalid transaction directory offset");
offset += entry->NextEntryOffset;
}
}
return result + "]}";
}
if (!mutation) throw std::runtime_error("Unknown transaction operation");
if (!missing) {
validate(target.value, relative, operation == "rmdir");
if (operation != "rmdir" && (workspace_read::information(target.value).dwFileAttributes & FILE_ATTRIBUTE_READONLY)) throw std::runtime_error("Transaction read-only file denied");
const auto identity = workspace_read::identity(target.value);
if (known.count(name) && known[name] != identity) throw std::runtime_error("Transaction target identity changed");
if (operation == "rmdir") {
if (fields[2] != identity) throw std::runtime_error("Transaction directory identity changed");
} else if (fields[2] != hash(contents(target.value))) throw std::runtime_error("Transaction contents changed");
} else if (operation != "write" || fields[2] != "-") throw std::runtime_error("Transaction target disappeared");
if (operation == "unlink" || operation == "rmdir") {
FILE_DISPOSITION_INFO disposition = {TRUE};
require(SetFileInformationByHandle(target.value, FileDispositionInfo, &disposition, sizeof(disposition)) != FALSE, "Delete pinned transaction target");
known.erase(name);
return "{\"ok\":true}";
}
const auto bytes = unhex(fields[3], fileLimit);
Handle output(openRelative(directory, temporary, GENERIC_WRITE | DELETE | FILE_READ_ATTRIBUTES, false, true));
require(output.value != INVALID_HANDLE_VALUE, "Create transaction replacement");
bool published = false, moved = false;
auto renameHandle = [directory](HANDLE handle, const fs::path& destination) {
const auto name = destination.wstring();
std::vector<BYTE> storage(offsetof(FILE_RENAME_INFO, FileName) + name.size() * sizeof(wchar_t));
auto rename = reinterpret_cast<FILE_RENAME_INFO*>(storage.data());
rename->ReplaceIfExists = FALSE;
rename->RootDirectory = directory; rename->FileNameLength = static_cast<DWORD>(name.size() * sizeof(wchar_t));
memcpy(rename->FileName, name.data(), rename->FileNameLength);
IO_STATUS_BLOCK status = {};
return nativeOk(nativeFunction<SetFileFn>("NtSetInformationFile")(handle, &status, rename, static_cast<ULONG>(storage.size()), static_cast<FILE_INFORMATION_CLASS>(10)));
};
try {
DWORD count = 0;
if (!bytes.empty()) require(WriteFile(output.value, bytes.data(), static_cast<DWORD>(bytes.size()), &count, nullptr) && count == bytes.size(), "Write transaction replacement");
require(FlushFileBuffers(output.value) != FALSE, "Flush transaction replacement");
validate(output.value, (fs::path(relative).parent_path() / fs::path(temporary).filename()).wstring(), false);
const auto replacementIdentity = workspace_read::identity(output.value);
// A no-overwrite handoff keeps the original handle exclusively pinned.
// A competing basename creation fails publication instead of losing data.
if (!missing) { require(renameHandle(target.value, previous), "Stage pinned transaction original"); moved = true; }
require(renameHandle(output.value, absolute), "Publish pinned transaction replacement");
published = true;
known[name] = replacementIdentity;
if (moved) {
FILE_DISPOSITION_INFO disposition = {TRUE};
require(SetFileInformationByHandle(target.value, FileDispositionInfo, &disposition, sizeof(disposition)) != FALSE, "Remove pinned transaction original");
}
} catch (...) {
FILE_DISPOSITION_INFO disposition = {TRUE};
if (!published) SetFileInformationByHandle(output.value, FileDispositionInfo, &disposition, sizeof(disposition));
if (moved && !published && !renameHandle(target.value, absolute)) throw std::runtime_error("Transaction handoff needs recovery; competing target preserved");
throw;
}
return "{\"ok\":true}";
}
};
static int run(const std::wstring& root, const std::string& expected, bool gitMetadata = false) {
if (gitMetadata) fileLimit = 128 * 1024 * 1024;
if (inContainer(GetCurrentProcess())) throw std::runtime_error("Workspace transaction is host-only");
workspace_read::Lifetime lifetime(600000);
Session session(root, expected);
reply("{\"protocol\":1,\"rootIdentity\":\"" + session.root.rootIdentity + "\"}");
std::string line; size_t requests = 0; char buffer[16384]; DWORD count = 0;
while (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, sizeof(buffer), &count, nullptr) && count) {
for (DWORD index = 0; index < count; ++index) {
if (buffer[index] != '\n') {
if (line.size() >= fileLimit * 2 + 16384) throw std::runtime_error("Transaction frame limit");
line += buffer[index]; continue;
}
if (++requests > 100000) throw std::runtime_error("Transaction request limit");
std::vector<std::string> fields; size_t start = 0;
for (;;) { const auto end = line.find('\t', start); fields.push_back(line.substr(start, end == std::string::npos ? end : end - start)); if (end == std::string::npos) break; start = end + 1; }
try { reply(session.execute(fields)); }
catch (const std::exception& error) {
std::string message;
for (char ch : std::string(error.what())) { if (ch == '\\' || ch == '"') message += '\\'; message += ch; }
reply("{\"ok\":false,\"error\":\"" + message + "\"}");
}
line.clear();
}
}
if (!line.empty()) throw std::runtime_error("Truncated transaction frame");
return 0;
}
}