mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-17 07:41:23 +01:00
add partial_canonicalize helper function
This is useful for checking for potential overlap between paths that have not been fully created yet.
This commit is contained in:
parent
c03103a142
commit
abb1b5681e
3 changed files with 125 additions and 1 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -962,6 +962,7 @@ dependencies = [
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
"strum",
|
"strum",
|
||||||
|
"tempfile",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
|
|
@ -3195,6 +3196,19 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.3.2",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ nix = { version = "0.29", features = ["resource", "time"] }
|
||||||
assert_cmd = "2.0.16"
|
assert_cmd = "2.0.16"
|
||||||
insta = { version = "1.42.2", features = ["filters", "json", "redactions"] }
|
insta = { version = "1.42.2", features = ["filters", "json", "redactions"] }
|
||||||
predicates = "3.1.3"
|
predicates = "3.1.3"
|
||||||
|
tempfile = "3.19.1"
|
||||||
|
|
||||||
[profile.dev.package.insta]
|
[profile.dev.package.insta]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
|
||||||
111
src/utils.rs
111
src/utils.rs
|
|
@ -2,6 +2,8 @@ use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp, fmt,
|
cmp, fmt,
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
|
io,
|
||||||
|
path::{Component, Path, PathBuf},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
@ -14,6 +16,7 @@ use ruma::{
|
||||||
api::client::error::ErrorKind, canonical_json::try_from_json_map,
|
api::client::error::ErrorKind, canonical_json::try_from_json_map,
|
||||||
CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri,
|
CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri,
|
||||||
};
|
};
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
|
||||||
|
|
@ -380,9 +383,67 @@ pub(crate) fn u8_slice_to_hex(slice: &[u8]) -> String {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Canonicalize a path where some components may not exist yet.
|
||||||
|
///
|
||||||
|
/// It's assumed that non-existent components will be created as
|
||||||
|
/// directories. This should match the result of [`fs::canonicalize`]
|
||||||
|
/// _after_ calling [`fs::create_dir_all`] on `path`.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) async fn partial_canonicalize(path: &Path) -> io::Result<PathBuf> {
|
||||||
|
let mut ret = std::env::current_dir()?;
|
||||||
|
|
||||||
|
let mut base_path = Cow::Borrowed(path);
|
||||||
|
let mut components = base_path.components();
|
||||||
|
|
||||||
|
while let Some(component) = components.next() {
|
||||||
|
match component {
|
||||||
|
Component::Prefix(_) | Component::RootDir => {
|
||||||
|
let component_path: &Path = component.as_ref();
|
||||||
|
component_path.clone_into(&mut ret);
|
||||||
|
}
|
||||||
|
Component::CurDir => (),
|
||||||
|
Component::ParentDir => {
|
||||||
|
ret.pop();
|
||||||
|
}
|
||||||
|
Component::Normal(p) => {
|
||||||
|
let component_path = ret.join(p);
|
||||||
|
match fs::symlink_metadata(&component_path).await {
|
||||||
|
// path is a symlink
|
||||||
|
Ok(metadata) if metadata.is_symlink() => {
|
||||||
|
let destination =
|
||||||
|
fs::read_link(&component_path).await?;
|
||||||
|
// iterate over the symlink destination components
|
||||||
|
// before continuing with the original path
|
||||||
|
base_path =
|
||||||
|
Cow::Owned(destination.join(components.as_path()));
|
||||||
|
components = base_path.components();
|
||||||
|
}
|
||||||
|
// path exists, not a symlink
|
||||||
|
Ok(_) => {
|
||||||
|
ret.push(p);
|
||||||
|
}
|
||||||
|
// path does not exist
|
||||||
|
Err(error) if error.kind() == io::ErrorKind::NotFound => {
|
||||||
|
// assume a directory will be created here
|
||||||
|
ret.push(p);
|
||||||
|
}
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::{dbg_truncate_str, u8_slice_to_hex};
|
use tempfile::TempDir;
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
use crate::utils::{
|
||||||
|
dbg_truncate_str, partial_canonicalize, u8_slice_to_hex,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_truncate_str() {
|
fn test_truncate_str() {
|
||||||
|
|
@ -412,4 +473,52 @@ mod tests {
|
||||||
4242424242424242424242424242424242424242"
|
4242424242424242424242424242424242424242"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_partial_canonicalize() {
|
||||||
|
let tmp_dir =
|
||||||
|
TempDir::with_prefix("test_partial_canonicalize").unwrap();
|
||||||
|
let path = tmp_dir.path();
|
||||||
|
|
||||||
|
fs::create_dir(&path.join("dir")).await.unwrap();
|
||||||
|
fs::symlink(path.join("dir"), path.join("absolute-link-to-dir"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
fs::symlink("./dir", path.join("relative-link-to-dir")).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(partial_canonicalize(path).await.unwrap(), path);
|
||||||
|
assert_eq!(partial_canonicalize(&path.join("./")).await.unwrap(), path);
|
||||||
|
assert_eq!(
|
||||||
|
partial_canonicalize(&path.join("dir/..")).await.unwrap(),
|
||||||
|
path
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
partial_canonicalize(&path.join("absolute-link-to-dir"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
path.join("dir")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
partial_canonicalize(&path.join("relative-link-to-dir"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
path.join("dir")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
partial_canonicalize(&path.join("absolute-link-to-dir/new-dir"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
path.join("dir/new-dir")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
partial_canonicalize(
|
||||||
|
&path.join("absolute-link-to-dir/new-dir/../..")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
path,
|
||||||
|
);
|
||||||
|
|
||||||
|
tmp_dir.close().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue