mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-16 15:21:24 +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",
|
||||
"sha-1",
|
||||
"strum",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"thread_local",
|
||||
"tikv-jemallocator",
|
||||
|
|
@ -3195,6 +3196,19 @@ dependencies = [
|
|||
"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]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.2"
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ nix = { version = "0.29", features = ["resource", "time"] }
|
|||
assert_cmd = "2.0.16"
|
||||
insta = { version = "1.42.2", features = ["filters", "json", "redactions"] }
|
||||
predicates = "3.1.3"
|
||||
tempfile = "3.19.1"
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
|
|
|||
111
src/utils.rs
111
src/utils.rs
|
|
@ -2,6 +2,8 @@ use std::{
|
|||
borrow::Cow,
|
||||
cmp, fmt,
|
||||
fmt::Write,
|
||||
io,
|
||||
path::{Component, Path, PathBuf},
|
||||
str::FromStr,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
|
@ -14,6 +16,7 @@ use ruma::{
|
|||
api::client::error::ErrorKind, canonical_json::try_from_json_map,
|
||||
CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri,
|
||||
};
|
||||
use tokio::fs;
|
||||
|
||||
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)]
|
||||
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]
|
||||
fn test_truncate_str() {
|
||||
|
|
@ -412,4 +473,52 @@ mod tests {
|
|||
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