Files
crank/crates/crank-artifacts/src/temp_scan.rs
T
bsodfather bc03c33387
CI / Rust Checks (push) Failing after 4m6s
CI / UI Checks (push) Has been skipped
CI / Frontend E2E (push) Has been skipped
CI / Community Image Smoke (push) Has been skipped
CI / Deploy (push) Has been skipped
fix(openapi): harden story 2.1 production lifecycle
2026-08-29 00:48:22 +03:00

118 lines
3.6 KiB
Rust

use crate::{ArtifactError, ArtifactStore, TempScan};
pub(crate) struct NamePage {
pub(crate) names: Vec<NameEntry>,
/// Opaque `telldir` position immediately after the last consumed entry.
/// It is meaningful only for the same opened directory inode.
pub(crate) continuation: Option<i64>,
}
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<i64>,
) -> Result<NamePage, ArtifactError> {
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,
});
}
}
}