diff --git a/src/header/sys_stat/mod.rs b/src/header/sys_stat/mod.rs index 7d258fefbc..912b1523bb 100644 --- a/src/header/sys_stat/mod.rs +++ b/src/header/sys_stat/mod.rs @@ -117,9 +117,21 @@ pub unsafe extern "C" fn mkfifo(path: *const c_char, mode: mode_t) -> c_int { Sys::mkfifo(path, mode) } -// #[no_mangle] -pub extern "C" fn mknod(path: *const c_char, mode: mode_t, dev: dev_t) -> c_int { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn mknod(path: *const c_char, mode: mode_t, dev: dev_t) -> c_int { + let path = CStr::from_ptr(path); + Sys::mknod(path, mode, dev) +} + +#[no_mangle] +pub unsafe extern "C" fn mknodat( + dirfd: c_int, + path: *const c_char, + mode: mode_t, + dev: dev_t, +) -> c_int { + let path = CStr::from_ptr(path); + Sys::mknodat(dirfd, path, mode, dev) } #[no_mangle] diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 982be1eb28..d6f0c8870f 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -4,7 +4,7 @@ use core_io::Write; use super::{errno, types::*, Pal}; use crate::{ c_str::CStr, - header::{dirent::dirent, signal::SIGCHLD, sys_stat::S_IFIFO}, + header::{dirent::dirent, errno::EINVAL, signal::SIGCHLD, sys_stat::S_IFIFO}, }; // use header::sys_resource::rusage; use crate::header::{ @@ -357,8 +357,23 @@ impl Pal for Sys { e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }) as c_int } + fn mknodat(dir_fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> c_int { + // Note: dev_t is c_long (i64) and __kernel_dev_t is u32; So we need to cast it + // and check for overflow + let k_dev: c_uint = dev as c_uint; + if k_dev as dev_t != dev { + return e(EINVAL as usize) as c_int; + } + + e(unsafe { syscall!(MKNODAT, dir_fildes, path.as_ptr(), mode, k_dev) }) as c_int + } + + fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int { + Sys::mknodat(AT_FDCWD, path, mode, dev) + } + fn mkfifo(path: CStr, mode: mode_t) -> c_int { - e(unsafe { syscall!(MKNODAT, AT_FDCWD, path.as_ptr(), mode | S_IFIFO, 0) }) as c_int + Sys::mknod(path, mode | S_IFIFO, 0) } unsafe fn mlock(addr: *const c_void, len: usize) -> c_int { diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index a8d5fdfd88..566de802c3 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -138,6 +138,10 @@ pub trait Pal { fn mkfifo(path: CStr, mode: mode_t) -> c_int; + fn mknodat(fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> c_int; + + fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int; + unsafe fn mlock(addr: *const c_void, len: usize) -> c_int; fn mlockall(flags: c_int) -> c_int; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index cf113953eb..7d427c2816 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -10,7 +10,7 @@ use crate::{ fs::File, header::{ dirent::dirent, - errno::{EINVAL, EIO, ENOMEM, ENOSYS, EPERM, ERANGE}, + errno::{EBADR, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS, EPERM, ERANGE}, fcntl, sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE}, sys_random, @@ -187,7 +187,7 @@ impl Pal for Sys { fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int { // TODO eprintln!("relibc clock_getres({}, {:p}): not implemented", clk_id, tp); - unsafe { errno = ENOSYS }; + unsafe { errno = ENOSYS } -1 } @@ -616,11 +616,44 @@ impl Pal for Sys { } fn mkfifo(path: CStr, mode: mode_t) -> c_int { - match File::create( - path, - fcntl::O_CREAT | fcntl::O_CLOEXEC, - syscall::MODE_FIFO as mode_t | (mode & 0o777), - ) { + Sys::mknod(path, syscall::MODE_FIFO as mode_t | (mode & 0o777), 0) + } + + fn mknodat(dir_fd: c_int, path_name: CStr, mode: mode_t, dev: dev_t) -> c_int { + let mut dir_path_buf = [0; 4096]; + let res = Sys::fpath(dir_fd, &mut dir_path_buf); + if res < 0 { + return !0; + } + + let dir_path = match str::from_utf8(&dir_path_buf[..res as usize]) { + Ok(path) => path, + Err(_) => { + unsafe { errno = EBADR }; + return !0; + } + }; + + let resource_path = + match path::canonicalize_using_cwd(Some(&dir_path), &path_name.to_string_lossy()) { + Some(path) => path, + None => { + // Since parent_dir_path is resolved by fpath, it is more likely that + // the problem was with path. + unsafe { errno = ENOENT }; + return !0; + } + }; + + Sys::mknod( + CStr::borrow(&CString::new(resource_path.as_bytes()).unwrap()), + mode, + dev, + ) + } + + fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> c_int { + match File::create(path, fcntl::O_CREAT | fcntl::O_CLOEXEC, mode) { Ok(fd) => 0, Err(_) => -1, } diff --git a/tests/Makefile b/tests/Makefile index c9bd1905d1..12062689c5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -33,7 +33,7 @@ EXPECT_NAMES=\ stdio/fseek \ stdio/fwrite \ stdio/getc_unget \ - stdio/getline \ + stdio/getline \ stdio/mutex \ stdio/popen \ stdio/printf \ @@ -131,9 +131,11 @@ EXPECT_NAMES=\ wchar/wcsnrtombs \ wchar/wcswidth \ wctype/towlower \ - wctype/towupper + wctype/towupper \ + mkfifo \ + mknod \ + mknodat \ # TODO: Fix these - # mkfifo # netdb/netdb \ BUILD?=. diff --git a/tests/expected/bins_static/mkfifo.stderr b/tests/expected/bins_static/mkfifo.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mkfifo.stdout b/tests/expected/bins_static/mkfifo.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknod.stderr b/tests/expected/bins_static/mknod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknod.stdout b/tests/expected/bins_static/mknod.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknodat.stderr b/tests/expected/bins_static/mknodat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknodat.stdout b/tests/expected/bins_static/mknodat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/mkfifo.c b/tests/mkfifo.c index 6fb40ceef3..c242f05771 100644 --- a/tests/mkfifo.c +++ b/tests/mkfifo.c @@ -7,7 +7,7 @@ #include #include -int main(int argc, char** argv) { +int main(void) { char temp[] = "/tmp/stattest-XXXXXX"; const char file[] = "/mkfifo_fifo"; int len = sizeof(temp) + sizeof(file); diff --git a/tests/mknod.c b/tests/mknod.c new file mode 100644 index 0000000000..80658866ec --- /dev/null +++ b/tests/mknod.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char file[] = "/mknod"; + int len = sizeof(temp) + sizeof(file); + char* path = malloc(len * sizeof(char)); + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + if(!mktemp(temp)) { + fprintf(stderr, "Unable to create a unique dir name %s: %s\n", temp, strerror(errno)); + exit(1); + } + + path = strncat(path, temp, strlen(temp)); + path = strncat(path, file, strlen(file)); + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + if (mknod(path, S_IFREG, S_IRUSR) == -1) { + fprintf(stderr, "mknod %s: %s\n", path, strerror(errno)); + exit(1); + } + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + exit(1); + } + if (!(sb.st_mode & S_IFREG)) { + fprintf(stderr, "Expected S_IFREG flag to be set, got mode: %d\n", sb.st_mode); + exit(1); + } +} diff --git a/tests/mknodat.c b/tests/mknodat.c new file mode 100644 index 0000000000..39c2016ece --- /dev/null +++ b/tests/mknodat.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char separator[] = "/"; + const char file[] = "mknod"; // relative + int len = sizeof(temp) + sizeof(file) + sizeof(separator); + char* path = malloc(len * sizeof(char)); + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + if(!mktemp(temp)) { + fprintf(stderr, "Unable to create a unique dir name %s: %s\n", temp, strerror(errno)); + exit(1); + } + + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + + int dir_fd = open(temp, O_RDONLY); + if(dir_fd == -1) { + fprintf(stderr, "unable to open temp directory: %s with error: %s\n", temp, strerror(errno)); + exit(1); + } + + if (mknodat(dir_fd, file, S_IFREG, S_IRUSR) == -1) { + fprintf(stderr, "mknod %s: %s\n", path, strerror(errno)); + exit(1); + } + + path = strncat(path, temp, strlen(temp)); + path = strncat(path, separator, strlen(temp)); + path = strncat(path, file, strlen(file)); + + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat for %s: %s\n", path, strerror(errno)); + exit(1); + } + + if (!(sb.st_mode & S_IFREG)) { + fprintf(stderr, "Expected S_IFREG flag to be set, got mode: %d\n", sb.st_mode); + exit(1); + } +}