Close prepared-lower identity and reclaim races
This commit is contained in:
parent
5458460717
commit
0dfd7767b5
1 changed files with 142 additions and 30 deletions
|
|
@ -14,7 +14,10 @@ use lazaret_core::identity::{
|
|||
use lazaret_core::records::{ArtifactState, PrepareOperation, PrepareStageState, PreparedArtifact};
|
||||
use lazaret_linux::filesystem::{ExclusiveFileLock, try_exclusive_flock};
|
||||
use lazaret_storage_catalog::{Catalog, CatalogError, CatalogRow};
|
||||
use rustix::fs::{AtFlags, Mode, OFlags, ResolveFlags, mkdirat, openat2, renameat, unlinkat};
|
||||
use rustix::fs::{
|
||||
AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, mkdirat, openat2, renameat, renameat_with,
|
||||
unlinkat,
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::layer::{
|
||||
|
|
@ -94,6 +97,9 @@ impl PreparedLowerStore {
|
|||
bail!("source identity is empty or exceeds 4096 bytes");
|
||||
}
|
||||
let verified = verify_layout(&request.layout_path, self.limits)?;
|
||||
if request.selected_manifest_digest != verified.manifest_digest {
|
||||
bail!("selected manifest identity does not match verified OCI layout");
|
||||
}
|
||||
let identities = Identities::new(request)?;
|
||||
if let Some(existing) = self.available_artifact(identities.artifact_id)? {
|
||||
identities.require_equivalent(&existing)?;
|
||||
|
|
@ -218,10 +224,20 @@ impl PreparedLowerStore {
|
|||
{
|
||||
let _lock = self.physical_lock()?;
|
||||
if let Some(canonical) = self.open_relative(&expected_artifact)? {
|
||||
require_identity(&canonical, reclaiming.device, reclaiming.inode)?;
|
||||
self.rename_relative(&expected_artifact, &quarantine)?;
|
||||
self.sync_fixed_directory("artifacts")?;
|
||||
self.sync_fixed_directory("quarantine")?;
|
||||
if file_identity(&canonical)? == (reclaiming.device, reclaiming.inode) {
|
||||
self.rename_relative_noreplace(&expected_artifact, &quarantine)?;
|
||||
self.sync_fixed_directory("artifacts")?;
|
||||
self.sync_fixed_directory("quarantine")?;
|
||||
} else {
|
||||
let quarantined = self
|
||||
.open_relative(&quarantine)?
|
||||
.context("canonical replacement exists without quarantined old artifact")?;
|
||||
require_identity(&quarantined, reclaiming.device, reclaiming.inode)?;
|
||||
let replacement = self
|
||||
.available_artifact(reclaiming.prepared_artifact_id)?
|
||||
.context("canonical replacement exists without available ownership")?;
|
||||
require_identity(&canonical, replacement.device, replacement.inode)?;
|
||||
}
|
||||
} else {
|
||||
let existing = self
|
||||
.open_relative(&quarantine)?
|
||||
|
|
@ -275,7 +291,7 @@ impl PreparedLowerStore {
|
|||
.context("canonical artifact path exists without matching catalog ownership")?;
|
||||
require_identity(&existing, reclaiming.device, reclaiming.inode)?;
|
||||
let quarantine = quarantine_path(&reclaiming);
|
||||
self.rename_relative(&artifact_path, &quarantine)?;
|
||||
self.rename_relative_noreplace(&artifact_path, &quarantine)?;
|
||||
self.sync_fixed_directory("quarantine")?;
|
||||
}
|
||||
self.rename_relative(&stage, &artifact_path)?;
|
||||
|
|
@ -360,6 +376,12 @@ impl PreparedLowerStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn rename_relative_noreplace(&self, from: &Path, to: &Path) -> Result<()> {
|
||||
let root = File::open(&self.root)?;
|
||||
renameat_with(&root, from, &root, to, RenameFlags::NOREPLACE)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_exact_path(&self, relative: &Path, device: DeviceId, inode: InodeId) -> Result<()> {
|
||||
let root = File::open(&self.root)?;
|
||||
let file = open_directory_at(&root, relative)?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue