Skip to content

Commit 6e2158b

Browse files
committed
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
1 parent 8519d9f commit 6e2158b

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1616
(#[1471](https://github.com/nix-rust/nix/pull/1471))
1717
- Added `pthread_kill`.
1818
(#[1472](https://github.com/nix-rust/nix/pull/1472))
19+
- Added `mknodat`.
20+
(#[1473](https://github.com/nix-rust/nix/pull/1473))
1921

2022
### Changed
2123

src/sys/stat.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::os::unix::io::RawFd;
99
use crate::sys::time::{TimeSpec, TimeVal};
1010

1111
libc_bitflags!(
12+
/// "File type" flags for `mknod` and related functions.
1213
pub struct SFlag: mode_t {
1314
S_IFIFO;
1415
S_IFCHR;
@@ -22,6 +23,7 @@ libc_bitflags!(
2223
);
2324

2425
libc_bitflags! {
26+
/// "File mode / permissions" flags.
2527
pub struct Mode: mode_t {
2628
S_IRWXU;
2729
S_IRUSR;
@@ -41,11 +43,26 @@ libc_bitflags! {
4143
}
4244
}
4345

46+
/// Create a special or ordinary file, by pathname.
4447
pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
45-
let res = path.with_nix_path(|cstr| {
46-
unsafe {
47-
libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
48-
}
48+
let res = path.with_nix_path(|cstr| unsafe {
49+
libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
50+
})?;
51+
52+
Errno::result(res).map(drop)
53+
}
54+
55+
/// Create a special or ordinary file, relative to a given directory.
56+
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
57+
pub fn mknodat<P: ?Sized + NixPath>(
58+
dirfd: RawFd,
59+
path: &P,
60+
kind: SFlag,
61+
perm: Mode,
62+
dev: dev_t,
63+
) -> Result<()> {
64+
let res = path.with_nix_path(|cstr| unsafe {
65+
libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
4966
})?;
5067

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

test/test_stat.rs

+42
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,45 @@ fn test_mkdirat_fail() {
306306
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
307307
assert_eq!(result, Errno::ENOTDIR);
308308
}
309+
310+
#[test]
311+
#[cfg(not(any(target_os = "freebsd",
312+
target_os = "ios",
313+
target_os = "macos",
314+
target_os = "redox")))]
315+
fn test_mknod_family() {
316+
use fcntl::{AtFlags, OFlag};
317+
use nix::dir::Dir;
318+
use stat::{fstatat, lstat, mknod, mknodat, SFlag};
319+
320+
let file_name = "test_file";
321+
{
322+
let tempdir = tempfile::tempdir().unwrap();
323+
let target = tempdir.path().join(file_name);
324+
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
325+
let mode = lstat(&target).unwrap().st_mode as mode_t;
326+
assert!(mode & libc::S_IFREG == libc::S_IFREG);
327+
assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
328+
}
329+
{
330+
let tempdir = tempfile::tempdir().unwrap();
331+
let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
332+
mknodat(
333+
target_dir.as_raw_fd(),
334+
file_name,
335+
SFlag::S_IFREG,
336+
Mode::S_IRWXU,
337+
0,
338+
)
339+
.unwrap();
340+
let mode = fstatat(
341+
target_dir.as_raw_fd(),
342+
file_name,
343+
AtFlags::AT_SYMLINK_NOFOLLOW,
344+
)
345+
.unwrap()
346+
.st_mode as mode_t;
347+
assert!(mode & libc::S_IFREG == libc::S_IFREG);
348+
assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
349+
}
350+
}

0 commit comments

Comments
 (0)