feat(format): follow soft links; explicit errors for external links and external raw data
- Path resolution follows soft links in both old-style (symbol table, cache
type 2) and new-style (compact and dense Link message) groups: absolute and
relative targets, links to groups, links through links, with a depth limit
so a link cycle is NestingDepthExceeded rather than a hang. A dangling link
reports the target it could not find. Previously every soft link was
PathNotFound.
- An external link is FormatError::ExternalLinkUnsupported { filename,
object_path } instead of a misleading PathNotFound.
- Message 0x0007 (External Data Files) is now a known MessageType, and a
dataset carrying it is FormatError::ExternalDataFilesUnsupported. Such a
dataset has no data address in this file, so it would otherwise be read as
"never written" and answered with fill values — wrong data, no error.
- Dense link iteration is shared between hard-link listing and the new
symbolic-link lookup; entry listing behaviour is unchanged.
- h5py interop test for both libver settings.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
12847c6c66
commit
e38c8133bc
@@ -60,6 +60,54 @@ pub fn resolve_v1_group_entries(
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
/// Symbol table cache type for a soft link: the scratch pad's first four bytes
|
||||
/// are the local-heap offset of the link's target path, and the entry's object
|
||||
/// header address is undefined.
|
||||
const CACHE_TYPE_SOFT_LINK: u32 = 2;
|
||||
|
||||
/// The target path of the soft link called `name` in a v1 group, if any.
|
||||
pub fn find_v1_soft_link(
|
||||
file_data: &[u8],
|
||||
sym_table_msg: &SymbolTableMessage,
|
||||
name: &str,
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<Option<String>, FormatError> {
|
||||
let heap = LocalHeap::parse(
|
||||
file_data,
|
||||
sym_table_msg.local_heap_address as usize,
|
||||
offset_size,
|
||||
length_size,
|
||||
)?;
|
||||
let snod_addrs = collect_symbol_table_nodes(
|
||||
file_data,
|
||||
sym_table_msg.btree_address,
|
||||
offset_size,
|
||||
length_size,
|
||||
)?;
|
||||
for snod_addr in snod_addrs {
|
||||
let snod = SymbolTableNode::parse(file_data, snod_addr as usize, offset_size)?;
|
||||
for entry in &snod.entries {
|
||||
if entry.cache_type != CACHE_TYPE_SOFT_LINK {
|
||||
continue;
|
||||
}
|
||||
if heap.read_string(file_data, entry.link_name_offset)? != name {
|
||||
continue;
|
||||
}
|
||||
let value_offset = u32::from_le_bytes([
|
||||
entry.scratch_pad[0],
|
||||
entry.scratch_pad[1],
|
||||
entry.scratch_pad[2],
|
||||
entry.scratch_pad[3],
|
||||
]);
|
||||
return heap
|
||||
.read_string(file_data, u64::from(value_offset))
|
||||
.map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Extract the SymbolTableMessage from an object header's messages.
|
||||
fn find_symbol_table_message(
|
||||
obj_header: &ObjectHeader,
|
||||
|
||||
Reference in New Issue
Block a user