Windows step 1 and 2 of D-62. Where Unix keeps the books' private folder at mode 0700 and refuses one others can open, Windows now makes the same promise with a DACL: `winsec::set_owner_only` gives the folder (and, inherited, everything made in it) one protected allow entry for this account's SID; `winsec::is_private` reads the effective DACL and owner back and answers false for anything else (a missing DACL, an entry for Everyone), so `Root::open` refuses a loosened root and `Root::setup` makes it private again, exactly as the 0700 checks do. Every question the system cannot answer is "not private". The root itself comes from the shell's known folders (`SHGetKnownFolderPath`: the profile, and LocalAppData wherever it was redirected), never from environment variables the process that started us chose. `rename_new` (the move that must never go over what is there, renameat2 RENAME_NOREPLACE on Linux) is `MoveFileExW` without the replace flag on Windows, written through. File locking sat behind `libc::flock` in ops.rs alone (the rest already used std's File::try_lock, which is LockFileEx on Windows). It now goes through `lock.rs`, which uses only std, so there is no second unsafe copy of either call. New dependency: windows-sys 0.61 behind cfg(windows), the raw bindings only (no runtime), already in the lockfile through other crates; the smallest Microsoft crate that covers SIDs, DACLs, the known-folder API, pipe process ids and MoveFileExW. mise.toml adds the Windows target to the pinned toolchain so `cargo check --target x86_64-pc-windows-msvc` type-checks this arm on Linux. CHOICE (P11c): nothing above the root is checked on Windows (the Unix rule about folders above it being swappable has no Windows authority; the profile's folders belong to the account and the Administrators). Tests: tests/windows_root.rs (Windows job only), the cross-platform `a_viewer_s_presence_is_a_shared_lock_others_can_see` in ops.rs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
62 lines
1.7 KiB
TOML
62 lines
1.7 KiB
TOML
[package]
|
|
name = "ledger-store"
|
|
version = "0.1.0"
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
description = "Durable persistence for wanderbooks: a hash-chained JSONL journal as the source of truth, content-addressed blob store, and a disposable SQLite projection."
|
|
|
|
[dependencies]
|
|
ledger-core = { path = "../ledger-core" }
|
|
# The one reader (plan P6 step 1, D-53, D-61: its own crate): the writer journals a file's
|
|
# text at attach time and on `wanderbooks books extract`, so the extractor
|
|
# must be callable here rather than only from the server. It is pure and
|
|
# deterministic (no I/O, no network, no time source).
|
|
ledger-reader = { path = "../ledger-reader" }
|
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
chrono = "0.4"
|
|
rust_decimal = "1"
|
|
thiserror = "2"
|
|
sha2 = "0.10"
|
|
hex = "0.4"
|
|
getrandom = { version = "0.2", features = ["std"] }
|
|
subtle = "2"
|
|
argon2 = "0.6"
|
|
|
|
[target.'cfg(unix)'.dependencies]
|
|
libc = "0.2"
|
|
|
|
# Owner-only DACLs, the account's SID, the known-folder API and
|
|
# MoveFileExW (P11c, D-62). The raw bindings, no runtime: the smallest
|
|
# Microsoft crate that covers them, already in the lockfile.
|
|
[target.'cfg(windows)'.dependencies]
|
|
windows-sys = { version = "0.61", features = [
|
|
"Win32_Foundation",
|
|
"Win32_Security",
|
|
"Win32_Security_Authorization",
|
|
"Win32_Storage_FileSystem",
|
|
"Win32_System_Com",
|
|
"Win32_System_Pipes",
|
|
"Win32_System_SystemServices",
|
|
"Win32_System_Threading",
|
|
"Win32_UI_Shell",
|
|
] }
|
|
|
|
[dev-dependencies]
|
|
tempfile = "3"
|
|
proptest = "1"
|
|
rust_decimal = "1"
|
|
criterion = "0.8.2"
|
|
|
|
[target.'cfg(unix)'.dev-dependencies]
|
|
libc = "0.2"
|
|
|
|
[[bench]]
|
|
name = "store"
|
|
harness = false
|
|
|
|
[lints]
|
|
workspace = true
|