Skip to content

Commit

Permalink
Merge #1195
Browse files Browse the repository at this point in the history
1195: Implement open file descriptor locks in fcntl r=asomers a=andrenth

Hello

This PR updates libc to 0.2.68, which adds the `F_OFD_*` fcntl commands, and uses them in `nix::fcntl::fcntl`.

Co-authored-by: Andre Nathan <andre@digirati.com.br>
  • Loading branch information
bors[bot] and andrenth committed Apr 8, 2020
2 parents 5c8cdd0 + f7f1d09 commit b5ee610
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 4 deletions.
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

0 comments on commit b5ee610

Please sign in to comment.