filelock_rs/lib.rs
1//! # FdLock
2//!
3//! `FdLock` is a Rust crate that provides functionality for file locking using `flock` or `fcntl` operations.
4//!
5//! This crate defines a trait `FdLock` that extends the `AsRawFd` trait,
6//! allowing file locks to be placed on file descriptors. It supports both
7//! shared and exclusive locks, as well as unlocking operations.
8//!
9//! ## Examples
10//!
11//! Placing a shared lock on a file:
12//!
13//! ```no_run
14//! use filelock_rs::FdLock;
15//! use std::fs::File;
16//!
17//! let file = File::open("data.txt").expect("Failed to open file");
18//! let lock_result = file.lock_shared();
19//!
20//! match lock_result {
21//! Ok(()) => {
22//! println!("Shared lock placed on the file");
23//! // Perform operations with the locked file
24//! }
25//! Err(error) => {
26//! eprintln!("Failed to place a shared lock: {}", error);
27//! }
28//! }
29//! ```
30//!
31//! Placing an exclusive lock on a file:
32//!
33//! ```rust
34//! use filelock_rs::FdLock;
35//! use std::fs::File;
36//!
37//! let file = File::create("data.txt").expect("Failed to create file");
38//! let lock_result = file.lock_exclusive();
39//!
40//! match lock_result {
41//! Ok(()) => {
42//! println!("Exclusive lock placed on the file");
43//! // Perform operations with the locked file
44//! }
45//! Err(error) => {
46//! eprintln!("Failed to place an exclusive lock: {}", error);
47//! }
48//! }
49//! ```
50//!
51//! ## Cleanup
52//!
53//! The `FdLock` trait is implemented for the `std::fs::File` type. When the `FdLock` methods are
54//! used on a `File` instance, the locks are automatically released when they go out of scope.
55//!
56//! ## Notes
57//!
58//! - The behavior of file locking may differ depending on the operating system.
59//! - The crate uses the `libc` and `io::Result` types from the standard library.
60//! - If the file lock operation fails, an `io::Error` is returned.
61pub mod pid;
62
63use std::io;
64use std::os::fd::AsRawFd;
65
66/// FdLock Operation type.
67pub type Operation = libc::c_int;
68
69/// Place a shared lock. More than one process may hold a shared lock for a given file at a given time.
70#[allow(dead_code)]
71const LOCK_SH: Operation = libc::LOCK_SH;
72/// Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a given time.
73#[allow(dead_code)]
74const LOCK_EX: Operation = libc::LOCK_EX;
75/// Remove an existing lock held by this process.
76#[allow(dead_code)]
77const LOCK_UN: Operation = libc::LOCK_UN;
78
79/// The `FdLock` trait extends the `AsRawFd` trait, allowing
80/// file locks to be placed on file descriptors.
81pub trait FdLock: AsRawFd {
82 /// Places a file lock on the associated file descriptor using the `flock` operation.
83 ///
84 /// # Arguments
85 ///
86 /// * `operation`: The type of lock to place on the file.
87 ///
88 /// # Errors
89 ///
90 /// If the lock operation fails, an `io::Error` is returned.
91 ///
92 #[cfg(not(target_os = "solaris"))]
93 fn flock(&self, operation: Operation) -> io::Result<()> {
94 let ret = unsafe { libc::flock(self.as_raw_fd(), operation) };
95 if ret < 0 {
96 return Err(io::Error::last_os_error());
97 }
98 Ok(())
99 }
100
101 /// Places a file lock on the associated file descriptor using the `flock` operation.
102 ///
103 /// # Arguments
104 ///
105 /// * `operation`: The type of lock to place on the file.
106 ///
107 /// # Errors
108 ///
109 /// If the lock operation fails, an `io::Error` is returned.
110 ///
111 #[cfg(target_os = "solaris")]
112 fn flock(&self, operation: Operation) -> io::Result<()> {
113 // Solaris lacks flock(), so try to emulate using fcntl()
114 let mut flock = libc::flock {
115 l_type: 0,
116 l_whence: 0,
117 l_start: 0,
118 l_len: 0,
119 l_sysid: 0,
120 l_pid: 0,
121 l_pad: [0, 0, 0, 0],
122 };
123 flock.l_type = if operation & LOCK_UN != 0 {
124 LOCK_UN
125 } else if operation & LOCK_EX != 0 {
126 libc::F_WRLCK
127 } else if operation & LOCK_SH != 0 {
128 libc::F_RDLCK
129 } else {
130 return Err(io::Error::new(
131 io::ErrorKind::Other,
132 format!("unexpected flock() operation"),
133 ));
134 };
135
136 let mut cmd = libc::F_SETLKW;
137 if (flag & libc::LOCK_NB) != 0 {
138 cmd = libc::F_SETLK;
139 }
140
141 let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) };
142 if ret < 0 {
143 Err(Error::last_os_error())
144 } else {
145 Ok(())
146 }
147 }
148
149 /// Places a shared lock on the file.
150 ///
151 /// This method uses the `LOCK_SH` operation to place a shared lock on the associated file descriptor.
152 ///
153 /// # Errors
154 ///
155 /// If the lock operation fails, an `io::Error` is returned.
156 ///
157 fn lock_shared(&self) -> io::Result<()> {
158 self.flock(libc::LOCK_SH)
159 }
160
161 /// Places an exclusive lock on the file.
162 ///
163 /// This method uses the `LOCK_EX` operation to place an exclusive lock on the associated file descriptor.
164 ///
165 /// # Errors
166 ///
167 /// If the lock operation fails, an `io::Error` is returned.
168 ///
169 fn lock_exclusive(&self) -> io::Result<()> {
170 self.flock(libc::LOCK_EX)
171 }
172
173 /// Tries to place a shared lock on the file.
174 ///
175 /// This method uses the `LOCK_SH | LOCK_NB` operations to try placing a shared lock on the associated file descriptor.
176 ///
177 /// # Errors
178 ///
179 /// If the lock operation fails or the lock is not immediately available, an `io::Error` is returned.
180 ///
181 fn try_lock_shared(&self) -> io::Result<()> {
182 self.flock(libc::LOCK_SH | libc::LOCK_NB)
183 }
184
185 /// Tries to place an exclusive lock on the file.
186 ///
187 /// This method uses the `LOCK_EX | LOCK_NB` operations to try placing an exclusive lock on the associated file descriptor.
188 ///
189 /// # Errors
190 ///
191 /// If the lock operation fails or the lock is not immediately available, an `io::Error` is returned.
192 ///
193 fn try_lock_exclusive(&self) -> io::Result<()> {
194 self.flock(libc::LOCK_EX | libc::LOCK_NB)
195 }
196
197 /// Unlocks the file.
198 ///
199 /// This method removes the lock held by the current process on the associated file descriptor.
200 /// It uses the `LOCK_UN` operation to unlock the file.
201 ///
202 /// # Errors
203 ///
204 /// If the unlock operation fails, an `io::Error` is returned.
205 ///
206 fn unlock(&self) -> io::Result<()> {
207 self.flock(libc::LOCK_UN)
208 }
209}
210
211impl FdLock for std::fs::File {}