Skip to content

Commit f926043

Browse files
bors[bot]kevinwern
andcommitted
Merge #1084
1084: sys/stat: implement mkdirat r=asomers a=kevinwern See: https://github.com/CraneStation/wasi-common/issues/16 https://linux.die.net/man/2/mkdirat My first contribution to this repo. Tests were probably overkill... Also, out of curiosity, is there any reason why `mkdir` is located in `unistd`? The documentation I read mentioned the function definition also being located in `sys/stat.h`. Co-authored-by: Kevin Wern <[email protected]>
2 parents 582846e + c006491 commit f926043

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1111
types that support them. ([#1035](https://github.com/nix-rust/nix/pull/1035))
1212
- Added `copy_file_range` wrapper
1313
([#1069](https://github.com/nix-rust/nix/pull/1069))
14+
- Add `mkdirat`.
15+
([#1084](https://github.com/nix-rust/nix/pull/1084))
1416

1517
### Changed
1618
- Support for `ifaddrs` now present when building for Android.

src/sys/stat.rs

+8
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,11 @@ pub fn utimensat<P: ?Sized + NixPath>(
284284

285285
Errno::result(res).map(drop)
286286
}
287+
288+
pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
289+
let res = path.with_nix_path(|cstr| {
290+
unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
291+
})?;
292+
293+
Errno::result(res).map(drop)
294+
}

test/test_stat.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use std::fs::{self, File};
2-
use std::os::unix::fs::symlink;
2+
use std::os::unix::fs::{symlink, PermissionsExt};
33
use std::os::unix::prelude::AsRawFd;
44
use std::time::{Duration, UNIX_EPOCH};
5+
use std::path::Path;
56

67
#[cfg(not(any(target_os = "netbsd")))]
7-
use libc::{S_IFMT, S_IFLNK};
8+
use libc::{S_IFMT, S_IFLNK, mode_t};
89

9-
use nix::fcntl;
10-
use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat};
10+
use nix::{fcntl, Error};
11+
use nix::errno::{Errno};
12+
use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat, mkdirat};
1113
#[cfg(any(target_os = "linux",
1214
target_os = "haiku",
1315
target_os = "ios",
@@ -260,3 +262,35 @@ fn test_utimensat() {
260262
UtimensatFlags::FollowSymlink).unwrap();
261263
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
262264
}
265+
266+
#[test]
267+
fn test_mkdirat_success_path() {
268+
let tempdir = tempfile::tempdir().unwrap();
269+
let filename = "example_subdir";
270+
let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
271+
assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok());
272+
assert!(Path::exists(&tempdir.path().join(filename)));
273+
}
274+
275+
#[test]
276+
fn test_mkdirat_success_mode() {
277+
let expected_bits = stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
278+
let tempdir = tempfile::tempdir().unwrap();
279+
let filename = "example_subdir";
280+
let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
281+
assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok());
282+
let permissions = fs::metadata(tempdir.path().join(filename)).unwrap().permissions();
283+
let mode = permissions.mode();
284+
assert_eq!(mode as mode_t, expected_bits)
285+
}
286+
287+
#[test]
288+
fn test_mkdirat_fail() {
289+
let tempdir = tempfile::tempdir().unwrap();
290+
let not_dir_filename= "example_not_dir";
291+
let filename = "example_subdir_dir";
292+
let dirfd = fcntl::open(&tempdir.path().join(not_dir_filename), fcntl::OFlag::O_CREAT,
293+
stat::Mode::empty()).unwrap();
294+
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
295+
assert_eq!(result, Error::Sys(Errno::ENOTDIR));
296+
}

0 commit comments

Comments
 (0)