Skip to content

Commit

Permalink
Remove trim_start_matches targeting '.'
Browse files Browse the repository at this point in the history
Use a custom method for trimming '/' or "./" that doesn't change ".foo"
into "foo".

Fixes #1036

Signed-off-by: J Robert Ray <jrray@jrray.org>
  • Loading branch information
jrray committed Aug 14, 2024
1 parent afddb87 commit 38e5292
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 13 deletions.
6 changes: 2 additions & 4 deletions crates/spfs-vfs/src/winfsp/mount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,10 @@ impl Mount {
return None;
};

const TRIM_START: &[char] = &['/', '.'];
const TRIM_END: &[char] = &['/'];
let path = str_path.replace('\\', "/");
let path = path
.trim_start_matches(TRIM_START)
.trim_end_matches(TRIM_END);
let path =
spfs::tracking::Manifest::trim_leading_slash(path.as_str()).trim_end_matches(TRIM_END);
let mut entry = self
.inodes
.get(&ROOT_INODE)
Expand Down
29 changes: 20 additions & 9 deletions crates/spfs/src/tracking/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,8 @@ impl<T> Manifest<T> {

/// Get an entry in this manifest given its filepath.
pub fn get_path<P: AsRef<str>>(&self, path: P) -> Option<&Entry<T>> {
const TRIM_START: &[char] = &['/', '.'];
const TRIM_END: &[char] = &['/'];
let path = path
.as_ref()
.trim_start_matches(TRIM_START)
.trim_end_matches(TRIM_END);
let path = Self::trim_leading_slash(path.as_ref()).trim_end_matches(TRIM_END);
let mut entry = &self.root;
if path.is_empty() {
return Some(entry);
Expand Down Expand Up @@ -228,8 +224,7 @@ where
/// file mode, but can and should be replaced by a new entry in the
/// case where this is not desired.
pub fn mkdirs<P: AsRef<str>>(&mut self, path: P) -> MkResult<&mut Entry<T>> {
const TRIM_PAT: &[char] = &['/', '.'];
let path = path.as_ref().trim_start_matches(TRIM_PAT);
let path = Self::trim_leading_slash(path.as_ref());
if path.is_empty() {
return Err(MkError::AlreadyExists(path.into()));
}
Expand Down Expand Up @@ -262,15 +257,31 @@ where
}

impl<T> Manifest<T> {
/// Remove any leading '/' or elements at the front of the path that are
/// redundant, like "/./".
#[inline]
pub fn trim_leading_slash(path: &str) -> &str {
match *path.as_bytes() {
[b'.', b'/', ref rest @ ..] => {
// Safety: we know that the rest of the path is valid utf-8
Self::trim_leading_slash(unsafe { std::str::from_utf8_unchecked(rest) })
}
[b'/', ref rest @ ..] => {
// Safety: we know that the rest of the path is valid utf-8
Self::trim_leading_slash(unsafe { std::str::from_utf8_unchecked(rest) })
}
_ => path,
}
}

pub fn mknod<P: AsRef<str>>(
&mut self,
path: P,
new_entry: Entry<T>,
) -> MkResult<&mut Entry<T>> {
use relative_path::Component;
const TRIM_PAT: &[char] = &['/', '.'];

let path = path.as_ref().trim_start_matches(TRIM_PAT);
let path = Self::trim_leading_slash(path.as_ref());
if path.is_empty() {
return Err(MkError::AlreadyExists(path.into()));
}
Expand Down
75 changes: 75 additions & 0 deletions crates/spk-cli/cmd-build/src/cmd_build_test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,3 +653,78 @@ build:
.await
.unwrap();
}

/// A package may contain files/directories with a leading dot
#[rstest]
#[tokio::test]
async fn test_dot_files_are_collected(tmpdir: tempfile::TempDir) {
let rt = spfs_runtime().await;

build_package!(
tmpdir,
"dot.spk.yaml",
br#"
api: v0/package
pkg: dot/1.0.0
build:
script:
- touch /spfs/no_dot
- touch /spfs/.dot
- mkdir /spfs/dot
- touch /spfs/dot/.dot
- mkdir /spfs/.dot2
- touch /spfs/.dot2/dot
- touch /spfs/.dot2/.dot
- ln -s .dot2 /spfs/.dot3
"#,
);

let build = rt
.tmprepo
.list_package_builds(&version_ident!("dot/1.0.0"))
.await
.unwrap()
.into_iter()
.find(|b| !b.is_source())
.unwrap();

let digest = *rt
.tmprepo
.read_components(&build)
.await
.unwrap()
.get(&Component::Run)
.unwrap();

let spk_storage::RepositoryHandle::SPFS(repo) = &*rt.tmprepo else {
panic!("Expected SPFS repo");
};

let layer = repo.read_layer(digest).await.unwrap();

let manifest = repo
.read_manifest(
*layer
.manifest()
.expect("Layer should have a manifest in this test"),
)
.await
.unwrap()
.to_tracking_manifest();

for path in &[
"/no_dot",
"/.dot",
"/dot/.dot",
"/.dot2",
"/.dot2/dot",
"/.dot2/.dot",
"/.dot3",
] {
let entry = manifest.get_path(path);
assert!(
entry.is_some(),
"should capture file/directory with leading dot: {path}"
);
}
}

0 comments on commit 38e5292

Please sign in to comment.