1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright (c) 2023 Nick Piaddo
// SPDX-License-Identifier: Apache-2.0 OR MIT
// From dependency library
// From standard library
use std::mem::MaybeUninit;
use std::path::Path;
// From this library
use crate::core::errors::FileLockError;
use crate::ffi_utils;
/// File lock.
#[derive(Debug)]
#[repr(transparent)]
pub struct FileLock {
pub(crate) ptr: *mut libmount::libmnt_lock,
}
impl FileLock {
#[doc(hidden)]
#[allow(dead_code)]
/// Wraps a boxed raw `libmount::mnt_lock` pointer in a safe reference.
pub(crate) unsafe fn ref_from_boxed_ptr<'a>(
ptr: Box<*mut libmount::libmnt_lock>,
) -> (*mut *mut libmount::libmnt_lock, &'a FileLock) {
let raw_ptr = Box::into_raw(ptr);
let entry_ref = unsafe { &*(raw_ptr as *const _ as *const FileLock) };
(raw_ptr, entry_ref)
}
#[doc(hidden)]
#[allow(dead_code)]
/// Wraps a boxed raw `libmount::mnt_lock` pointer in a safe mutable reference.
pub(crate) unsafe fn mut_from_boxed_ptr<'a>(
ptr: Box<*mut libmount::libmnt_lock>,
) -> (*mut *mut libmount::libmnt_lock, &'a mut FileLock) {
let raw_ptr = Box::into_raw(ptr);
let entry_ref = unsafe { &mut *(raw_ptr as *mut FileLock) };
(raw_ptr, entry_ref)
}
/// Creates a new `FileLock`.
pub fn new<T>(file: T) -> Result<FileLock, FileLockError>
where
T: AsRef<Path>,
{
let file = file.as_ref();
let file_cstr = ffi_utils::as_ref_path_to_c_string(file)?;
log::debug!(
"FileLock::new creating a new `FileLock` for file {:?}",
file
);
// The second argument of mnt_new_lock should hold a `pid_t` value but is ignored by
// `libmount`. Putting i32::MIN should trigger an error if `libmount`'s API changes in
// future versions.
let mut ptr = MaybeUninit::<*mut libmount::libmnt_lock>::zeroed();
unsafe { ptr.write(libmount::mnt_new_lock(file_cstr.as_ptr(), i32::MIN)) };
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!("failed to create a new `FileLock` for file {:?}", file);
log::debug!(
"FileLock::new {}. libmount::mnt_new_lock returned a NULL pointer",
err_msg
);
Err(FileLockError::Creation(err_msg))
}
ptr => {
log::debug!("FileLock::new created a new `FileLock` for file {:?}", file);
let lock = Self { ptr };
Ok(lock)
}
}
}
/// Locks the associated file.
pub fn lock(&mut self) -> Result<(), FileLockError> {
log::debug!("FileLock::lock locking file");
let result = unsafe { libmount::mnt_lock_file(self.ptr) };
match result {
0 => {
log::debug!("FileLock::lock locked file");
Ok(())
}
code => {
let err_msg = "failed to lock file".to_owned();
log::debug!(
"FileLock::lock {}. libmount::mnt_lock_file returned error code: {:?}",
err_msg,
code
);
Err(FileLockError::Lock(err_msg))
}
}
}
/// Releases the lock on the associated file.
pub fn unlock(&mut self) {
log::debug!("FileLock::unlock releasing file lock");
unsafe { libmount::mnt_unlock_file(self.ptr) }
}
#[doc(hidden)]
/// Blocks/Unblocks signals.
fn set_signals(lock: *mut libmount::libmnt_lock, enable: bool) -> Result<(), FileLockError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_lock_block_signals(lock, op) };
match result {
0 => {
log::debug!("FileLock::set_signals {}d signals", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} signals", op_str);
log::debug!("FileLock::set_signals {}. libmount::mnt_lock_block_signals returned error code: {:?}", err_msg, code);
Err(FileLockError::Config(err_msg))
}
}
}
// List from https://www.man7.org/linux/man-pages/man7/signal.7.html#DESCRIPTION
/// Blocks POSIX signals.
///
/// Linux supports the standard signals listed below. The second column of the table indicates
/// which standard (if any) specified the signal: `P1990` indicates that the signal is described in
/// the original POSIX.1-1990 standard; `P2001` indicates that the signal was added in SUSv2 and
/// POSIX.1-2001.
///
/// | Signal | Standard | Action | Comment |
/// | ----- | ------ | ---- | ---- |
/// | SIGABRT | P1990 | Core | Abort signal from [abort(3)](https://www.man7.org/linux/man-pages/man3/abort.3.html) |
/// | SIGALRM | P1990 | Term | Timer signal from [alarm(2)](https://www.man7.org/linux/man-pages/man2/alarm.2.html) |
/// | SIGBUS | P2001 | Core | Bus error (bad memory access) |
/// | SIGCHLD | P1990 | Ign | Child stopped or terminated |
/// | SIGCLD | - | Ign | A synonym for SIGCHLD |
/// | SIGCONT | P1990 | Cont | Continue if stopped |
/// | SIGEMT | - | Term | Emulator trap |
/// | SIGFPE | P1990 | Core | Floating-point exception |
/// | SIGHUP | P1990 | Term | Hangup detected on controlling terminal or death of controlling process |
/// | SIGILL | P1990 | Core | Illegal Instruction |
/// | SIGINFO | - | | A synonym for SIGPWR |
/// | SIGINT | P1990 | Term | Interrupt from keyboard |
/// | SIGIO | - | Term | I/O now possible (4.2BSD) |
/// | SIGIOT | - | Core | IOT trap. A synonym for SIGABRT |
/// | SIGKILL | P1990 | Term | Kill signal |
/// | SIGLOST | - | Term | File lock lost (unused) |
/// | SIGPIPE | P1990 | Term | Broken pipe: write to pipe with no readers; see [pipe(7)](https://www.man7.org/linux/man-pages/man7/pipe.7.html) |
/// | SIGPOLL | P2001 | Term | Pollable event (Sys V); synonym for SIGIO |
/// | SIGPROF | P2001 | Term | Profiling timer expired |
/// | SIGPWR | - | Term | Power failure (System V) |
/// | SIGQUIT | P1990 | Core | Quit from keyboard |
/// | SIGSEGV | P1990 | Core | Invalid memory reference |
/// | SIGSTKFLT | - | Term | Stack fault on coprocessor (unused) |
/// | SIGSTOP | P1990 | Stop | Stop process |
/// | SIGTSTP | P1990 | Stop | Stop typed at terminal |
/// | SIGSYS | P2001 | Core | Bad system call (SVr4); see also [seccomp(2)](https://www.man7.org/linux/man-pages/man2/seccomp.2.html) |
/// | SIGTERM | P1990 | Term | Termination signal |
/// | SIGTRAP | P2001 | Core | Trace/breakpoint trap |
/// | SIGTTIN | P1990 | Stop | Terminal input for background process |
/// | SIGTTOU | P1990 | Stop | Terminal output for background process |
/// | SIGUNUSED | - | Core | Synonymous with SIGSYS |
/// | SIGURG | P2001 | Ign | Urgent condition on socket (4.2BSD) |
/// | SIGUSR1 | P1990 | Term | User-defined signal 1 |
/// | SIGUSR2 | P1990 | Term | User-defined signal 2 |
/// | SIGVTALRM | P2001 | Term | Virtual alarm clock (4.2BSD) |
/// | SIGXCPU | P2001 | Core | CPU time limit exceeded (4.2BSD); see [setrlimit(2)](https://www.man7.org/linux/man-pages/man2/setrlimit.2.html) |
/// | SIGXFSZ | P2001 | Core | File size limit exceeded (4.2BSD); see [setrlimit(2)](https://www.man7.org/linux/man-pages/man2/setrlimit.2.html) |
/// | SIGWINCH | - | Ign | Window resize signal (4.3BSD, Sun) |
///
/// The signals `SIGKILL` and `SIGSTOP` cannot be caught, blocked, or
/// ignored.
///
/// Source: <cite>[Signal - Overview of signals](https://www.man7.org/linux/man-pages/man7/signal.7.html), §Standard signals.</cite>
pub fn block_signals(&mut self) -> Result<(), FileLockError> {
log::debug!("FileLock::block_signals blocking signals");
Self::set_signals(self.ptr, true)
}
/// Unblocks signals.
pub fn unblock_signals(&mut self) -> Result<(), FileLockError> {
log::debug!("FileLock::unblock_signals unblocking signals");
Self::set_signals(self.ptr, false)
}
}
impl AsRef<FileLock> for FileLock {
#[inline]
fn as_ref(&self) -> &FileLock {
self
}
}
impl Drop for FileLock {
fn drop(&mut self) {
log::debug!("FileLock::drop deallocating `FileLock` instance");
self.unlock();
unsafe { libmount::mnt_free_lock(self.ptr) }
}
}