fix(format): refuse path-like group and dataset names
FileWriter writes the root group plus one level of groups; it has no way
to create intermediate groups. create_group("a/b") therefore stored a
single link literally named "a/b", which no HDF5 reader can resolve
(h5py: "component not found"). Nesting would mean restructuring the
writer's layout around a group tree, so for now finish() rejects any
group, dataset or external-link name that is empty, "." or contains '/'.
Attribute names may still contain '/'.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -63,6 +63,19 @@ fn build_paged_superblock_extension(page_size: u32) -> Result<Vec<u8>, FormatErr
|
||||
w.serialize()
|
||||
}
|
||||
|
||||
/// A group or dataset name must be one path component: not empty, not ".",
|
||||
/// and without '/'. `FileWriter` writes a root group plus one level of
|
||||
/// groups, and cannot create intermediate groups for a path.
|
||||
fn check_link_name(name: &str) -> Result<(), FormatError> {
|
||||
if name.is_empty() || name == "." || name.contains('/') {
|
||||
return Err(FormatError::SerializationError(format!(
|
||||
"invalid object name {name:?}: names must be a single path component \
|
||||
(FileWriter does not create nested groups)"
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Threshold for switching from compact (inline) to dense attribute storage.
|
||||
const DENSE_ATTR_THRESHOLD: usize = 8;
|
||||
|
||||
@@ -1137,6 +1150,19 @@ impl FileWriter {
|
||||
})
|
||||
};
|
||||
|
||||
// Every name becomes a single link in its parent group. The writer
|
||||
// has no nested groups, so a path like "a/b" would be stored as one
|
||||
// link literally named "a/b" — which no HDF5 reader can resolve.
|
||||
let root_names = self.root_datasets.iter().map(|d| d.name.as_str());
|
||||
let group_names = self.groups.iter().flat_map(|g| {
|
||||
core::iter::once(g.name.as_str())
|
||||
.chain(g.datasets.iter().map(|d| d.name.as_str()))
|
||||
.chain(g.external_links.iter().map(|l| l.0.as_str()))
|
||||
});
|
||||
for name in root_names.chain(group_names) {
|
||||
check_link_name(name)?;
|
||||
}
|
||||
|
||||
let mut all_ds: Vec<DsFlat> = Vec::new();
|
||||
let mut groups: Vec<GrpFlat> = Vec::new();
|
||||
let mut root_ds_indices: Vec<usize> = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user