From f3d9646b8aaaf84fe0a77415dee2ceab7ff7f5d8 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sun, 13 Oct 2024 12:28:05 +0000 Subject: [PATCH] mkdir Fix #122 `AT_REMOVEDIR` on RISC-V/Spike differs between MacOS and Linux: * Spike/MacOS: 0x080 * Spike/Linux: 0x200 This cannot distinguish naively, so define implicit macro. --- include/fcntl.h | 11 ++++++++++ include/sys/stat.h | 3 +++ include/unistd.h | 1 + libsrc/_wasm/unistd/mkdir.c | 38 +++++++++++++++++++++++++++++++++++ libsrc/_wasm/unistd/rmdir.c | 40 +++++++++++++++++++++++++++++++++++++ libsrc/_wasm/wasi.h | 2 ++ libsrc/tests/file_test.c | 17 ++++++++++++---- libsrc/unistd/_syscall.h | 5 +++++ libsrc/unistd/mkdir.c | 22 ++++++++++++++++++++ libsrc/unistd/mkdirat.c | 15 ++++++++++++++ libsrc/unistd/rmdir.c | 23 +++++++++++++++++++++ src/xcc/main.c | 9 +++++++++ 12 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 libsrc/_wasm/unistd/mkdir.c create mode 100644 libsrc/_wasm/unistd/rmdir.c create mode 100644 libsrc/unistd/mkdir.c create mode 100644 libsrc/unistd/mkdirat.c create mode 100644 libsrc/unistd/rmdir.c diff --git a/include/fcntl.h b/include/fcntl.h index 53c200044..906c5db3a 100644 --- a/include/fcntl.h +++ b/include/fcntl.h @@ -27,7 +27,18 @@ #define AT_SYMLINK_NOFOLLOW 0x100 #define AT_SYMLINK_FOLLOW 0x400 + +/* Flag for faccessat(2). */ #define AT_EACCESS 0x200 +/* Flag for unlinkat(2). */ +#if defined(__AT_REMOVEDIR) +#define AT_REMOVEDIR __AT_REMOVEDIR +#elif defined(__APPLE__) +#define AT_REMOVEDIR 0x080 +#else +#define AT_REMOVEDIR 0x200 +#endif + int open(const char *fn, int flag, ...); int openat(int dirfd, const char *fn, int flag, ...); diff --git a/include/sys/stat.h b/include/sys/stat.h index 12e9775ed..2251b3be6 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -101,6 +101,9 @@ struct stat { }; #endif +int mkdir(const char *pathname, mode_t mode); +int mkdirat(int dirfd, const char *pathname, mode_t mode); + int chmod(const char *pathname, mode_t mode); int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); diff --git a/include/unistd.h b/include/unistd.h index 84c5132ed..60888a2d4 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -34,6 +34,7 @@ int close(int fd); ssize_t read(int fd, void *buf, size_t size); off_t lseek(int fd, off_t offset, int whence); int unlink(const char *pathname); +int rmdir(const char *pathname); char *getcwd(char *buffer, size_t size); int brk(void *addr); diff --git a/libsrc/_wasm/unistd/mkdir.c b/libsrc/_wasm/unistd/mkdir.c new file mode 100644 index 000000000..83cd9d13c --- /dev/null +++ b/libsrc/_wasm/unistd/mkdir.c @@ -0,0 +1,38 @@ +#include "sys/stat.h" +#include "string.h" // memset, strncmp +#include "../wasi.h" + +extern int __max_preopen_fd; + +int mkdir(const char *fn, mode_t mode) { + size_t fnlen = strlen(fn); + for (int base_fd = 3; base_fd < __max_preopen_fd; ++base_fd) { + Prestat prestat; + fd_prestat_get(base_fd, &prestat); + size_t l = prestat.u.dir.pr_name_len; // Includes '\0' or not, depending on the environment, + char buf[256]; + fd_prestat_dir_name(base_fd, buf, l); + buf[l] = '\0'; + + if ((*fn == '/' && *buf != '/') || + (*fn != '/' && strcmp(buf, ".") != 0)) + continue; + + const char *fn2 = fn; + size_t fnlen2 = fnlen; + + if (strncmp(fn, buf, l) == 0 && fn[l] == '/') { + fn2 = fn + (l + 1); + fnlen2 = fnlen - (l + 1); + } else if (l == 1 && *buf == '/' && *fn == '/') { + fn2 = fn + 1; + fnlen2 = fnlen - 1; + } + + Filestat fs; + uint32_t result = path_create_directory(base_fd, fn2, fnlen2); + if (result == 0) + return 0; + } + return -1; +} diff --git a/libsrc/_wasm/unistd/rmdir.c b/libsrc/_wasm/unistd/rmdir.c new file mode 100644 index 000000000..57d714dff --- /dev/null +++ b/libsrc/_wasm/unistd/rmdir.c @@ -0,0 +1,40 @@ +#include "unistd.h" +#include "errno.h" +#include "string.h" +#include "../wasi.h" + +extern int __max_preopen_fd; + +int rmdir(const char *fn) { + // Search from preopens + size_t fnlen = strlen(fn); + for (int base_fd = 3; base_fd < __max_preopen_fd; ++base_fd) { + Prestat prestat; + if (fd_prestat_get(base_fd, &prestat) != 0) + break; + + size_t l = prestat.u.dir.pr_name_len; // Includes '\0' or not, depending on the environment, + char buf[256]; + fd_prestat_dir_name(base_fd, buf, l); + buf[l] = '\0'; + + if ((*fn == '/' && *buf != '/') || + (*fn != '/' && strcmp(buf, ".") != 0)) + continue; + + const char *fn2 = fn; + size_t fnlen2 = fnlen; + + if (strncmp(fn, buf, l) == 0 && fn[l] == '/') { + fn2 = fn + (l + 1); + fnlen2 = fnlen - (l + 1); + } else if (l == 1 && *buf == '/' && *fn == '/') { + fn2 = fn + 1; + fnlen2 = fnlen - 1; + } + int result = path_remove_directory(base_fd, fn2, fnlen2); + if (result == 0) + return 0; + } + return -1; +} diff --git a/libsrc/_wasm/wasi.h b/libsrc/_wasm/wasi.h index 027a0e0e8..46655e3c4 100644 --- a/libsrc/_wasm/wasi.h +++ b/libsrc/_wasm/wasi.h @@ -110,6 +110,8 @@ WASI_MODULE int path_open(int fd, int dirflags, const char *path, size_t path_le uint64_t fs_rights_base, uint64_t fs_rights_inherting, uint16_t fdflags, uint32_t *opend_fd); WASI_MODULE int path_unlink_file(int fd, const char *path, size_t path_len); +WASI_MODULE int path_create_directory(int fd, const char *path, size_t path_len); +WASI_MODULE int path_remove_directory(int fd, const char *path, size_t path_len); WASI_MODULE int path_filestat_get(int fd, int flags, const char *path, size_t path_len, Filestat *out); WASI_MODULE int fd_read(int fd, const void *iov, int count, size_t *out); diff --git a/libsrc/tests/file_test.c b/libsrc/tests/file_test.c index 5887d7d14..c0b19df8f 100644 --- a/libsrc/tests/file_test.c +++ b/libsrc/tests/file_test.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -44,6 +45,17 @@ TEST(basic_access) { // remove EXPECT_EQ(0, remove(fn)); + + // mkdir + { + const char dn[] = "tmp_ddd"; + int r; + EXPECT_EQ(0, r = mkdir(dn, 0755)); + if (r == 0) { + // rmdir + EXPECT_EQ(0, rmdir(dn)); + } + } } TEST(stat) { @@ -63,13 +75,11 @@ TEST(stat) { EXPECT_FALSE(S_ISDIR(st.st_mode)); } - // remove(fn); + remove(fn); } // Test directory. - // TODO: Add `mkdir` const char dn[] = "tests"; - // EXPECT_EQ(0, mkdir(dn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); { struct stat st; int r; @@ -79,7 +89,6 @@ TEST(stat) { EXPECT_FALSE(S_ISREG(st.st_mode)); } } - // EXPECT_EQ(0, rmdir()); } TEST(ungetc) { diff --git a/libsrc/unistd/_syscall.h b/libsrc/unistd/_syscall.h index 47abb8f17..80176c447 100644 --- a/libsrc/unistd/_syscall.h +++ b/libsrc/unistd/_syscall.h @@ -35,10 +35,13 @@ #define __NR_kill 62 #define __NR_getcwd 79 #define __NR_chdir 80 +#define __NR_mkdir 83 +#define __NR_rmdir 84 #define __NR_unlink 87 #define __NR_chmod 90 #define __NR_time 201 #define __NR_clock_gettime 228 +#define __NR_mkdirat 258 #define __NR_newfstatat 262 #elif defined(__aarch64__) @@ -77,6 +80,7 @@ #define __NR_unlinkat 35 #define __NR_fchmodat 53 #define __NR_clock_gettime 113 +#define __NR_mkdirat 34 #define __NR_newfstatat 79 #elif defined(__riscv) @@ -107,6 +111,7 @@ #define __NR_wait4 260 #define __NR_fstat 80 #define __NR_fstatat 79 +#define __NR_mkdirat 34 #define __NR_unlinkat 35 #else diff --git a/libsrc/unistd/mkdir.c b/libsrc/unistd/mkdir.c new file mode 100644 index 000000000..b12a2e5d2 --- /dev/null +++ b/libsrc/unistd/mkdir.c @@ -0,0 +1,22 @@ +#include "sys/stat.h" +#include "_syscall.h" +#include "errno.h" + +#if defined(__NR_mkdir) +int mkdir(const char *pathname, mode_t mode) { + int ret; + SYSCALL_RET(__NR_mkdir, ret); + if (ret < 0) { + errno = -ret; + ret = -1; + } + return ret; +} + +#elif defined(__NR_mkdirat) +#include "fcntl.h" + +int mkdir(const char *pathname, mode_t mode) { + return mkdirat(AT_FDCWD, pathname, mode); +} +#endif diff --git a/libsrc/unistd/mkdirat.c b/libsrc/unistd/mkdirat.c new file mode 100644 index 000000000..c7ec89b0c --- /dev/null +++ b/libsrc/unistd/mkdirat.c @@ -0,0 +1,15 @@ +#include "sys/stat.h" +#include "_syscall.h" +#include "errno.h" + +#if defined(__NR_mkdirat) +int mkdirat(int dirfd, const char *pathname, mode_t mode) { + int ret; + SYSCALL_RET(__NR_mkdirat, ret); + if (ret < 0) { + errno = -ret; + ret = -1; + } + return ret; +} +#endif diff --git a/libsrc/unistd/rmdir.c b/libsrc/unistd/rmdir.c new file mode 100644 index 000000000..1691247dc --- /dev/null +++ b/libsrc/unistd/rmdir.c @@ -0,0 +1,23 @@ +#include "sys/stat.h" +#include "_syscall.h" +#include "errno.h" + +#if defined(__NR_rmdir) +int rmdir(const char *pathname) { + int ret; + SYSCALL_RET(__NR_rmdir, ret); + if (ret < 0) { + errno = -ret; + ret = -1; + } + return ret; +} + +#elif defined(__NR_unlinkat) +#include "unistd.h" +#include "fcntl.h" + +int rmdir(const char *pathname) { + return unlinkat(AT_FDCWD, pathname, AT_REMOVEDIR); +} +#endif diff --git a/src/xcc/main.c b/src/xcc/main.c index e24683e24..d71313bae 100644 --- a/src/xcc/main.c +++ b/src/xcc/main.c @@ -16,6 +16,11 @@ #include "util.h" + // Hack: AT_REMOVEDIR defined in riscv-gnu-toolchain differs on MacOS and Linux? +#if defined(__APPLE__) && !defined(__AT_REMOVEDIR) +#define __AT_REMOVEDIR 0x080 +#endif + static pid_t fork1(void) { pid_t pid = fork(); if (pid < 0) @@ -635,6 +640,10 @@ static void append_predefined_macros(Vector *cpp_cmd) { #if defined(__NO_WCHAR) vec_push(cpp_cmd, "-D__NO_WCHAR"); #endif + +#if defined(__AT_REMOVEDIR) + vec_push(cpp_cmd, "-D__AT_REMOVEDIR=" STR(__AT_REMOVEDIR)); +#endif } int main(int argc, char *argv[]) {