diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f76bfaa24..3d565a9ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/Cargo.toml b/Cargo.toml index 7327987b56..9d4711f1ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/fcntl.rs b/src/fcntl.rs index 773d79913e..1dddee7361 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -286,6 +286,12 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result { 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), @@ -295,8 +301,6 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result { 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!() } }; diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 38a1e7b95a..691236eacf 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -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}; @@ -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",