From 7fb272d190d33f6e15e0528bd8a62ef6b583039f Mon Sep 17 00:00:00 2001 From: "Brian L. Troutwine" Date: Wed, 16 Oct 2024 16:05:08 -0700 Subject: [PATCH] fix errors, able to tail -F Signed-off-by: Brian L. Troutwine --- lading/src/bin/logrotate_fs.rs | 71 +++++++++++++++++--------- lading/src/generator/file_gen/model.rs | 40 +++++++++++---- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/lading/src/bin/logrotate_fs.rs b/lading/src/bin/logrotate_fs.rs index 045f1a3601..6c87319cbd 100644 --- a/lading/src/bin/logrotate_fs.rs +++ b/lading/src/bin/logrotate_fs.rs @@ -93,10 +93,16 @@ impl LogrotateFS { #[tracing::instrument(skip(self))] fn getattr_helper(&mut self, tick: model::Tick, inode: usize) -> Option { + let nlink = self.state.nlink(inode) as u32; + self.state.getattr(tick, inode).map(|attr| FileAttr { ino: attr.inode as u64, size: attr.size, blocks: (attr.size + 511) / 512, + // TODO these times should reflect those in the model, will need to + // be translated from the tick to systemtime. Implies we'll need to + // adjust up from start_time, knowing that a tick is one second + // wide. atime: UNIX_EPOCH, mtime: UNIX_EPOCH, ctime: UNIX_EPOCH, @@ -110,7 +116,7 @@ impl LogrotateFS { } else { 0o644 }, - nlink: 1, + nlink, uid: unsafe { libc::getuid() }, gid: unsafe { libc::getgid() }, rdev: 0, @@ -137,9 +143,14 @@ impl Filesystem for LogrotateFS { let name_str = name.to_str().unwrap_or(""); if let Some(ino) = self.state.lookup(tick, parent as usize, name_str) { if let Some(attr) = self.getattr_helper(tick, ino) { + info!("lookup: returning attr for inode {}: {:?}", ino, attr); reply.entry(&TTL, &attr, 0); return; + } else { + error!("lookup: getattr_helper returned None for inode {}", ino); } + } else { + error!("lookup: state.lookup returned None for name {}", name_str); } reply.error(ENOENT); } @@ -191,44 +202,56 @@ impl Filesystem for LogrotateFS { let tick = self.get_current_tick(); self.state.advance_time(tick); - if offset != 0 { - reply.ok(); - return; - } - let root_inode = self.state.root_inode(); - if let Some(entries) = self.state.readdir(ino as usize) { - let mut index = 1; - // TODO fix - let _ = reply.add(ino, index, fuser::FileType::Directory, "."); - index += 1; - if ino != root_inode as u64 { - let parent_ino = self - .state - .get_parent_inode(ino as usize) - .expect("inode must have parent"); - // TODO fix - let _ = reply.add(parent_ino as u64, index, fuser::FileType::Directory, ".."); - index += 1; - } + // TODO building up a vec of entries here to handle offset really does + // suggest that the model needs to be exposed in such a way that this + // needn't be done. + let mut entries = Vec::new(); - for child_ino in entries { + // '.' and '..' + entries.push((ino, fuser::FileType::Directory, ".".to_string())); + if ino != root_inode as u64 { + let parent_ino = self + .state + .get_parent_inode(ino as usize) + .expect("inode must have parent"); + entries.push(( + parent_ino as u64, + fuser::FileType::Directory, + "..".to_string(), + )); + } + + // reaming children + if let Some(child_inodes) = self.state.readdir(ino as usize) { + for child_ino in child_inodes { if let Some(name) = self.state.get_name(*child_ino) { let file_type = self .state .get_file_type(*child_ino) .expect("inode must have file type"); - let _ = reply.add(*child_ino as u64, index, file_type, name); - index += 1; + entries.push((*child_ino as u64, file_type, name.to_string())); } else { error!("child inode has no name"); } } - reply.ok(); } else { reply.error(ENOENT); + return; + } + + let mut idx = offset as usize; + while idx < entries.len() { + let (inode, file_type, name) = &entries[idx]; + let next_offset = (idx + 1) as i64; + if reply.add(*inode, next_offset, *file_type, name) { + // Buffer is full, exit the loop + break; + } + idx += 1; } + reply.ok(); } #[tracing::instrument(skip(self, _req, reply))] diff --git a/lading/src/generator/file_gen/model.rs b/lading/src/generator/file_gen/model.rs index 03adb8d33a..66efb97130 100644 --- a/lading/src/generator/file_gen/model.rs +++ b/lading/src/generator/file_gen/model.rs @@ -68,7 +68,7 @@ impl File { self.advance_time(now); assert!(self.bytes_written >= self.bytes_read); - self.bytes_read.saturating_sub(self.bytes_read) + self.bytes_written.saturating_sub(self.bytes_read) } /// Register a read. @@ -181,18 +181,19 @@ pub enum NodeType { impl State { /// Create a new instance of `State`. - #[tracing::instrument] + #[tracing::instrument(skip(block_cache))] pub fn new(bytes_per_tick: u64, block_cache: block::Cache) -> State { - let root_inode: Inode = 0; // `/` - let logs_inode: Inode = 1; // `/logs` - let foo_log_inode: Inode = 2; // `/logs/foo.log` + let root_inode: Inode = 1; // `/` + let logs_inode: Inode = 2; // `/logs` + let foo_log_inode: Inode = 3; // `/logs/foo.log` let mut nodes = HashMap::new(); - let root_dir = Directory { + let mut root_dir = Directory { children: HashSet::new(), parent: None, }; + root_dir.children.insert(logs_inode); nodes.insert( root_inode, Node::Directory { @@ -201,14 +202,15 @@ impl State { }, ); - let logs_dir = Directory { + let mut logs_dir = Directory { children: HashSet::new(), parent: Some(root_inode), }; + logs_dir.children.insert(foo_log_inode); nodes.insert( logs_inode, Node::Directory { - name: "/logs".to_string(), + name: "logs".to_string(), dir: logs_dir, }, ); @@ -228,7 +230,7 @@ impl State { nodes.insert( foo_log_inode, Node::File { - name: "/logs/foo.log".to_string(), + name: "foo.log".to_string(), file: foo_log, }, ); @@ -267,7 +269,7 @@ impl State { pub fn lookup(&mut self, now: Tick, parent_inode: Inode, name: &str) -> Option { self.advance_time(now); - if let Some(Node::Directory { name, dir }) = self.nodes.get(&parent_inode) { + if let Some(Node::Directory { dir, .. }) = self.nodes.get(&parent_inode) { for child_inode in &dir.children { let child_node = &self .nodes @@ -391,4 +393,22 @@ impl State { pub fn root_inode(&self) -> Inode { self.root_inode } + + /// Return the number of links for the inode. + #[must_use] + pub fn nlink(&self, inode: Inode) -> usize { + if let Some(Node::Directory { dir, .. }) = self.nodes.get(&inode) { + let subdirectory_count = dir + .children + .iter() + .filter(|child_inode| { + matches!(self.nodes.get(child_inode), Some(Node::Directory { .. })) + }) + .count(); + // nlink is 2 (for "." and "..") plus the number of subdirectories + 2 + subdirectory_count + } else { + 1 + } + } }