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 #[cfg(unix)]
49 file: fs::File,
50}
51
52impl JournalLock {
53 pub fn acquire(journal_path: &Path) -> Result<Self, JournalLockError> {
58 let path = journal_lock_path(journal_path);
59
60 #[cfg(unix)]
61 {
62 acquire_supported(&path)
63 }
64
65 #[cfg(not(unix))]
66 {
67 Err(io::Error::new(
68 io::ErrorKind::Unsupported,
69 format!(
70 "journal locking is unsupported on this host: {}",
71 path.display()
72 ),
73 )
74 .into())
75 }
76 }
77}
78
79#[cfg(unix)]
80impl Drop for JournalLock {
81 fn drop(&mut self) {
82 file_lock::unlock(&self.file);
83 }
84}
85
86#[cfg(unix)]
87fn acquire_supported(path: &Path) -> Result<JournalLock, JournalLockError> {
88 match file_lock::acquire(path) {
89 Ok(file) => Ok(JournalLock { file }),
90 Err(FileLockError::Locked) => Err(JournalLockError::Locked {
91 lock_path: path.to_string_lossy().to_string(),
92 }),
93 Err(FileLockError::UnsafeEntry { kind }) => Err(JournalLockError::UnsafeEntry {
94 lock_path: path.to_string_lossy().to_string(),
95 kind,
96 }),
97 Err(FileLockError::Io(error)) => Err(JournalLockError::Io(error)),
98 }
99}
100
101fn journal_lock_path(path: &Path) -> PathBuf {
102 let mut lock_path = path.as_os_str().to_os_string();
103 lock_path.push(".lock");
104 PathBuf::from(lock_path)
105}
106
107#[cfg(test)]
108mod tests;