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
use std::io::{Error as IOError, ErrorKind as IOErrorKind}; use std::os::unix::io::AsRawFd; use nix::{ errno::Errno, fcntl::{flock, FlockArg}, Error as NixError, }; pub enum LockType { Exclusive, Shared, } #[derive(Debug)] pub enum Error { InvalidFd, Interrupted, InvalidOperation, OutOfMemory, WouldBlock, Other(NixError), } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use Error::*; match self { InvalidFd => write!(f, "The provided item is not an open file descriptor."), Interrupted => write!(f, "While waiting to acquire a lock, the call was interrupted by delivery of a signal caught by a handler."), InvalidOperation => write!(f, "File locking operation is invalid."), OutOfMemory => write!(f, "The kernel ran out of memory for allocating lock records."), WouldBlock => write!(f, "The file is locked and the blocking flag was set to false."), Other(e) => write!(f, "Non-flock error: {}", e), } } } impl std::error::Error for Error {} impl From<NixError> for Error { fn from(e: NixError) -> Self { match e { NixError::Sys(Errno::EBADF) => Error::InvalidFd, NixError::Sys(Errno::EINTR) => Error::Interrupted, NixError::Sys(Errno::EINVAL) => Error::InvalidOperation, NixError::Sys(Errno::ENOLCK) => Error::OutOfMemory, NixError::Sys(nix::errno::EWOULDBLOCK) => Error::WouldBlock, _ => Error::Other(e), } } } impl From<Error> for IOError { fn from(e: Error) -> IOError { use Error::*; match e { InvalidFd | InvalidOperation => IOError::new(IOErrorKind::InvalidInput, e), Interrupted => IOError::new(IOErrorKind::Interrupted, e), OutOfMemory | Other(_) => IOError::new(IOErrorKind::Other, e), WouldBlock => IOError::new(IOErrorKind::WouldBlock, e), } } } pub struct FdLock<F: AsRawFd>(Option<F>); impl<F: AsRawFd> std::ops::Deref for FdLock<F> { type Target = F; fn deref(&self) -> &Self::Target { self.0.as_ref().unwrap() } } impl<F: AsRawFd> std::ops::DerefMut for FdLock<F> { fn deref_mut(&mut self) -> &mut Self::Target { self.0.as_mut().unwrap() } } impl<F: AsRawFd> FdLock<F> { pub fn lock(f: F, lock_type: LockType, blocking: bool) -> Result<Self, Error> { flock( f.as_raw_fd(), match lock_type { LockType::Exclusive => { if blocking { FlockArg::LockExclusive } else { FlockArg::LockExclusiveNonblock } } LockType::Shared => { if blocking { FlockArg::LockShared } else { FlockArg::LockSharedNonblock } } }, )?; Ok(FdLock(Some(f))) } pub fn map<Func: FnOnce(F) -> F_, F_: AsRawFd>(mut self, map_fn: Func) -> FdLock<F_> { FdLock(self.0.take().map(map_fn)) } pub fn unlock(mut self, blocking: bool) -> Result<F, (Self, Error)> { match flock( self.0.as_ref().unwrap().as_raw_fd(), if blocking { FlockArg::Unlock } else { FlockArg::UnlockNonblock }, ) { Ok(()) => Ok(self.0.take().unwrap()), Err(e) => Err((self, e.into())), } } } impl<F: AsRawFd> std::ops::Drop for FdLock<F> { fn drop(&mut self) { if let Some(f) = self.0.take() { flock(f.as_raw_fd(), FlockArg::Unlock).unwrap() } } }