feat(metatag-dirs): nested directory hierarchy via path metatag + flags
Implements the source PDF's prescribed model for directory structure: 'treat the CZYX cube as the only persistent store, with system calls that operate on CZYX records and Null-space flags rather than paths and inodes.' Nested dirs are RECONSTRUCTED from a per-record `path` metatag, so the filesystem does not need recursive inode trees. - cubecoords: add CubeHeader.path (Option<String>) + HAS_PATH flag bit (1<<8); refresh_flags() sets it. Cubestore TLV codec now serializes/deserializes the path as tag 13 (persists across checkpoint/reopen). - cubestore: scan_by_path (exact), scan_by_path_prefix (dir listing by path prefix; bare '/etc' normalized to '/etc/'), plus query_by_path/_prefix returning decoded (coord,header,body). - cubesys: `put` verb gains path=<p>; new `query-path <dir>` verb reconstructs a directory listing from metatags. - ConcurrentStore: query_by_path(_prefix) delegate to inner CubeStore. Verified live in VM: migrated /etc/passwd, /etc/network/interfaces, /usr/bin/ls at unrelated coordinates; 'query-path /etc' returns the two /etc files, '/usr/bin/ls' correctly excluded. cubestore 8/8 tests pass. Refs: OS-in-CUBE migration, query-layer prototype.
This commit is contained in:
@@ -448,6 +448,35 @@ impl Session {
|
||||
))
|
||||
}
|
||||
}
|
||||
"query-path" => {
|
||||
// Reconstruct a directory listing from path metatags (no FS
|
||||
// inode tree needed). `query-path /etc` returns every record
|
||||
// whose `path` metatag is under /etc — the PDF's "operate on
|
||||
// CZYX records and Null-space flags rather than paths/inodes".
|
||||
let dir = it
|
||||
.next()
|
||||
.ok_or_else(|| "query-path needs <dir-prefix>".to_string())?;
|
||||
let matches = store.query_by_path_prefix(dir);
|
||||
if matches.is_empty() {
|
||||
Ok(format!("query-path {dir} -> (no matches)"))
|
||||
} else {
|
||||
let mut names: Vec<String> = matches
|
||||
.iter()
|
||||
.filter_map(|c| {
|
||||
store.get_record(c).map(|(h, _)| {
|
||||
let p = h.path.as_deref().unwrap_or("");
|
||||
format!("{}:{}", c.pack_u32(), p)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
names.sort();
|
||||
Ok(format!(
|
||||
"query-path {dir} -> {} matches:\n {}",
|
||||
names.len(),
|
||||
names.join("\n ")
|
||||
))
|
||||
}
|
||||
}
|
||||
"prog" => {
|
||||
let path = it.next().ok_or_else(|| "prog needs <path>".to_string())?;
|
||||
let mut ops: Vec<Op> = Vec::new();
|
||||
@@ -964,6 +993,10 @@ impl Session {
|
||||
header.doc_type = Some(v.to_string());
|
||||
} else if let Some(v) = tok.strip_prefix("title=") {
|
||||
header.title = Some(v.to_string());
|
||||
} else if let Some(v) = tok.strip_prefix("path=") {
|
||||
// POSIX path metatag: the original filesystem path.
|
||||
// Lets scan_by_path_prefix reconstruct nesting from flags.
|
||||
header.path = Some(v.to_string());
|
||||
} else {
|
||||
return Err(format!("put: unknown option {tok}"));
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ pub fn grant_header() -> CubeHeader {
|
||||
owner_local_user: None,
|
||||
owner_remote_user: None,
|
||||
linked_records: Vec::new(),
|
||||
path: None,
|
||||
total_accesses: 0,
|
||||
total_remote_accesses: 0,
|
||||
last_access: None,
|
||||
|
||||
@@ -574,6 +574,19 @@ impl ConcurrentStore {
|
||||
out
|
||||
}
|
||||
|
||||
/// Reconstruct a directory listing from `path` metatags: every record
|
||||
/// whose `path` header field is under `dir` (the PDF's "operate on CZYX
|
||||
/// records and Null-space flags rather than paths and inodes"). Delegates
|
||||
/// to [`CubeStore::scan_by_path_prefix`].
|
||||
pub fn query_by_path_prefix(&self, dir: &str) -> Vec<Czyx> {
|
||||
self.inner.read().unwrap().scan_by_path_prefix(dir)
|
||||
}
|
||||
|
||||
/// Exact-path metatag lookup. Delegates to [`CubeStore::scan_by_path`].
|
||||
pub fn query_by_path(&self, path: &str) -> Vec<Czyx> {
|
||||
self.inner.read().unwrap().scan_by_path(path)
|
||||
}
|
||||
|
||||
/// The `owner_local_user` stamped on the record at `key`, if it has one.
|
||||
/// Used by owner enforcement (Task 6): a mutating command may only
|
||||
/// overwrite a record whose owner matches the session's identity owner.
|
||||
|
||||
Reference in New Issue
Block a user