use crate::{ArtifactError, ArtifactStore, TempScan}; pub(crate) struct NamePage { pub(crate) names: Vec, /// Opaque `telldir` position immediately after the last consumed entry. /// It is meaningful only for the same opened directory inode. pub(crate) continuation: Option, } pub(crate) struct NameEntry { pub(crate) name: String, /// Directory position immediately before this entry. Resuming here /// guarantees a candidate omitted by a result limit is reconsidered. pub(crate) cookie_before: i64, } pub(crate) fn valid_temp_name(name: &str) -> bool { let Some(rest) = name.strip_prefix(ArtifactStore::temp_prefix()) else { return false; }; let mut fields = rest.split('-'); let (Some(nonce), Some(pid), Some(sequence), None) = (fields.next(), fields.next(), fields.next(), fields.next()) else { return false; }; nonce.len() == 32 && nonce .bytes() .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) && !pid.is_empty() && pid.bytes().all(|byte| byte.is_ascii_digit()) && !sequence.is_empty() && sequence.bytes().all(|byte| byte.is_ascii_digit()) } pub(crate) fn list_names_after( parent: i32, remaining: &mut usize, report: &mut TempScan, continuation: Option, ) -> Result { let dot = std::ffi::CString::new(".").map_err(|_| ArtifactError::Storage)?; let raw = unsafe { libc::openat( parent, dot.as_ptr(), libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC, ) }; if raw < 0 { return Err(ArtifactError::Storage); } let directory = unsafe { libc::fdopendir(raw) }; if directory.is_null() { unsafe { libc::close(raw); } return Err(ArtifactError::Storage); } let mut names = Vec::new(); if let Some(cookie) = continuation { unsafe { libc::seekdir(directory, cookie as libc::c_long); } } loop { if *remaining == 0 { let continuation = unsafe { libc::telldir(directory) }; unsafe { libc::closedir(directory); } return Ok(NamePage { names, continuation: (continuation >= 0).then_some(continuation as i64), }); } unsafe { *libc::__errno_location() = 0; } let cookie_before = unsafe { libc::telldir(directory) }; if cookie_before < 0 { unsafe { libc::closedir(directory); } return Err(ArtifactError::Storage); } let entry = unsafe { libc::readdir(directory) }; if entry.is_null() { if unsafe { *libc::__errno_location() } != 0 { unsafe { libc::closedir(directory); } return Err(ArtifactError::Storage); } unsafe { libc::closedir(directory); } return Ok(NamePage { names, continuation: None, }); } let name = unsafe { std::ffi::CStr::from_ptr((*entry).d_name.as_ptr()) }.to_bytes(); if name == b"." || name == b".." { continue; } report.scanned += 1; *remaining -= 1; if let Ok(name) = std::str::from_utf8(name) { names.push(NameEntry { name: name.to_owned(), cookie_before: cookie_before as i64, }); } } }