Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement open file descriptor locks in fcntl #1195

Merged
merged 14 commits into from Apr 8, 2020
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
- Added support for `F_OFD_*` `fcntl` commands on Linux and Android.
(#[1195](https://github.com/nix-rust/nix/pull/1195))
- Added `env::clearenv()`: calls `libc::clearenv` on platforms
where it's available, and clears the environment of all variables
via `std::env::vars` and `std::env::remove_var` on others.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -16,7 +16,7 @@ exclude = [
]

[dependencies]
libc = { version = "0.2.60", features = [ "extra_traits" ] }
libc = { version = "0.2.68", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "0.1.10"
void = "1.0.2"
Expand Down
8 changes: 6 additions & 2 deletions src/fcntl.rs
Expand Up @@ -286,6 +286,12 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
Expand All @@ -295,8 +301,6 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
#[cfg(any(target_os = "linux", target_os = "android"))]
_ => unimplemented!()
}
};

Expand Down
95 changes: 94 additions & 1 deletion test/test_fcntl.rs
Expand Up @@ -65,13 +65,15 @@ fn test_readlink() {

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use std::fs::File;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::io::{BufRead, BufReader, SeekFrom};
use std::os::unix::prelude::*;

use libc::loff_t;

use nix::fcntl::*;
use nix::sys::stat::fstat;
use nix::sys::uio::IoVec;
use nix::unistd::{close, pipe, read, write};

Expand Down Expand Up @@ -197,6 +199,97 @@ mod linux_android {
let mut buf = [0u8; 200];
assert_eq!(100, read(fd, &mut buf).unwrap());
}

// The tests below are disabled for the listed targets
// due to OFD locks not being available in the kernel/libc
// versions used in the CI environment, probably because
// they run under QEMU.

#[test]
#[cfg(not(any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "armv7",
target_arch = "x86",
target_arch = "mips",
target_arch = "mips64",
target_arch = "mips64el",
target_arch = "powerpc64",
target_arch = "powerpc64le")))]
fn test_ofd_write_lock() {
let tmp = NamedTempFile::new().unwrap();

let fd = tmp.as_raw_fd();
let inode = fstat(fd).expect("fstat failed").st_ino as usize;

let mut flock = libc::flock {
l_type: libc::F_WRLCK as libc::c_short,
l_whence: libc::SEEK_SET as libc::c_short,
l_start: 0,
l_len: 0,
l_pid: 0,
};
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "WRITE".to_string())),
lock_info(inode)
);

flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
assert_eq!(None, lock_info(inode));
}

#[test]
#[cfg(not(any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "armv7",
target_arch = "x86",
target_arch = "mips",
target_arch = "mips64",
target_arch = "mips64el",
target_arch = "powerpc64",
target_arch = "powerpc64le")))]
fn test_ofd_read_lock() {
let tmp = NamedTempFile::new().unwrap();

let fd = tmp.as_raw_fd();
let inode = fstat(fd).expect("fstat failed").st_ino as usize;

let mut flock = libc::flock {
l_type: libc::F_RDLCK as libc::c_short,
l_whence: libc::SEEK_SET as libc::c_short,
l_start: 0,
l_len: 0,
l_pid: 0,
};
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "READ".to_string())),
lock_info(inode)
);

flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
assert_eq!(None, lock_info(inode));
}

fn lock_info(inode: usize) -> Option<(String, String)> {
let file = File::open("/proc/locks").expect("open /proc/locks failed");
let buf = BufReader::new(file);

for line in buf.lines() {
let line = line.unwrap();
let parts: Vec<_> = line.split_whitespace().collect();
let lock_type = parts[1];
let lock_access = parts[3];
let ino_parts: Vec<_> = parts[5].split(':').collect();
let ino: usize = ino_parts[2].parse().unwrap();
if ino == inode {
return Some((lock_type.to_string(), lock_access.to_string()));
}
}
None
}
}

#[cfg(any(target_os = "linux",
Expand Down