ic_backup/ops/persistence/journal_lock/
mod.rs1use std::{fs, io, path::Path, path::PathBuf};
7
8use super::file_lock::{self, FileLockError};
9use thiserror::Error as ThisError;
10
11#[derive(Debug, ThisError)]
18pub enum JournalLockError {
19 #[error("filesystem authority is locked: {lock_path}")]
21 Locked {
22 lock_path: String,
24 },
25
26 #[error("unsafe lock entry at {lock_path}: {kind}")]
28 UnsafeEntry {
29 lock_path: String,
31 kind: String,
33 },
34
35 #[error(transparent)]
37 Io(#[from] io::Error),
38}
39
40#[derive(Debug)]
47pub struct JournalLock {
48 journal: PathBuf,
49 #[cfg(unix)]
50 file: fs::File,
51}
52
53impl JournalLock {
54 #[must_use]
56 pub fn journal(&self) -> &Path {
57 &self.journal
58 }
59
60 pub fn acquire(journal_path: &Path) -> Result<Self, JournalLockError> {
65 let name = journal_path
66 .file_name()
67 .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidInput))?;
68 let parent = journal_path
69 .parent()
70 .filter(|parent| !parent.as_os_str().is_empty())
71 .unwrap_or_else(|| Path::new("."));
72 let journal = parent.canonicalize()?.join(name);
74 let path = journal_lock_path(&journal);
75
76 #[cfg(unix)]
77 {
78 acquire_supported(&path, journal)
79 }
80
81 #[cfg(not(unix))]
82 {
83 Err(io::Error::new(
84 io::ErrorKind::Unsupported,
85 format!(
86 "journal locking is unsupported on this host: {}",
87 path.display()
88 ),
89 )
90 .into())
91 }
92 }
93}
94
95#[cfg(unix)]
96impl Drop for JournalLock {
97 fn drop(&mut self) {
98 file_lock::unlock(&self.file);
99 }
100}
101
102#[cfg(unix)]
103fn acquire_supported(path: &Path, journal: PathBuf) -> Result<JournalLock, JournalLockError> {
104 match file_lock::acquire(path) {
105 Ok(file) => Ok(JournalLock { journal, file }),
106 Err(FileLockError::Locked) => Err(JournalLockError::Locked {
107 lock_path: path.to_string_lossy().to_string(),
108 }),
109 Err(FileLockError::UnsafeEntry { kind }) => Err(JournalLockError::UnsafeEntry {
110 lock_path: path.to_string_lossy().to_string(),
111 kind,
112 }),
113 Err(FileLockError::Io(error)) => Err(JournalLockError::Io(error)),
114 }
115}
116
117fn journal_lock_path(path: &Path) -> PathBuf {
118 let mut lock_path = path.as_os_str().to_os_string();
119 lock_path.push(".lock");
120 PathBuf::from(lock_path)
121}
122
123#[cfg(test)]
124mod tests;