Skip to content

Commit c5af4ad

Browse files
authored
Add Flock::relock (#2407)
It can upgrade or downgrade a lock. Fixes #2356
1 parent e7acaff commit c5af4ad

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

changelog/2407.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added `Flock::relock` for upgrading and downgrading locks.

src/fcntl.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,30 @@ impl<T: Flockable> Flock<T> {
955955
std::mem::forget(self);
956956
Ok(inner)
957957
}
958+
959+
/// Relock the file. This can upgrade or downgrade the lock type.
960+
///
961+
/// # Example
962+
/// ```
963+
/// # use std::fs::File;
964+
/// # use nix::fcntl::{Flock, FlockArg};
965+
/// # use tempfile::tempfile;
966+
/// let f: std::fs::File = tempfile().unwrap();
967+
/// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap();
968+
/// // Do stuff, then downgrade the lock
969+
/// locked_file.relock(FlockArg::LockShared).unwrap();
970+
/// ```
971+
pub fn relock(&self, arg: FlockArg) -> Result<()> {
972+
let flags = match arg {
973+
FlockArg::LockShared => libc::LOCK_SH,
974+
FlockArg::LockExclusive => libc::LOCK_EX,
975+
FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
976+
FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
977+
#[allow(deprecated)]
978+
FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL),
979+
};
980+
Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop)
981+
}
958982
}
959983

960984
// Safety: `File` is not [std::clone::Clone].

test/test_fcntl.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ mod test_flock {
686686

687687
/// Verify that `Flock::lock()` correctly obtains a lock, and subsequently unlocks upon drop.
688688
#[test]
689-
fn verify_lock_and_drop() {
689+
fn lock_and_drop() {
690690
// Get 2 `File` handles to same underlying file.
691691
let file1 = NamedTempFile::new().unwrap();
692692
let file2 = file1.reopen().unwrap();
@@ -710,9 +710,32 @@ mod test_flock {
710710
}
711711
}
712712

713+
/// An exclusive lock can be downgraded
714+
#[test]
715+
fn downgrade() {
716+
let file1 = NamedTempFile::new().unwrap();
717+
let file2 = file1.reopen().unwrap();
718+
let file1 = file1.into_file();
719+
720+
// Lock first handle
721+
let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
722+
723+
// Attempt to lock second handle
724+
let file2 = Flock::lock(file2, FlockArg::LockSharedNonblock)
725+
.unwrap_err()
726+
.0;
727+
728+
// Downgrade the lock
729+
lock1.relock(FlockArg::LockShared).unwrap();
730+
731+
// Attempt to lock second handle again (but successfully)
732+
Flock::lock(file2, FlockArg::LockSharedNonblock)
733+
.expect("Expected locking to be successful.");
734+
}
735+
713736
/// Verify that `Flock::unlock()` correctly obtains unlocks.
714737
#[test]
715-
fn verify_unlock() {
738+
fn unlock() {
716739
// Get 2 `File` handles to same underlying file.
717740
let file1 = NamedTempFile::new().unwrap();
718741
let file2 = file1.reopen().unwrap();
@@ -729,4 +752,29 @@ mod test_flock {
729752
panic!("Expected locking to be successful.");
730753
}
731754
}
755+
756+
/// A shared lock can be upgraded
757+
#[test]
758+
fn upgrade() {
759+
let file1 = NamedTempFile::new().unwrap();
760+
let file2 = file1.reopen().unwrap();
761+
let file3 = file1.reopen().unwrap();
762+
let file1 = file1.into_file();
763+
764+
// Lock first handle
765+
let lock1 = Flock::lock(file1, FlockArg::LockShared).unwrap();
766+
767+
// Attempt to lock second handle
768+
{
769+
Flock::lock(file2, FlockArg::LockSharedNonblock)
770+
.expect("Locking should've succeeded");
771+
}
772+
773+
// Upgrade the lock
774+
lock1.relock(FlockArg::LockExclusive).unwrap();
775+
776+
// Acquiring an additional shared lock should fail
777+
Flock::lock(file3, FlockArg::LockSharedNonblock)
778+
.expect_err("Should not have been able to lock the file");
779+
}
732780
}

0 commit comments

Comments
 (0)