mirror of
https://github.com/herdrdev/herdr.git
synced 2026-11-06 00:06:18 +00:00
@@ -3,11 +3,11 @@ use std::process::Command;
|
||||
|
||||
use super::{ClipboardImage, ForegroundJob, Signal};
|
||||
|
||||
pub(crate) fn server_lifecycle_identity(path: &std::path::Path) -> std::io::Result<Vec<u8>> {
|
||||
Ok(std::path::absolute(path)?
|
||||
.as_os_str()
|
||||
.as_encoded_bytes()
|
||||
.to_vec())
|
||||
pub(crate) fn server_lifecycle_identity(
|
||||
path: &std::path::Path,
|
||||
) -> std::io::Result<(Vec<u8>, std::path::PathBuf)> {
|
||||
let path = std::path::absolute(path)?;
|
||||
Ok((path.as_os_str().as_encoded_bytes().to_vec(), path))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -493,9 +493,38 @@ pub(crate) fn create_remote_ssh_config_file(path: &Path) -> std::io::Result<std:
|
||||
.open(path)
|
||||
}
|
||||
|
||||
pub(crate) fn server_lifecycle_identity(path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
pub(crate) fn server_lifecycle_identity(path: &Path) -> std::io::Result<(Vec<u8>, PathBuf)> {
|
||||
let path = std::path::absolute(path)?;
|
||||
Ok(path.as_os_str().as_encoded_bytes().to_vec())
|
||||
let name = path.file_name().ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"invalid server endpoint path",
|
||||
)
|
||||
})?;
|
||||
let mut resolved = PathBuf::new();
|
||||
for component in path.parent().into_iter().flat_map(Path::components) {
|
||||
if component == std::path::Component::ParentDir {
|
||||
resolved.pop();
|
||||
continue;
|
||||
}
|
||||
resolved.push(component.as_os_str());
|
||||
match resolved.canonicalize() {
|
||||
Ok(path) => resolved = path,
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
||||
match std::fs::symlink_metadata(&resolved) {
|
||||
Ok(metadata) if metadata.file_type().is_symlink() => return Err(error),
|
||||
Err(error) if error.kind() != std::io::ErrorKind::NotFound => {
|
||||
return Err(error)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
// Keep the literal socket name stable after shutdown unlinks it.
|
||||
resolved.push(name);
|
||||
Ok((resolved.as_os_str().as_encoded_bytes().to_vec(), resolved))
|
||||
}
|
||||
|
||||
pub(crate) fn create_remote_private_dir(path: &Path) -> std::io::Result<()> {
|
||||
|
||||
@@ -3466,7 +3466,9 @@ impl Drop for InputSourceRestore {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn server_lifecycle_identity(path: &std::path::Path) -> std::io::Result<Vec<u8>> {
|
||||
pub(crate) fn server_lifecycle_identity(
|
||||
path: &std::path::Path,
|
||||
) -> std::io::Result<(Vec<u8>, std::path::PathBuf)> {
|
||||
use std::os::windows::ffi::OsStrExt as _;
|
||||
use windows_sys::Win32::Globalization::{
|
||||
LCMapStringEx, LCMAP_UPPERCASE, LOCALE_NAME_INVARIANT,
|
||||
@@ -3507,7 +3509,10 @@ pub(crate) fn server_lifecycle_identity(path: &std::path::Path) -> std::io::Resu
|
||||
if written == 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
Ok(normalized.into_iter().flat_map(u16::to_le_bytes).collect())
|
||||
Ok((
|
||||
normalized.into_iter().flat_map(u16::to_le_bytes).collect(),
|
||||
std::path::absolute(path)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+39
-13
@@ -26,9 +26,8 @@ pub(super) fn receipt_path(api_path: &Path) -> io::Result<PathBuf> {
|
||||
}
|
||||
|
||||
fn sidecar_path(api_path: &Path, suffix: &str) -> io::Result<PathBuf> {
|
||||
let identity = crate::platform::server_lifecycle_identity(api_path)?;
|
||||
let (identity, absolute) = crate::platform::server_lifecycle_identity(api_path)?;
|
||||
let key = format!("{:x}", Sha256::digest(identity));
|
||||
let absolute = std::path::absolute(api_path)?;
|
||||
let parent = absolute.parent().ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::InvalidInput, "invalid server endpoint path")
|
||||
})?;
|
||||
@@ -185,26 +184,36 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
));
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::fs::create_dir_all(base.join("hop")).unwrap();
|
||||
std::os::unix::fs::symlink(&base, base.join("link")).unwrap();
|
||||
}
|
||||
for path in [
|
||||
base.join("server.sock"),
|
||||
base.join("sessions/named/server.sock"),
|
||||
] {
|
||||
#[cfg(windows)]
|
||||
let alias = PathBuf::from(path.to_string_lossy().to_uppercase());
|
||||
let aliases = vec![PathBuf::from(path.to_string_lossy().to_uppercase())];
|
||||
#[cfg(not(windows))]
|
||||
let alias = path
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(".")
|
||||
.join(path.file_name().unwrap());
|
||||
let aliases = vec![
|
||||
path.parent()
|
||||
.unwrap()
|
||||
.join(".")
|
||||
.join(path.file_name().unwrap()),
|
||||
base.join("link/hop/..")
|
||||
.join(path.strip_prefix(&base).unwrap()),
|
||||
];
|
||||
let mut guard = lock(&path).unwrap();
|
||||
guard.commit(false).unwrap();
|
||||
assert_eq!(
|
||||
lock(&alias).err().unwrap().kind(),
|
||||
io::ErrorKind::WouldBlock
|
||||
);
|
||||
for alias in &aliases {
|
||||
assert_eq!(lock(alias).err().unwrap().kind(), io::ErrorKind::WouldBlock);
|
||||
assert_eq!(read(alias).unwrap().epoch, guard.receipt.epoch);
|
||||
#[cfg(unix)]
|
||||
assert_eq!(receipt_path(alias).unwrap(), receipt_path(&path).unwrap());
|
||||
}
|
||||
let waiter = std::thread::spawn({
|
||||
let alias = alias.clone();
|
||||
let alias = aliases.last().unwrap().clone();
|
||||
move || lock_for_start(&alias).unwrap()
|
||||
});
|
||||
guard.commit(true).unwrap();
|
||||
@@ -213,6 +222,14 @@ mod tests {
|
||||
assert!(admitted.receipt.stopped);
|
||||
assert!(admitted.require_epoch(1).is_err());
|
||||
drop(admitted);
|
||||
if path.parent().unwrap().file_name().unwrap() == "named" {
|
||||
let receipt = receipt_path(&path).unwrap();
|
||||
assert!(!path.parent().unwrap().exists());
|
||||
std::fs::create_dir(path.parent().unwrap()).unwrap();
|
||||
assert_eq!(receipt_path(&path).unwrap(), receipt);
|
||||
std::fs::remove_dir(path.parent().unwrap()).unwrap();
|
||||
assert_eq!(receipt_path(&path).unwrap(), receipt);
|
||||
}
|
||||
let starts = std::thread::scope(|scope| {
|
||||
let attempt = || {
|
||||
let mut guard = lock_for_start(&path).unwrap();
|
||||
@@ -225,6 +242,15 @@ mod tests {
|
||||
assert_eq!(starts.iter().filter(|result| result.is_ok()).count(), 1);
|
||||
remove_test_receipt(&path);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::os::unix::fs::symlink(base.join("absent"), base.join("dangling")).unwrap();
|
||||
std::os::unix::fs::symlink("loop", base.join("loop")).unwrap();
|
||||
for directory in ["dangling", "loop"] {
|
||||
assert!(lock(&base.join(directory).join("server.sock")).is_err());
|
||||
}
|
||||
assert!(!base.join("absent").exists());
|
||||
}
|
||||
std::fs::remove_dir_all(base).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user