From 6e2158bc7126510890351d67400d53de9fa9629e Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Thu, 22 Jul 2021 07:49:59 +0000 Subject: sys/stat: add a safe wrapper for mknodat(2) This introduces a new `mknodat` helper. Ref: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknod.html --- CHANGELOG.md | 2 ++ src/sys/stat.rs | 25 +++++++++++++++++++++---- test/test_stat.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97268ef2..dce18009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 15451e78..c797576e 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -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; @@ -22,6 +23,7 @@ libc_bitflags!( ); libc_bitflags! { + /// "File mode / permissions" flags. pub struct Mode: mode_t { S_IRWXU; S_IRUSR; @@ -41,11 +43,26 @@ libc_bitflags! { } } +/// Create a special or ordinary file, by pathname. pub fn mknod(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( + 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) diff --git a/test/test_stat.rs b/test/test_stat.rs index 424371fa..42c536a8 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -306,3 +306,45 @@ 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 = "freebsd", + 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 mode = lstat(&target).unwrap().st_mode as mode_t; + assert!(mode & libc::S_IFREG == libc::S_IFREG); + assert!(mode & libc::S_IRWXU == libc::S_IRWXU); + } + { + 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 mode = fstatat( + target_dir.as_raw_fd(), + file_name, + AtFlags::AT_SYMLINK_NOFOLLOW, + ) + .unwrap() + .st_mode as mode_t; + assert!(mode & libc::S_IFREG == libc::S_IFREG); + assert!(mode & libc::S_IRWXU == libc::S_IRWXU); + } +} -- cgit v1.2.3