Skip to content

Commit

Permalink
Add fchmod, fchmodat functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
antage committed Mar 23, 2018
1 parent 0418d34 commit d315804
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#825](https://github.com/nix-rust/nix/pull/825))
- Exposed `MSG_CMSG_CLOEXEC` on *BSD.
([#825](https://github.com/nix-rust/nix/pull/825))
- Added `fchmod`, `fchmodat`.
([#857](https://github.com/nix-rust/nix/pull/857))

### Changed
- Display and Debug for SysControlAddr now includes all fields.
Expand Down
61 changes: 61 additions & 0 deletions src/sys/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,64 @@ pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> R
Ok(dst)
}

/// Change the file permission bits of the file specified by a file descriptor.
///
/// # References
///
/// [fchmod(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };

Errno::result(res).map(|_| ())
}

/// Flags for `fchmodat` function.
#[derive(Clone, Copy, Debug)]
pub enum FchmodatFlags {
FollowSymlink,
NoFollowSymlink,
}

/// Change the file permission bits.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmod(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
/// # References
///
/// [fchmodat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
pub fn fchmodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
mode: Mode,
flag: FchmodatFlags,
) -> Result<()> {
let actual_dirfd =
match dirfd {
None => libc::AT_FDCWD,
Some(fd) => fd,
};
let atflag =
match flag {
FchmodatFlags::FollowSymlink => AtFlags::empty(),
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let res = path.with_nix_path(|cstr| unsafe {
libc::fchmodat(
actual_dirfd,
cstr.as_ptr(),
mode.bits() as mode_t,
atflag.bits() as libc::c_int,
)
})?;

Errno::result(res).map(|_| ())
}
54 changes: 52 additions & 2 deletions test/test_stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ 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::{self, fchmod, fchmodat, fstat, lstat, stat};
use nix::sys::stat::{FileStat, Mode, FchmodatFlags};
use nix::unistd::chdir;
use nix::Result;
use tempdir::TempDir;

Expand Down Expand Up @@ -102,3 +103,52 @@ fn test_stat_fstat_lstat() {
let fstat_result = fstat(link.as_raw_fd());
assert_stat_results(fstat_result);
}

#[test]
fn test_fchmod() {
let tempdir = TempDir::new("nix-test_fchmod").unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();

let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(file.as_raw_fd(), mode1).unwrap();

let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits());

let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(file.as_raw_fd(), mode2).unwrap();

let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits());
}

#[test]
fn test_fchmodat() {
let tempdir = TempDir::new("nix-test_fchmodat").unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();

let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();

let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink).unwrap();

let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits());

chdir(tempdir.path()).unwrap();

let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();

let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits());
}

0 comments on commit d315804

Please sign in to comment.