Skip to content

Commit

Permalink
sys/stat: add a safe wrapper for mknodat(2)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucab committed Jul 28, 2021
1 parent 8519d9f commit 004ca1b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1471](https://github.com/nix-rust/nix/pull/1471))
- Added `pthread_kill`.
(#[1472](https://github.com/nix-rust/nix/pull/1472))
- Added `mknodat`.
(#[1473](https://github.com/nix-rust/nix/pull/1473))

### Changed

Expand Down
25 changes: 21 additions & 4 deletions src/sys/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::os::unix::io::RawFd;
use crate::sys::time::{TimeSpec, TimeVal};

libc_bitflags!(
/// "File type" flags for `mknod` and related functions.
pub struct SFlag: mode_t {
S_IFIFO;
S_IFCHR;
Expand All @@ -22,6 +23,7 @@ libc_bitflags!(
);

libc_bitflags! {
/// "File mode / permissions" flags.
pub struct Mode: mode_t {
S_IRWXU;
S_IRUSR;
Expand All @@ -41,11 +43,26 @@ libc_bitflags! {
}
}

/// Create a special or ordinary file, by pathname.
pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
let res = path.with_nix_path(|cstr| {
unsafe {
libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
}
let res = path.with_nix_path(|cstr| unsafe {
libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
})?;

Errno::result(res).map(drop)
}

/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
pub fn mknodat<P: ?Sized + NixPath>(
dirfd: RawFd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
let res = 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)
Expand Down
38 changes: 38 additions & 0 deletions test/test_stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,41 @@ fn test_mkdirat_fail() {
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}

#[test]
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
fn test_mknod_family() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, lstat, mknod, mknodat, SFlag};

let file_name = "test_file";
{
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let meta = lstat(&target).unwrap();
assert!(meta.st_mode & libc::S_IFREG == libc::S_IFREG.into());
assert!(meta.st_mode & libc::S_IRWXU == libc::S_IRWXU.into());
}
{
let tempdir = tempfile::tempdir().unwrap();
let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(
target_dir.as_raw_fd(),
file_name,
SFlag::S_IFREG,
Mode::S_IRWXU,
0,
)
.unwrap();
let meta = fstatat(
target_dir.as_raw_fd(),
file_name,
AtFlags::AT_SYMLINK_NOFOLLOW,
)
.unwrap();
assert!(meta.st_mode & libc::S_IFREG == libc::S_IFREG.into());
assert!(meta.st_mode & libc::S_IRWXU == libc::S_IRWXU.into());
}
}

0 comments on commit 004ca1b

Please sign in to comment.