fix: git gets plain Windows paths; the FIFO test is Unix-only (P11c)
std::fs::canonicalize on Windows returns \\?\C:\..., which git refuses
("not a git repository"): every argument handed to git now has the
extended-length prefix taken off (root::without_extended_prefix, with a
unit test that runs everywhere; \\?\UNC\ becomes \\). The access-record
test's fixture is a FIFO in the log's place, which Windows has no
counterpart for; the refusal path is the same code on every system, so
the test and its helper are Unix-only. main (8f0e5f08) merged in.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
fa7c2805a6
commit
9f57269def
3 files changed
+45
-3
No files matched your search
@@ -1159,7 +1159,9 @@ fn logged(tag: &str) -> (BooksServer, TestDir) {
|
||||
}
|
||||
|
||||
/// Books whose access log can't be written: its file is a FIFO no one
|
||||
/// reads, so every record fails (as on a full disk).
|
||||
/// reads, so every record fails (as on a full disk). Unix only: there is
|
||||
/// no FIFO in a file's place on Windows.
|
||||
#[cfg(unix)]
|
||||
fn unwritable(tag: &str) -> (BooksServer, TestDir) {
|
||||
let books = unit_dir(tag);
|
||||
let store = store(&books);
|
||||
@@ -1430,7 +1432,9 @@ fn a_call_the_books_close_or_stop_under_is_refused_as_not_run() {
|
||||
/// access.rs INVARIANT 3: a record that can't be written stops the
|
||||
/// books, and the answer says so; a call or read refused as not run
|
||||
/// (closing) keeps its not-run code, so the bridge still sends it
|
||||
/// again; anything else is an error in words.
|
||||
/// again; anything else is an error in words. Unix only: its fixture is a
|
||||
/// FIFO (the refusal path itself is the same on every system).
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn an_access_record_that_cant_be_written_stops_the_books_and_says_so() {
|
||||
rt().block_on(async {
|
||||
|
||||
@@ -223,7 +223,13 @@ impl Git {
|
||||
fn run(&self, what: &str, args: &[&std::ffi::OsStr]) -> Result<String, BackupError> {
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
let mut c = self.command();
|
||||
c.args(args);
|
||||
// A path made canonical on Windows carries `\\?\`, which git refuses.
|
||||
c.args(args.iter().map(|a| match a.to_str() {
|
||||
Some(text) if text.contains(r"\\?\") => {
|
||||
std::ffi::OsString::from(crate::root::without_extended_prefix(text))
|
||||
}
|
||||
_ => (*a).to_os_string(),
|
||||
}));
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
@@ -314,6 +314,23 @@ pub fn private_book_folder(book_dir: &Path, name: &str) -> Result<PathBuf, RootE
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// `text` with Windows' extended-length prefix taken off the paths in it:
|
||||
/// `\\?\C:\dir` becomes `C:\dir` and `\\?\UNC\server\share` becomes
|
||||
/// `\\server\share`. `std::fs::canonicalize` returns that form on Windows,
|
||||
/// and programs such as git refuse it. Text without the prefix is
|
||||
/// returned as it is, on every system (so the rule is tested everywhere).
|
||||
pub fn without_extended_prefix(text: &str) -> String {
|
||||
text.replace(r"\\?\UNC\", r"\\").replace(r"\\?\", "")
|
||||
}
|
||||
|
||||
/// [`without_extended_prefix`] for a path (the same path on Unix).
|
||||
pub fn plain_path(path: &Path) -> PathBuf {
|
||||
match path.to_str() {
|
||||
Some(text) if text.contains(r"\\?\") => PathBuf::from(without_extended_prefix(text)),
|
||||
_ => path.to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The longest socket path used as it is: a Unix socket's address holds
|
||||
/// 104 bytes on macOS and 108 on Linux, the terminating zero included.
|
||||
const SOCKET_PATH_MAX: usize = 100;
|
||||
@@ -2264,6 +2281,21 @@ fn copy_flat_private(from: &Path, to: &Path) -> Result<u64, RootError> {
|
||||
mod unit {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn the_extended_length_prefix_is_taken_off_paths() {
|
||||
assert_eq!(without_extended_prefix(r"\\?\C:\a\b"), r"C:\a\b");
|
||||
assert_eq!(
|
||||
without_extended_prefix(r"--git-dir=\\?\C:\a\archive.git"),
|
||||
r"--git-dir=C:\a\archive.git"
|
||||
);
|
||||
assert_eq!(
|
||||
without_extended_prefix(r"\\?\UNC\server\share\x"),
|
||||
r"\\server\share\x"
|
||||
);
|
||||
assert_eq!(without_extended_prefix("/usr/bin/git"), "/usr/bin/git");
|
||||
assert_eq!(plain_path(Path::new("/a/b")), Path::new("/a/b"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_buffer_grows_while_the_lookup_says_too_small() {
|
||||
// Too small twice, then found: 16, 32, 64 KiB tried, in order.
|
||||
|
||||
Reference in new issue
Block a user