Skip to content

Commit

Permalink
Basic fcntl-style locking.
Browse files Browse the repository at this point in the history
Add an `fcntl_lock` function implementing fcntl-style
process-associated locking. Currently this only supports locking the
entire file.
  • Loading branch information
sunfishcode committed Mar 4, 2023
1 parent b855bd0 commit b030c43
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 6 deletions.
29 changes: 29 additions & 0 deletions src/backend/libc/fs/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,35 @@ pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Resul
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_ADD_SEALS, seals.bits())) }
}

#[cfg(not(any(
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
use c::{flock, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK};

let (cmd, l_type) = match operation {
FlockOperation::LockShared => (F_SETLKW, F_RDLCK),
FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK),
FlockOperation::Unlock => (F_SETLKW, F_UNLCK),
FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK),
FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK),
FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK),
};

unsafe {
let mut lock: flock = core::mem::zeroed();
lock.l_type = l_type as _;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0;
ret(c::fcntl(borrowed_fd(fd), cmd, &lock))
}
}

pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
let (whence, offset): (c::c_int, libc_off_t) = match pos {
SeekFrom::Start(pos) => {
Expand Down
5 changes: 3 additions & 2 deletions src/backend/libc/fs/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,10 +785,11 @@ bitflags! {
}
}

/// `LOCK_*` constants for use with [`flock`]
/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`].
///
/// [`flock`]: crate::fs::flock
#[cfg(not(any(target_os = "solaris", target_os = "wasi")))]
/// [`fcntl_lock`]: crate::fs::fcntl_lock
#[cfg(not(target_os = "wasi"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum FlockOperation {
Expand Down
46 changes: 46 additions & 0 deletions src/backend/linux_raw/fs/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,52 @@ pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Resul
}
}

#[inline]
pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
#[cfg(target_pointer_width = "64")]
use linux_raw_sys::general::{flock, F_SETLK, F_SETLKW};
#[cfg(target_pointer_width = "32")]
use linux_raw_sys::general::{flock64 as flock, F_SETLK64 as F_SETLK, F_SETLKW64 as F_SETLKW};
use linux_raw_sys::general::{F_RDLCK, F_UNLCK, F_WRLCK};

let (cmd, l_type) = match operation {
FlockOperation::LockShared => (F_SETLKW, F_RDLCK),
FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK),
FlockOperation::Unlock => (F_SETLKW, F_UNLCK),
FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK),
FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK),
FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK),
};

unsafe {
let lock = flock {
l_type: l_type as _,
l_whence: 0,
l_start: 0,
l_len: 0,
..core::mem::zeroed()
};
#[cfg(target_pointer_width = "32")]
{
ret(syscall_readonly!(
__NR_fcntl64,
fd,
c_uint(cmd),
by_ref(&lock)
))
}
#[cfg(target_pointer_width = "64")]
{
ret(syscall_readonly!(
__NR_fcntl,
fd,
c_uint(cmd),
by_ref(&lock)
))
}
}
}

#[inline]
pub(crate) fn rename(oldname: &CStr, newname: &CStr) -> io::Result<()> {
#[cfg(target_arch = "riscv64")]
Expand Down
3 changes: 2 additions & 1 deletion src/backend/linux_raw/fs/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,9 +518,10 @@ bitflags! {
}
}

/// `LOCK_*` constants for use with [`flock`]
/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`].
///
/// [`flock`]: crate::fs::flock
/// [`fcntl_lock`]: crate::fs::fcntl_lock
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum FlockOperation {
Expand Down
36 changes: 36 additions & 0 deletions src/fs/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
//! a type-safe API, rustix makes them all separate functions so that they
//! can have dedicated static type signatures.
#[cfg(not(any(
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
target_os = "wasi"
)))]
use crate::fs::FlockOperation;
use crate::{backend, io};
use backend::fd::AsFd;
use backend::fs::types::OFlags;
Expand Down Expand Up @@ -85,3 +92,32 @@ pub use backend::fs::types::SealFlags;
pub fn fcntl_add_seals<Fd: AsFd>(fd: Fd, seals: SealFlags) -> io::Result<()> {
backend::fs::syscalls::fcntl_add_seals(fd.as_fd(), seals)
}

/// `fcntl(fd, F_SETLK)`—Acquire or release an `fcntl`-style lock.
///
/// This function doesn't currently have an offset or len; it currently always
/// sets the `l_len` field to 0, which is a special case that means the entire
/// file should be locked.
///
/// Unlike `flock`-style locks, `fcntl`-style locks are process-associated,
/// meaning that they don't guard against being acquired by two threads in
/// the same process.
///
/// # References
/// - [POSIX]
/// - [Linux]
///
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html
/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html
#[cfg(not(any(
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
target_os = "wasi"
)))]
#[inline]
#[doc(alias = "F_SETLK")]
#[doc(alias = "F_SETLKW")]
pub fn fcntl_lock<Fd: AsFd>(fd: Fd, operation: FlockOperation) -> io::Result<()> {
backend::fs::syscalls::fcntl_lock(fd.as_fd(), operation)
}
File renamed without changes.
2 changes: 1 addition & 1 deletion src/fs/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::process::{Gid, Uid};
use crate::{backend, io};
use backend::fd::{AsFd, BorrowedFd};

#[cfg(not(any(target_os = "solaris", target_os = "wasi")))]
#[cfg(not(target_os = "wasi"))]
pub use backend::fs::types::FlockOperation;

#[cfg(not(any(
Expand Down
4 changes: 2 additions & 2 deletions src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod dir;
mod fadvise;
pub(crate) mod fcntl;
#[cfg(apple)]
mod fcntl_darwin;
mod fcntl_apple;
#[cfg(apple)]
mod fcopyfile;
pub(crate) mod fd;
Expand Down Expand Up @@ -66,7 +66,7 @@ pub use dir::{Dir, DirEntry};
pub use fadvise::{fadvise, Advice};
pub use fcntl::*;
#[cfg(apple)]
pub use fcntl_darwin::{fcntl_fullfsync, fcntl_rdadvise};
pub use fcntl_apple::{fcntl_fullfsync, fcntl_rdadvise};
#[cfg(apple)]
pub use fcopyfile::*;
pub use fd::*;
Expand Down
33 changes: 33 additions & 0 deletions tests/fs/fcntl_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#[test]
fn test_fcntl_lock() {
use rustix::fs::{fcntl_lock, FlockOperation};

let f = tempfile::tempfile().unwrap();
fcntl_lock(&f, FlockOperation::LockExclusive).unwrap();
fcntl_lock(&f, FlockOperation::Unlock).unwrap();
let g = tempfile::tempfile().unwrap();
fcntl_lock(&g, FlockOperation::LockExclusive).unwrap();
fcntl_lock(&g, FlockOperation::Unlock).unwrap();
drop(f);
drop(g);

let f = tempfile::tempfile().unwrap();
fcntl_lock(&f, FlockOperation::LockShared).unwrap();
let g = tempfile::tempfile().unwrap();
fcntl_lock(&g, FlockOperation::LockShared).unwrap();
fcntl_lock(&f, FlockOperation::Unlock).unwrap();
fcntl_lock(&g, FlockOperation::Unlock).unwrap();
drop(f);
drop(g);

let f = tempfile::tempfile().unwrap();
fcntl_lock(&f, FlockOperation::LockShared).unwrap();
fcntl_lock(&f, FlockOperation::LockExclusive).unwrap();
fcntl_lock(&f, FlockOperation::Unlock).unwrap();
let g = tempfile::tempfile().unwrap();
fcntl_lock(&g, FlockOperation::LockShared).unwrap();
fcntl_lock(&g, FlockOperation::LockExclusive).unwrap();
fcntl_lock(&g, FlockOperation::Unlock).unwrap();
drop(f);
drop(g);
}
7 changes: 7 additions & 0 deletions tests/fs/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
mod cwd;
mod dir;
mod fcntl;
#[cfg(not(any(
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
target_os = "wasi"
)))]
mod fcntl_lock;
mod file;
#[cfg(not(target_os = "wasi"))]
mod flock;
Expand Down

0 comments on commit b030c43

Please sign in to comment.