From 099628e70117f444b4b8358697f1bf3c063e516f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 22 Mar 2017 17:42:57 +0100 Subject: [PATCH 01/10] add support for `mknodat` --- CHANGELOG.md | 2 +- src/sys/stat.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36b528ca91..93dcd07fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, mknodat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 7a0b3970ce..b24e93783d 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -50,6 +50,19 @@ pub fn mknod(path: &P, kind: SFlag, perm: Mode, dev: dev_t) Errno::result(res).map(drop) } +/// Create a special or ordinary file +/// ([see mknodat(2)](http://man7.org/linux/man-pages/man2/mknodat.2.html)). +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn mknodat(dirfd: &RawFd, path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> { + let res = try!(path.with_nix_path(|cstr| { + unsafe { + libc::mknodat(*dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev) + } + })); + + Errno::result(res).map(drop) +} + #[cfg(target_os = "linux")] pub fn major(dev: dev_t) -> u64 { ((dev >> 32) & 0xfffff000) | From 007a2b7ae65d219d2069c65017fdecf1f650cf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 23 Mar 2017 22:00:49 +0100 Subject: [PATCH 02/10] add support for `rename/renameat` --- CHANGELOG.md | 2 +- src/fcntl.rs | 29 +++++++++++++++++++++++++++++ test/test_fcntl.rs | 21 ++++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93dcd07fa6..80d1a7ae82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, mknodat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/fcntl.rs b/src/fcntl.rs index 003c316c06..d8593156d0 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -206,6 +206,35 @@ libc_bitflags!( } ); +/// Change the name or location of a file +/// ([see rename(2)](http://man7.org/linux/man-pages/man2/rename.2.html)). +pub fn rename(oldpath: &P1, newpath: &P2) -> Result<()> { + let res = try!(try!(oldpath.with_nix_path(|old| + newpath.with_nix_path(|new| + unsafe { + libc::rename(old.as_ptr() as *const c_char, new.as_ptr() as *const c_char) + } + ) + ))); + + Errno::result(res).map(drop) +} + +/// Change the name or location of a file +/// ([see renameat(2)](http://man7.org/linux/man-pages/man2/renameat.2.html)). +pub fn renameat(olddirfd: RawFd, oldpath: &P1, newdirfd: RawFd, newpath: &P2) -> Result<()> { + let res = try!(try!(oldpath.with_nix_path(|old| + newpath.with_nix_path(|new| + unsafe { + libc::renameat(olddirfd, old.as_ptr() as *const c_char, + newdirfd, new.as_ptr() as *const c_char) + } + ) + ))); + + Errno::result(res).map(drop) +} + pub enum FcntlArg<'a> { F_DUPFD(RawFd), F_DUPFD_CLOEXEC(RawFd), diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 43bfc09162..533278ed97 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -1,8 +1,9 @@ -use nix::fcntl::{openat, open, OFlag, O_RDONLY, readlink, readlinkat}; +use nix::fcntl::{openat, open, OFlag, O_RDONLY, readlink, readlinkat, rename, renameat}; use nix::sys::stat::Mode; use nix::unistd::{close, read}; use tempdir::TempDir; use tempfile::NamedTempFile; +use std::fs::File; use std::io::prelude::*; use std::os::unix::fs; @@ -47,6 +48,24 @@ fn test_readlink() { src.to_str().unwrap()); } +#[test] +fn test_rename() { + let tempdir = TempDir::new("nix-test_rename") + .unwrap_or_else(|e| panic!("tempdir failed: {}", e)); + let src = tempdir.path().join("a"); + let dst = tempdir.path().join("b"); + File::create(&src).unwrap(); + + rename(&src, &dst).unwrap(); + assert!(dst.exists()); + + let dir = open(tempdir.path(), + OFlag::empty(), + Mode::empty()).unwrap(); + renameat(dir, "b", dir, "a").unwrap(); + assert!(src.exists()); +} + #[cfg(any(target_os = "linux", target_os = "android"))] mod linux_android { use std::io::prelude::*; From 3c2315ed074a51d62ff7df6e543babe98c3ffec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 23 Mar 2017 22:02:29 +0100 Subject: [PATCH 03/10] add support for `unlinkat` --- CHANGELOG.md | 2 +- src/fcntl.rs | 8 +++++--- src/unistd.rs | 13 ++++++++++++- test/test_unistd.rs | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80d1a7ae82..76550012ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/fcntl.rs b/src/fcntl.rs index d8593156d0..3a90d38436 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -11,10 +11,12 @@ use sys::uio::IoVec; // For vmsplice libc_bitflags!{ pub struct AtFlags: c_int { AT_SYMLINK_NOFOLLOW; - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "linux", target_os = "android"))] + AT_REMOVEDIR; + #[cfg(any(target_os = "linux", target_os = "android"))] AT_NO_AUTOMOUNT; - #[cfg(any(target_os = "android", target_os = "linux"))] - AT_EMPTY_PATH; + #[cfg(any(target_os = "linux", target_os = "android"))] + AT_EMPTY_PATH } } diff --git a/src/unistd.rs b/src/unistd.rs index fad51272e0..fc981a902c 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2,7 +2,7 @@ use errno; use {Errno, Error, Result, NixPath}; -use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC}; +use fcntl::{AtFlags, fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC}; use fcntl::FcntlArg::F_SETFD; use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t}; @@ -914,6 +914,17 @@ pub fn unlink(path: &P) -> Result<()> { Errno::result(res).map(drop) } +/// Delete a name and possibly the file it refers to +/// ([see unlinkat(2)](http://man7.org/linux/man-pages/man2/unlinkat.2.html)). +pub fn unlinkat(fd: RawFd, pathname: &P, flags: AtFlags) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { + libc::unlinkat(fd, cstr.as_ptr(), flags.bits()) + } + })); + Errno::result(res).map(drop) +} + #[inline] pub fn chroot(path: &P) -> Result<()> { let res = try!(path.with_nix_path(|cstr| { diff --git a/test/test_unistd.rs b/test/test_unistd.rs index adf735794e..f6b3782e35 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -1,5 +1,6 @@ extern crate tempdir; +use nix::fcntl; use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::wait::*; @@ -225,6 +226,21 @@ fn test_lseek() { close(tmpfd).unwrap(); } +#[test] +fn test_unlinkat() { + let tempdir = TempDir::new("nix-test_unlinkat").unwrap(); + let dirfd = fcntl::open(tempdir.path(), + fcntl::OFlag::empty(), + stat::Mode::empty()); + let file = tempdir.path().join("foo"); + File::create(&file).unwrap(); + + unlinkat(dirfd.unwrap(), + &file.file_name(), + fcntl::AtFlags::empty()).unwrap(); + assert!(!file.exists()); +} + #[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_lseek64() { From ba25de16a1a4e226e7791254d34cb1195b988bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 23 Mar 2017 22:03:31 +0100 Subject: [PATCH 04/10] add support for `mkdirat` --- CHANGELOG.md | 2 +- src/unistd.rs | 10 ++++++++++ test/test_unistd.rs | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76550012ed..6392be87e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/unistd.rs b/src/unistd.rs index fc981a902c..2b9e71cb54 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -441,6 +441,16 @@ pub fn mkdir(path: &P, mode: Mode) -> Result<()> { Errno::result(res).map(drop) } +/// Create a directory +/// ([see mkdirat(2)](http://man7.org/linux/man-pages/man2/mkdirat.2.html)). +pub fn mkdirat(dirfd: RawFd, pathname: &P, mode: Mode) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { libc::mkdirat(dirfd, cstr.as_ptr(), mode.bits() as mode_t) } + })); + + Errno::result(res).map(drop) +} + /// Returns the current directory as a PathBuf /// /// Err is returned if the current user doesn't have the permission to read or search a component diff --git a/test/test_unistd.rs b/test/test_unistd.rs index f6b3782e35..c9342c5ba9 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -103,6 +103,21 @@ mod linux_android { let tid: ::libc::pid_t = gettid().into(); assert!(tid > 0); } + +} +#[test] +fn test_mkdirat() { + let tempdir = TempDir::new("nix-test_mkdirat").unwrap(); + let path = tempdir.path().join("test_path"); + + let dirfd = fcntl::open(tempdir.path(), + fcntl::OFlag::empty(), + stat::Mode::empty()); + + mkdirat(dirfd.unwrap(), + &path.file_name(), + stat::Mode::empty()).unwrap(); + assert!(path.exists()); } macro_rules! execve_test_factory( From 3d56d3a983bc39ced7621ddef269977f3a6ec6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 24 Mar 2017 08:16:49 +0100 Subject: [PATCH 05/10] add support for `link|linkat` --- CHANGELOG.md | 2 +- src/unistd.rs | 30 ++++++++++++++++++++++++++++++ test/test_unistd.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6392be87e4..7c74930d44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/unistd.rs b/src/unistd.rs index 2b9e71cb54..fca0db6ac2 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -822,6 +822,36 @@ pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result(oldpath: &P1, newpath: &P2) -> Result<()> { + let res = try!(try!(oldpath.with_nix_path(|old| + newpath.with_nix_path(|new| + unsafe { + libc::link(old.as_ptr() as *const c_char, new.as_ptr() as *const c_char) + } + ) + ))); + + Errno::result(res).map(drop) +} + +/// Call the link function to create a link to a file +/// ([see linkat(2)](http://man7.org/linux/man-pages/man2/linkat.2.html)). +pub fn linkat(olddirfd: RawFd, oldpath: &P1, + newdirfd: RawFd, newpath: &P2, flags: AtFlags) -> Result<()> { + let res = try!(try!(oldpath.with_nix_path(|old| + newpath.with_nix_path(|new| + unsafe { + libc::linkat(olddirfd, old.as_ptr() as *const c_char, + newdirfd, new.as_ptr() as *const c_char, flags.bits()) + } + ) + ))); + + Errno::result(res).map(drop) +} + pub fn pipe() -> Result<(RawFd, RawFd)> { unsafe { let mut fds: [c_int; 2] = mem::uninitialized(); diff --git a/test/test_unistd.rs b/test/test_unistd.rs index c9342c5ba9..bcdd3090b8 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -281,6 +281,36 @@ fn test_fpathconf_limited() { assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0); } +#[test] +fn test_linkat() { + let tempdir = TempDir::new("nix-test_linkat").unwrap(); + let src = tempdir.path().join("foo"); + let dst = tempdir.path().join("bar"); + File::create(&src).unwrap(); + + let dirfd = fcntl::open(tempdir.path(), + fcntl::OFlag::empty(), + stat::Mode::empty()); + linkat(dirfd.unwrap(), + &src.file_name(), + dirfd.unwrap(), + &dst.file_name(), + fcntl::AtFlags::empty()).unwrap(); + assert!(dst.exists()); +} + +#[test] +fn test_link() { + let tempdir = TempDir::new("nix-test_link").unwrap(); + let src = tempdir.path().join("foo"); + let dst = tempdir.path().join("bar"); + File::create(&src).unwrap(); + + link(&src, &dst).unwrap(); + assert!(dst.exists()); +} + + #[test] fn test_pathconf_limited() { // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test From 7925ab8412039ea956047c6ab8dc491dbc5bd6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 24 Mar 2017 19:06:50 +0100 Subject: [PATCH 06/10] add support for `symlink|symlinkat` --- CHANGELOG.md | 2 +- src/unistd.rs | 33 +++++++++++++++++++++++++++++++++ test/test_fcntl.rs | 23 ++++++++++++++++------- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c74930d44..f77a04b9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat, symlink, symlinkat}` ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/unistd.rs b/src/unistd.rs index fca0db6ac2..651fad45b6 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -852,6 +852,39 @@ pub fn linkat(olddirfd: RawFd, oldpa Errno::result(res).map(drop) } +/// Make a new name for a file +/// ([see symlink(2)](http://man7.org/linux/man-pages/man2/symlink.2.html)). +pub fn symlink(target: &P1, + linkpath: &P2) -> Result<()> { + let res = try!(try!(target.with_nix_path(|t| + linkpath.with_nix_path(|l| + unsafe { + libc::symlink(t.as_ptr() as *const c_char, l.as_ptr() as *const c_char) + } + ) + ))); + + Errno::result(res).map(drop) +} + +/// Make a new name for a file +/// ([see symlinkat(2)](http://man7.org/linux/man-pages/man2/symlinkat.2.html)). +pub fn symlinkat(target: &P1, + newdirfd: RawFd, + linkpath: &P2) -> Result<()> { + let res = try!(try!(target.with_nix_path(|t| + linkpath.with_nix_path(|l| + unsafe { + libc::symlinkat(t.as_ptr() as *const c_char, + newdirfd, + l.as_ptr() as *const c_char) + } + ) + ))); + + Errno::result(res).map(drop) +} + pub fn pipe() -> Result<(RawFd, RawFd)> { unsafe { let mut fds: [c_int; 2] = mem::uninitialized(); diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 533278ed97..9bff5d5850 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -1,11 +1,10 @@ use nix::fcntl::{openat, open, OFlag, O_RDONLY, readlink, readlinkat, rename, renameat}; use nix::sys::stat::Mode; -use nix::unistd::{close, read}; +use nix::unistd::{close, read, symlink, symlinkat}; use tempdir::TempDir; use tempfile::NamedTempFile; use std::fs::File; use std::io::prelude::*; -use std::os::unix::fs; #[test] fn test_openat() { @@ -31,19 +30,29 @@ fn test_openat() { #[test] fn test_readlink() { - let tempdir = TempDir::new("nix-test_readdir") + let tempdir = TempDir::new("nix-test_readdlink") + .unwrap_or_else(|e| panic!("tempdir failed: {}", e)); + let src = tempdir.path().join("a"); + let dst = tempdir.path().join("b"); + symlink(src.as_path(), dst.as_path()).unwrap(); + + let mut buf = vec![0; src.to_str().unwrap().len() + 1]; + assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(), + src.to_str().unwrap()); +} + +#[test] +fn test_readlinkat() { + let tempdir = TempDir::new("nix-test_readlinkat") .unwrap_or_else(|e| panic!("tempdir failed: {}", e)); let src = tempdir.path().join("a"); let dst = tempdir.path().join("b"); - println!("a: {:?}, b: {:?}", &src, &dst); - fs::symlink(&src.as_path(), &dst.as_path()).unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + symlinkat(src.as_path(), dirfd, dst.file_name().unwrap()).unwrap(); let mut buf = vec![0; src.to_str().unwrap().len() + 1]; - assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(), - src.to_str().unwrap()); assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(), src.to_str().unwrap()); } From 4ecc4a11a0e4ce124de16e8cfa7b71a7690c9ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 26 Mar 2017 12:40:24 +0200 Subject: [PATCH 07/10] add support for `access/faccessat` --- CHANGELOG.md | 4 ++-- src/unistd.rs | 27 +++++++++++++++++++++++++++ test/test_unistd.rs | 26 ++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f77a04b9c5..e8394ff3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,8 +72,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#591](https://github.com/nix-rust/nix/pull/591) - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat, symlink, symlinkat}` - ([#551](https://github.com/nix-rust/nix/pull/551)) +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat, symlink, symlinkat, access, faccessat}` + ([#552](https://github.com/nix-rust/nix/pull/552), [#561](https://github.com/nix-rust/nix/pull/561)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) - Added `nix::ptr::openpty` diff --git a/src/unistd.rs b/src/unistd.rs index 651fad45b6..74a3917fbe 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -168,6 +168,15 @@ impl ForkResult { } } +libc_bitflags!{ + pub struct AccessMode: c_int { + R_OK; + W_OK; + X_OK; + F_OK + } +} + /// Create a new child process duplicating the parent process ([see /// fork(2)](http://man7.org/linux/man-pages/man2/fork.2.html)). /// @@ -998,6 +1007,24 @@ pub fn unlinkat(fd: RawFd, pathname: &P, flags: AtFlags) -> Errno::result(res).map(drop) } +/// Check user's permissions for a file +/// ([see access(2)](http://man7.org/linux/man-pages/man2/access.2.html)). +pub fn access(pathname: &P, mode: AccessMode) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { libc::access(cstr.as_ptr(), mode.bits()) } + })); + Errno::result(res).map(drop) +} + +/// Check user's permissions for a file +/// ([see faccessat(2)](http://man7.org/linux/man-pages/man2/faccessat.2.html)). +pub fn faccessat(dirfd: RawFd, pathname: &P, mode: AccessMode, flags: AtFlags) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { libc::faccessat(dirfd, cstr.as_ptr(), mode.bits(), flags.bits()) } + })); + Errno::result(res).map(drop) +} + #[inline] pub fn chroot(path: &P) -> Result<()> { let res = try!(path.with_nix_path(|cstr| { diff --git a/test/test_unistd.rs b/test/test_unistd.rs index bcdd3090b8..842a621335 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -111,15 +111,33 @@ fn test_mkdirat() { let path = tempdir.path().join("test_path"); let dirfd = fcntl::open(tempdir.path(), - fcntl::OFlag::empty(), - stat::Mode::empty()); + fcntl::OFlag::empty(), + stat::Mode::empty()); mkdirat(dirfd.unwrap(), - &path.file_name(), - stat::Mode::empty()).unwrap(); + &path.file_name(), + stat::Mode::empty()).unwrap(); assert!(path.exists()); } +#[test] +fn test_access() { + let tempdir = TempDir::new("nix-test_mkdirat").unwrap(); + + let dirfd = fcntl::open(tempdir.path().parent().unwrap(), + fcntl::OFlag::empty(), + stat::Mode::empty()); + + // if succeed, permissions are or ok + access(tempdir.path(), R_OK | X_OK | W_OK).unwrap(); + + faccessat(dirfd.unwrap(), + &tempdir.path().file_name(), + R_OK | X_OK | W_OK, + fcntl::AtFlags::empty()).unwrap(); + +} + macro_rules! execve_test_factory( ($test_name:ident, $syscall:ident, $exe: expr) => ( #[test] From df45fe7b6107b7429a9b44f2f2b64c230f25301f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 29 Mar 2017 22:32:22 +0200 Subject: [PATCH 08/10] add support for `chmod/fchmod/fchmodat` --- src/sys/stat.rs | 32 ++++++++++++++++++++++++++++++++ test/test_stat.rs | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/sys/stat.rs b/src/sys/stat.rs index b24e93783d..9f73566bf3 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -134,3 +134,35 @@ pub fn fstatat(dirfd: RawFd, pathname: &P, f: AtFlags) -> R Ok(dst) } +/// Change permissions of a file +/// ([see chmod(2)](http://man7.org/linux/man-pages/man2/chmod.2.html)). +pub fn chmod(pathname: &P, mode: Mode) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { libc::chmod(cstr.as_ptr(), mode.bits()) } + })); + + Errno::result(res).map(drop) +} + +/// Change permissions of a file +/// ([see fchmod(2)](http://man7.org/linux/man-pages/man2/fchmod.2.html)). +pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { + let res = unsafe { libc::fchmod(fd, mode.bits()) }; + + Errno::result(res).map(drop) +} + +/// Change permissions of a file +/// ([see fchmodat(2)](http://man7.org/linux/man-pages/man2/fchmodat.2.html)). +pub fn fchmodat(dirfd: RawFd, pathname: &P, mode: Mode, flags: AtFlags) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { + libc::fchmodat(dirfd, + cstr.as_ptr(), + mode.bits(), + flags.bits()) + } + })); + + Errno::result(res).map(drop) +} diff --git a/test/test_stat.rs b/test/test_stat.rs index 765d4fa191..8eb56cadfc 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -1,14 +1,14 @@ use std::fs::File; -use std::os::unix::fs::symlink; +use std::os::unix::fs::{symlink, PermissionsExt}; use std::os::unix::prelude::AsRawFd; use libc::{S_IFMT, S_IFLNK}; use nix::fcntl; -use nix::sys::stat::{self, stat, fstat, lstat}; -use nix::sys::stat::FileStat; +use nix::sys::stat::*; use nix::Result; use tempdir::TempDir; +use tempfile::NamedTempFile; #[allow(unused_comparisons)] // uid and gid are signed on Windows, but not on other platforms. This function @@ -81,11 +81,11 @@ fn test_fstatat() { File::create(&filename).unwrap(); let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), - stat::Mode::empty()); + Mode::empty()); - let result = stat::fstatat(dirfd.unwrap(), - &filename, - fcntl::AtFlags::empty()); + let result = fstatat(dirfd.unwrap(), + &filename, + fcntl::AtFlags::empty()); assert_stat_results(result); } @@ -110,3 +110,30 @@ fn test_stat_fstat_lstat() { let fstat_result = fstat(link.as_raw_fd()); assert_stat_results(fstat_result); } + +fn assert_mode(f: &NamedTempFile, mode: u32) { + assert_eq!(f.metadata().unwrap().permissions().mode(), + mode); +} + +#[test] +fn test_chmod() { + let tempfile = NamedTempFile::new().unwrap(); + chmod(tempfile.path(), + Mode::from_bits(0o755).unwrap()).unwrap(); + assert_mode(&tempfile, 0o755); + + fchmod(tempfile.as_raw_fd(), + Mode::from_bits(0o644).unwrap()).unwrap(); + assert_mode(&tempfile, 0o644); + + let parent_dir = tempfile.path().parent().unwrap(); + let dirfd = fcntl::open(parent_dir, + fcntl::OFlag::empty(), + Mode::empty()).unwrap(); + fchmodat(dirfd, + tempfile.path().file_name().unwrap(), + Mode::from_bits(0o600).unwrap(), + fcntl::AtFlags::empty()).unwrap(); + assert_mode(&tempfile, 0o600); +} From ad170f96b87564429d621f84c1a2fbdd4aebc9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 29 Mar 2017 22:34:56 +0200 Subject: [PATCH 09/10] add support for `chown/lchown/fchown/fchownat` --- CHANGELOG.md | 2 ++ src/unistd.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8394ff3c5..6adcc7b24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#582](https://github.com/nix-rust/nix/pull/582) - Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat, symlink, symlinkat, access, faccessat}` ([#552](https://github.com/nix-rust/nix/pull/552), [#561](https://github.com/nix-rust/nix/pull/561)) +- Added `nix::stat::{chmod, fchmod, fchmodat}` ([#561](https://github.com/nix-rust/nix/pull/561)) +- Added `nix::unistd::{chown, lchown, fchown, fchownat}` ([#561](https://github.com/nix-rust/nix/pull/561)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) - Added `nix::ptr::openpty` diff --git a/src/unistd.rs b/src/unistd.rs index 74a3917fbe..2afb2afa5b 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -511,6 +511,13 @@ pub fn getcwd() -> Result { } } +// According to the POSIX, -1 is used to indicate that +// owner and group, respectively, are not to be changed. Since uid_t and +// gid_t are unsigned types, we use wrapping_sub to get '-1'. +fn optional_ownership(val: Option) -> u32 { + val.unwrap_or(0).wrapping_sub(1) +} + /// Change the ownership of the file at `path` to be owned by the specified /// `owner` (user) and `group` (see /// [chown(2)](http://man7.org/linux/man-pages/man2/lchown.2.html)). @@ -525,12 +532,57 @@ pub fn getcwd() -> Result { #[inline] pub fn chown(path: &P, owner: Option, group: Option) -> Result<()> { let res = try!(path.with_nix_path(|cstr| { - // According to the POSIX specification, -1 is used to indicate that - // owner and group, respectively, are not to be changed. Since uid_t and - // gid_t are unsigned types, we use wrapping_sub to get '-1'. - unsafe { libc::chown(cstr.as_ptr(), - owner.map(Into::into).unwrap_or((0 as uid_t).wrapping_sub(1)), - group.map(Into::into).unwrap_or((0 as gid_t).wrapping_sub(1))) } + unsafe { + libc::chown(cstr.as_ptr(), + optional_ownership(owner), + optional_ownership(group)) + } + })); + + Errno::result(res).map(drop) +} + +/// Change ownership of a file +/// (see [lchown(2)](http://man7.org/linux/man-pages/man2/lchown.2.html)). +pub fn lchown(path: &P, owner: Option, group: Option) -> Result<()> { + let res = try!(path.with_nix_path(|cstr| { + unsafe { + libc::lchown(cstr.as_ptr(), + optional_ownership(owner), + optional_ownership(group)) + } + })); + + Errno::result(res).map(drop) +} + +/// Change ownership of a file +/// (see [fchown(2)](http://man7.org/linux/man-pages/man2/fchown.2.html)). +pub fn fchown(fd: RawFd, owner: Option, group: Option) -> Result<()> { + let res = unsafe { + libc::fchown(fd, + optional_ownership(owner), + optional_ownership(group)) + }; + + Errno::result(res).map(drop) +} + +/// Change ownership of a file +/// (see [fchownat(2)](http://man7.org/linux/man-pages/man2/fchownat.2.html)). +pub fn fchownat(dirfd: RawFd, + pathname: &P, + owner: Option, + group: Option, + flags: AtFlags) -> Result<()> { + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { + libc::fchownat(dirfd, + cstr.as_ptr(), + optional_ownership(owner), + optional_ownership(group), + flags.bits()) + } })); Errno::result(res).map(drop) From 1c84ae3875f8d5a29893fc70a6ebb1d7aa6928d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 30 Mar 2017 12:32:39 +0200 Subject: [PATCH 10/10] add support for `futimens/utimensat` --- CHANGELOG.md | 2 +- src/sys/stat.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++ test/test_stat.rs | 17 +++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6adcc7b24c..20a0773faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,7 +74,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#582](https://github.com/nix-rust/nix/pull/582) - Added `nix::unistd::{openat, fstatat, readlink, readlinkat, rename, renameat, mknodat, unlinkat, mkdirat, link, linkat, symlink, symlinkat, access, faccessat}` ([#552](https://github.com/nix-rust/nix/pull/552), [#561](https://github.com/nix-rust/nix/pull/561)) -- Added `nix::stat::{chmod, fchmod, fchmodat}` ([#561](https://github.com/nix-rust/nix/pull/561)) +- Added `nix::stat::{chmod, fchmod, fchmodat, futimens, utimensat}` ([#561](https://github.com/nix-rust/nix/pull/561)) - Added `nix::unistd::{chown, lchown, fchown, fchownat}` ([#561](https://github.com/nix-rust/nix/pull/561)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 9f73566bf3..7fc98efde0 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -7,6 +7,8 @@ use libc::{self, mode_t}; use std::mem; use std::os::unix::io::RawFd; +pub use self::linux::*; + libc_bitflags!( pub struct SFlag: mode_t { S_IFIFO; @@ -166,3 +168,73 @@ pub fn fchmodat(dirfd: RawFd, pathname: &P, mode: Mode, fla Errno::result(res).map(drop) } + +#[cfg(target_os = "linux")] +mod linux { + use {Errno, Result, NixPath}; + use std::os::unix::io::RawFd; + use libc; + use fcntl::AtFlags; + use sys::time::TimeSpec; + + /// A file timestamp. + pub enum UtimeSpec { + /// File timestamp is set to the current time. + Now, + /// The corresponding file timestamp is left unchanged. + Omit, + /// File timestamp is set to value + Time(TimeSpec) + } + + impl <'a> From<&'a UtimeSpec> for libc::timespec { + fn from(time: &'a UtimeSpec) -> libc::timespec { + match time { + &UtimeSpec::Now => libc::timespec { + tv_sec: 0, + tv_nsec: libc::UTIME_NOW, + }, + &UtimeSpec::Omit => libc::timespec { + tv_sec: 0, + tv_nsec: libc::UTIME_OMIT, + }, + &UtimeSpec::Time(spec) => *spec.as_ref() + } + } + } + + /// Change file timestamps with nanosecond precision + /// (see [utimensat(2)](http://man7.org/linux/man-pages/man2/utimensat.2.html)). + pub fn utimensat(dirfd: RawFd, + pathname: &P, + atime: &UtimeSpec, + mtime: &UtimeSpec, + flags: AtFlags) -> Result<()> { + let time = [atime.into(), mtime.into()]; + let res = try!(pathname.with_nix_path(|cstr| { + unsafe { + libc::utimensat(dirfd, + cstr.as_ptr(), + time.as_ptr() as *const libc::timespec, + flags.bits()) + } + })); + + Errno::result(res).map(drop) + } + + /// Change file timestamps with nanosecond precision + /// (see [futimens(2)](http://man7.org/linux/man-pages/man2/futimens.2.html)). + pub fn futimens(fd: RawFd, + atime: &UtimeSpec, + mtime: &UtimeSpec) -> Result<()> { + let time = [atime.into(), mtime.into()]; + let res = unsafe { + libc::futimens(fd, time.as_ptr() as *const libc::timespec) + }; + + Errno::result(res).map(drop) + } +} +#[cfg(not(target_os = "linux"))] +mod linux { } diff --git a/test/test_stat.rs b/test/test_stat.rs index 8eb56cadfc..606df55649 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -137,3 +137,20 @@ fn test_chmod() { fcntl::AtFlags::empty()).unwrap(); assert_mode(&tempfile, 0o600); } + +#[test] +#[cfg(target_os = "linux")] +fn test_utime() { + use std::time::UNIX_EPOCH; + use nix::sys::time::{TimeSpec, TimeValLike}; + + let tempfile = NamedTempFile::new().unwrap(); + utimensat(0, // is ignored, if pathname is absolute path + tempfile.path(), + &UtimeSpec::Time(TimeSpec::zero()), + &UtimeSpec::Time(TimeSpec::zero()), + fcntl::AtFlags::empty()).unwrap(); + let mtime = tempfile.metadata().unwrap().modified().unwrap(); + + assert_eq!(mtime, UNIX_EPOCH); +}