1use std::path::{Path, PathBuf};
4
5use prikk_error::{PrikkError, Result};
6
7use crate::fsutil::{MutationRoot, create_new_file_required, remove_file_cleanup_best_effort};
8use crate::layout::{LockableContainer, RepositoryLayout};
9
10#[derive(Debug)]
12pub struct ActiveLock {
13 path: PathBuf,
14 relative: PathBuf,
15 mutation_root: MutationRoot,
16}
17
18impl ActiveLock {
19 pub fn acquire(layout: &RepositoryLayout) -> Result<Self> {
21 let path = layout.default_active_lock_path();
22 let relative = layout.repository_relative(&path)?;
23 let mutation_root = layout.repository_mutation_root().clone();
24 acquire_lock_file(&mutation_root, &relative, &path, "active")?;
25 Ok(Self {
26 path,
27 relative,
28 mutation_root,
29 })
30 }
31
32 #[must_use]
34 pub fn path(&self) -> &Path {
35 &self.path
36 }
37
38 pub(crate) fn require_layout(&self, layout: &RepositoryLayout) -> Result<()> {
39 if self
40 .mutation_root
41 .same_authority(layout.repository_mutation_root())
42 {
43 return Ok(());
44 }
45 Err(PrikkError::LockConflict(
46 "active lock belongs to a different repository authority".to_string(),
47 ))
48 }
49}
50
51impl Drop for ActiveLock {
52 fn drop(&mut self) {
53 remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
54 }
55}
56
57#[derive(Debug)]
59pub struct RefLock {
60 path: PathBuf,
61 relative: PathBuf,
62 mutation_root: MutationRoot,
63}
64
65impl RefLock {
66 pub fn acquire(layout: &RepositoryLayout, ref_name: &str) -> Result<Self> {
68 let path = layout.ref_lock_path(ref_name);
69 let relative = layout.repository_relative(&path)?;
70 let mutation_root = layout.repository_mutation_root().clone();
71 acquire_lock_file(&mutation_root, &relative, &path, "ref")?;
72 Ok(Self {
73 path,
74 relative,
75 mutation_root,
76 })
77 }
78
79 #[must_use]
81 pub fn path(&self) -> &Path {
82 &self.path
83 }
84}
85
86impl Drop for RefLock {
87 fn drop(&mut self) {
88 remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
89 }
90}
91
92#[derive(Debug)]
96struct ContainerLockHandle {
97 relative: PathBuf,
98 mutation_root: MutationRoot,
99}
100
101impl Drop for ContainerLockHandle {
102 fn drop(&mut self) {
103 remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
104 }
105}
106
107#[derive(Debug)]
111pub struct ContainerLockGuard {
112 _handles: Vec<ContainerLockHandle>,
113}
114
115pub fn acquire_container_locks(
129 layout: &RepositoryLayout,
130 containers: &[LockableContainer],
131) -> Result<ContainerLockGuard> {
132 let mut sorted = containers.to_vec();
133 sorted.sort_unstable();
134 sorted.dedup();
135 let mutation_root = layout.repository_mutation_root().clone();
136 let mut handles = Vec::with_capacity(sorted.len());
137 for container in sorted {
138 let path = layout.lockable_container_lock_path(container);
139 let relative = layout.repository_relative(&path)?;
140 acquire_lock_file(
141 &mutation_root,
142 &relative,
143 &path,
144 container_lock_kind(container),
145 )?;
146 handles.push(ContainerLockHandle {
147 relative,
148 mutation_root: mutation_root.clone(),
149 });
150 }
151 Ok(ContainerLockGuard { _handles: handles })
152}
153
154fn container_lock_kind(container: LockableContainer) -> &'static str {
155 match container {
156 LockableContainer::RefPointerIndex => "container:ref-pointer-index",
157 LockableContainer::RefLog => "container:ref-log",
158 LockableContainer::ReceivedIndex => "container:received-index",
159 LockableContainer::TrustPolicy => "container:trust-policy",
160 }
161}
162
163fn acquire_lock_file(
164 mutation_root: &MutationRoot,
165 relative: &Path,
166 path: &Path,
167 kind: &str,
168) -> Result<()> {
169 let body = lock_body(kind);
170 match create_new_file_required(mutation_root, relative, body.as_bytes()) {
171 Ok(()) => Ok(()),
172 Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => Err(
173 PrikkError::LockConflict(format!("{kind} lock already exists: {}", path.display())),
174 ),
175 Err(err) => Err(err.into()),
176 }
177}
178
179fn lock_body(kind: &str) -> String {
180 format!(
181 "pid={}\nkind={kind}\nnote=PR-007 lock has no stale-lock stealing yet\n",
182 std::process::id()
183 )
184}
185
186#[cfg(all(test, target_os = "linux"))]
189mod tests;