Skip to content

Commit

Permalink
Implement unlinkat
Browse files Browse the repository at this point in the history
This adds the unlinkat function, which is part of POSIX:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html
and widely implmented on Unix-family platforms.
  • Loading branch information
jlb6740 committed May 21, 2019
1 parent 63aec50 commit 890de05
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#1045](https://github.com/nix-rust/nix/pull/1045))
- Add `forkpty`
([#1042](https://github.com/nix-rust/nix/pull/1042))
- Add `unlinkat`
([#TBD](https://github.com/nix-rust/nix/pull/TBD))

### Changed
- `PollFd` event flags renamed to `PollFlags` ([#1024](https://github.com/nix-rust/nix/pull/1024/))
Expand Down
1 change: 1 addition & 0 deletions src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use sys::uio::IoVec; // For vmsplice

libc_bitflags!{
pub struct AtFlags: c_int {
AT_REMOVEDIR;
AT_SYMLINK_NOFOLLOW;
#[cfg(any(target_os = "android", target_os = "linux"))]
AT_NO_AUTOMOUNT;
Expand Down
34 changes: 34 additions & 0 deletions src/unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,40 @@ pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
Errno::result(res).map(drop)
}

/// Flags for `unlinkat` function.
#[derive(Clone, Copy, Debug)]
pub enum UnlinkatFlags {
RemoveDir,
NoRemoveDir,
}

/// Remove a directory entry
///
/// In the case of a relative path, the directory entry to be removed is determined relative to
/// the directory associated with the file descriptor dirfd. If flag is UnlinkatFlags::RemoveDir
/// then remove the directory entry specified by dirfd and path is performed.
///
/// # References
/// See also [unlinkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
pub fn unlinkat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
flag: UnlinkatFlags,
) -> Result<()> {
let atflag =
match flag {
UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR,
UnlinkatFlags::NoRemoveDir => AtFlags::empty(),
};
let res = path.with_nix_path(|cstr| {
unsafe {
libc::unlinkat(at_rawfd(dirfd), cstr.as_ptr(), atflag.bits() as libc::c_int)
}
})?;
Errno::result(res).map(drop)
}


#[inline]
pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
let res = path.with_nix_path(|cstr| {
Expand Down
38 changes: 36 additions & 2 deletions test/test_unistd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use nix::fcntl::{fcntl, FcntlArg, FdFlag, open, OFlag, readlink};
use nix::fcntl::{self, fcntl, FcntlArg, FdFlag, open, OFlag, readlink};
use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
Expand All @@ -7,9 +7,10 @@ use nix::sys::stat::{self, Mode, SFlag};
use nix::errno::Errno;
use std::{env, iter};
use std::ffi::CString;
use std::fs::{self, File};
use std::fs::{self, DirBuilder, File};
use std::io::Write;
use std::os::unix::prelude::*;
use std::panic;
use tempfile::{self, tempfile};
use libc::{self, _exit, off_t};

Expand Down Expand Up @@ -576,6 +577,39 @@ fn test_symlinkat() {
);
}


#[test]
fn test_unlinkat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let dirname = "foo_dir";
let filepath = tempdir.path().join(filename);
let dirpath = tempdir.path().join(dirname);

// Create file
File::create(&filepath).unwrap();

// Create dir
DirBuilder::new().recursive(true).create(&dirpath).unwrap();

// Get file descriptor for base directory
let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();

// Attempt unlink dir at relative path without proper flag
panic::set_hook(Box::new(|_info| {}));
let result = std::panic::catch_unwind(|| unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap());
assert!(result.is_err());
assert!(dirpath.exists());

// Attempt unlink dir at relative path with proper flag
unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
assert!(!dirpath.exists());

// Attempt unlink file at relative path
unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
assert!(!filepath.exists());
}

#[test]
fn test_access_not_existing() {
let tempdir = tempfile::tempdir().unwrap();
Expand Down

2 comments on commit 890de05

@jlb6740
Copy link
Owner Author

@jlb6740 jlb6740 commented on 890de05 May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunfishcode
Two questions here about patch submissions:

  1. In order to update the original patch, I did an amend locally but was forced to you -f when pushing to git hub due to complaints about my tip being behind. Is using -f the correct technique? I am not accustom to needing to use -f when amending remote patches in other git projects plus the original patch appears to now be gone. Is this the github way to update a remote patch intended for a pull request?

  2. In the changelog above, how do I know the pull ID if the pull request hasn't been created yet? Is it intended that I will update this patch again after the pull request?

@sunfishcode
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlb6740 I think github always requires -f when pushing an amended commit to a remote repo that already has the unammended commit, because it's effectively replacing the old unamended commit with the new amended one. In any case, it's fine to amend commits and do push -f on your own branches when preparing for a pull request.

For 2, you can submit the original PR using "#TBD" or so, and then amend the patch and push -f once the PR is created.

Please sign in to comment.