diff --git a/libc/calls/access.c b/libc/calls/access.c index 2f5db766..1c0dabc7 100644 --- a/libc/calls/access.c +++ b/libc/calls/access.c @@ -17,10 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" /** * Checks if effective user can access path in particular ways. @@ -31,12 +28,5 @@ * @asyncsignalsafe */ int access(const char *path, int mode) { - char16_t path16[PATH_MAX]; - if (!path) return efault(); - if (!IsWindows()) { - return faccessat$sysv(AT_FDCWD, path, mode, 0); - } else { - if (__mkntpath(path, path16) == -1) return -1; - return ntaccesscheck(path16, mode); - } + return faccessat(AT_FDCWD, path, mode, 0); } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index dd34dbdf..ec73c2b3 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -230,6 +230,7 @@ long ptrace(int, int, void *, void *); int chroot(const char *); int prctl(); int sysctl(const int *, unsigned, void *, size_t *, void *, size_t); +int fchdir(int); #define getcwd(BUF, SIZE) \ (__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \ diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index 86ec65a0..40869356 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -17,12 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" textwindows int chdir$nt(const char *path) { - int len; + int e, ms, len; char16_t path16[PATH_MAX]; if ((len = __mkntpath(path, path16)) == -1) return -1; if (path16[len - 1] != u'\\') { @@ -30,9 +32,24 @@ textwindows int chdir$nt(const char *path) { path16[len + 0] = u'\\'; path16[len + 1] = u'\0'; } - if (SetCurrentDirectory(path16)) { - return 0; - } else { - return __winerr(); + /* + * chdir() seems flaky on windows 7 + * in a similar way to rmdir() sigh + */ + for (ms = 1;; ms *= 2) { + if (SetCurrentDirectory(path16)) { + return 0; + } else { + e = GetLastError(); + if (ms <= 512 && + (e == kNtErrorFileNotFound || e == kNtErrorAccessDenied)) { + Sleep(ms); + continue; + } else { + break; + } + } } + errno = e; + return -1; } diff --git a/libc/calls/chdir.c b/libc/calls/chdir.c index 885d0f7e..0ebbf1b9 100644 --- a/libc/calls/chdir.c +++ b/libc/calls/chdir.c @@ -23,11 +23,11 @@ /** * Sets current directory. + * * @asyncsignalsafe - * @syscall + * @see fchdir() */ int chdir(const char *path) { - if (!path) return efault(); if (!IsWindows()) { return chdir$sysv(path); } else { diff --git a/libc/calls/chown.c b/libc/calls/chown.c index 432885b2..090cb6ec 100644 --- a/libc/calls/chown.c +++ b/libc/calls/chown.c @@ -32,9 +32,7 @@ * @see /etc/passwd for user ids * @see /etc/group for group ids * @asyncsignalsafe - * @syscall */ int chown(const char *pathname, uint32_t uid, uint32_t gid) { - if (!pathname) return efault(); return fchownat$sysv(AT_FDCWD, pathname, uid, gid, 0); } diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index a5068f27..d08ebafd 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -22,7 +22,6 @@ int faccessat$nt(int dirfd, const char *path, int mode, uint32_t flags) { char16_t path16[PATH_MAX]; - if (dirfd != AT_FDCWD || flags) return einval(); - if (__mkntpath(path, path16) == -1) return -1; + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; return ntaccesscheck(path16, mode); } diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 1cb94485..9e948367 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -25,12 +25,13 @@ /** * Checks if effective user can access path in particular ways. * - * @param dirfd is usually AT_FDCWD + * @param dirfd is normally AT_FDCWD but if it's an open directory and + * file is a relative path, then file is opened relative to dirfd * @param path is a filename or directory - * @param flags can be R_OK, W_OK, X_OK, F_OK + * @param mode can be R_OK, W_OK, X_OK, F_OK + * @param flags should be 0 * @return 0 if ok, or -1 and sets errno * @asyncsignalsafe - * @syscall */ int faccessat(int dirfd, const char *path, int mode, uint32_t flags) { if (!path) return efault(); diff --git a/libc/calls/fchdir-nt.c b/libc/calls/fchdir-nt.c new file mode 100644 index 00000000..740dfbcb --- /dev/null +++ b/libc/calls/fchdir-nt.c @@ -0,0 +1,41 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/nt/files.h" +#include "libc/sysv/errfuns.h" + +textwindows int fchdir$nt(int dirfd) { + uint32_t len; + char16_t dir[PATH_MAX]; + if (!__isfdkind(dirfd, kFdFile)) return ebadf(); + len = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), + kNtFileNameNormalized | kNtVolumeNameDos); + if (len + 1 + 1 > ARRAYLEN(dir)) return enametoolong(); + if (dir[len - 1] != u'\\') { + dir[len + 0] = u'\\'; + dir[len + 1] = u'\0'; + } + if (SetCurrentDirectory(dir)) { + return 0; + } else { + return __winerr(); + } +} diff --git a/libc/calls/lstat-nt.c b/libc/calls/fchdir.c similarity index 88% rename from libc/calls/lstat-nt.c rename to libc/calls/fchdir.c index 1d72d361..d44aab12 100644 --- a/libc/calls/lstat-nt.c +++ b/libc/calls/fchdir.c @@ -18,7 +18,18 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/dce.h" -textwindows int lstat$nt(const char *pathname, struct stat *st) { - return stat$nt(pathname, st); /* todo(jart) */ +/** + * Sets current directory based on file descriptor. + * + * @see open(path, O_DIRECTORY) + * @asyncsignalsafe + */ +int fchdir(int dirfd) { + if (!IsWindows()) { + return fchdir$sysv(dirfd); + } else { + return fchdir$nt(dirfd); + } } diff --git a/libc/calls/fchownat.c b/libc/calls/fchownat.c index 444f5d40..2a4fa4c9 100644 --- a/libc/calls/fchownat.c +++ b/libc/calls/fchownat.c @@ -31,10 +31,8 @@ * @see /etc/passwd for user ids * @see /etc/group for group ids * @asyncsignalsafe - * @syscall */ int fchownat(int dirfd, const char *pathname, uint32_t uid, uint32_t gid, uint32_t flags) { - /* TODO(jart): Windows? */ return fchownat$sysv(dirfd, pathname, uid, gid, flags); } diff --git a/libc/calls/stat-nt.c b/libc/calls/fstatat-nt.c similarity index 93% rename from libc/calls/stat-nt.c rename to libc/calls/fstatat-nt.c index 49436019..4aa6717f 100644 --- a/libc/calls/stat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -26,11 +26,12 @@ #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" -textwindows int stat$nt(const char *path, struct stat *st) { +textwindows int fstatat$nt(int dirfd, const char *path, struct stat *st, + uint32_t flags) { int rc; int64_t fh; uint16_t path16[PATH_MAX]; - if (__mkntpath(path, path16) == -1) return -1; + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile( path16, kNtFileReadAttributes, kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, NULL, diff --git a/libc/calls/fstatat.c b/libc/calls/fstatat.c index bbabe017..9bd871e7 100644 --- a/libc/calls/fstatat.c +++ b/libc/calls/fstatat.c @@ -16,33 +16,31 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/errno.h" #include "libc/sysv/consts/at.h" +#include "libc/zipos/zipos.internal.h" /** - * Performs stat() w/ features for threaded apps. + * Returns information about thing. * - * @param dirfd can be AT_FDCWD or an open directory descriptor, and is - * ignored if pathname is absolute + * @param dirfd is normally AT_FDCWD but if it's an open directory and + * file is a relative path, then file becomes relative to dirfd + * @param st is where result is stored * @param flags can have AT_{EMPTY_PATH,NO_AUTOMOUNT,SYMLINK_NOFOLLOW} * @return 0 on success, or -1 w/ errno + * @see S_ISDIR(st.st_mode), S_ISREG() * @asyncsignalsafe */ -int fstatat(int dirfd, const char *pathname, struct stat *st, uint32_t flags) { - int olderr = errno; - int rc = fstatat$sysv(dirfd, pathname, st, flags); - if (rc != -1) { - stat2linux(st); - } else if (errno == ENOSYS && dirfd == AT_FDCWD) { - if (!flags) { - errno = olderr; - rc = stat(pathname, st); - } else if (flags == AT_SYMLINK_NOFOLLOW) { - errno = olderr; - rc = lstat(pathname, st); - } +int fstatat(int dirfd, const char *path, struct stat *st, uint32_t flags) { + struct ZiposUri zipname; + if (weaken(__zipos_stat) && weaken(__zipos_parseuri)(path, &zipname) != -1) { + return weaken(__zipos_stat)(&zipname, st); + } else if (!IsWindows()) { + return fstatat$sysv(dirfd, path, st, flags); + } else { + return fstatat$nt(dirfd, path, st, flags); } - return rc; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 7fe639d1..4b44da8a 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -115,6 +115,7 @@ i32 execve$sysv(const char *, char *const[], char *const[]) hidden; i32 faccessat$sysv(i32, const char *, i32, u32) hidden; i32 fadvise$sysv(i32, i64, i64, i32) hidden; i32 fallocate$sysv(i64, i32, i64, i64) hidden; +i32 fchdir$sysv(i32) hidden; i32 fchmod$sysv(i32, u32) hidden; i32 fchmodat$sysv(i32, const char *, u32, u32) hidden; i32 fchown$sysv(i64, u32, u32) hidden; @@ -230,11 +231,13 @@ int dup$nt(int, int, int) hidden; int execve$nt(const char *, char *const[], char *const[]) hidden; int faccessat$nt(int, const char *, int, uint32_t) hidden; int fadvise$nt(int, u64, u64, int) hidden; +int fchdir$nt(int) hidden; int fcntl$nt(int, int, unsigned) hidden; int fdatasync$nt(int) hidden; int flock$nt(int, int) hidden; int fork$nt(void) hidden; int fstat$nt(i64, struct stat *) hidden; +int fstatat$nt(int, const char *, struct stat *, uint32_t) hidden; int ftruncate$nt(int, u64) hidden; int getpriority$nt(int) hidden; int getrusage$nt(int, struct rusage *) hidden; @@ -243,23 +246,21 @@ int kill$nt(int, int) hidden; int link$nt(const char *, const char *) hidden; int lstat$nt(const char *, struct stat *) hidden; int madvise$nt(void *, size_t, int) hidden; -int mkdir$nt(const char *, uint32_t) hidden; +int mkdirat$nt(int, const char *, uint32_t) hidden; int msync$nt(void *, size_t, int) hidden; int nanosleep$nt(const struct timespec *, struct timespec *) hidden; int pipe$nt(int[hasatleast 2], unsigned) hidden; -int rename$nt(const char *, const char *) hidden; -int rmdir$nt(const char *) hidden; +int renameat$nt(int, const char *, int, const char *) hidden; int sched_yield$nt(void) hidden; int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden; int setpriority$nt(int) hidden; -int stat$nt(const char *, struct stat *) hidden; -int symlink$nt(const char *, const char *) hidden; +int symlinkat$nt(const char *, int, const char *) hidden; int sync$nt(void) hidden; int sysinfo$nt(struct sysinfo *) hidden; int truncate$nt(const char *, u64) hidden; -int unlink$nt(const char *) hidden; +int unlinkat$nt(int, const char *, int) hidden; int utimensat$nt(int, const char *, const struct timespec *, int) hidden; -ssize_t open$nt(const char *, u32, i32) nodiscard hidden; +ssize_t open$nt(int, const char *, u32, i32) nodiscard hidden; ssize_t read$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; ssize_t write$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; @@ -279,6 +280,7 @@ int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; int64_t __winerr(void) nocallback privileged; int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden; int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden; +int __mkntpathat(int, const char *, int, char16_t[PATH_MAX]) hidden; unsigned __wincrash$nt(struct NtExceptionPointers *); /*───────────────────────────────────────────────────────────────────────────│─╗ diff --git a/libc/calls/lchown.c b/libc/calls/lchown.c index 22144709..61a98b82 100644 --- a/libc/calls/lchown.c +++ b/libc/calls/lchown.c @@ -31,6 +31,5 @@ * @see /etc/group for group ids */ int lchown(const char *pathname, uint32_t uid, uint32_t gid) { - /* TODO(jart): Windows? */ return fchownat$sysv(AT_FDCWD, pathname, uid, gid, AT_SYMLINK_NOFOLLOW); } diff --git a/libc/calls/lstat.c b/libc/calls/lstat.c index 027090c4..9889bc41 100644 --- a/libc/calls/lstat.c +++ b/libc/calls/lstat.c @@ -16,8 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/calls/internal.h" #include "libc/calls/calls.h" #include "libc/sysv/consts/at.h" @@ -26,9 +24,5 @@ * @asyncsignalsafe */ int lstat(const char *pathname, struct stat *st) { - if (!IsWindows()) { - return fstatat$sysv(AT_FDCWD, pathname, st, AT_SYMLINK_NOFOLLOW); - } else { - return lstat$nt(pathname, st); - } + return fstatat(AT_FDCWD, pathname, st, AT_SYMLINK_NOFOLLOW); } diff --git a/libc/calls/mkdir.c b/libc/calls/mkdir.c index d07c5af5..cda89a91 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -41,10 +41,5 @@ * @see makedirs() */ int mkdir(const char *path, unsigned mode) { - if (!path) return efault(); - if (!IsWindows()) { - return mkdirat$sysv(AT_FDCWD, path, mode); - } else { - return mkdir$nt(path, mode); - } + return mkdirat(AT_FDCWD, path, mode); } diff --git a/libc/calls/mkdir-nt.c b/libc/calls/mkdirat-nt.c similarity index 95% rename from libc/calls/mkdir-nt.c rename to libc/calls/mkdirat-nt.c index 73fd2d13..03b25ffb 100644 --- a/libc/calls/mkdir-nt.c +++ b/libc/calls/mkdirat-nt.c @@ -36,10 +36,10 @@ static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) { return false; } -textwindows int mkdir$nt(const char *path, uint32_t mode) { +textwindows int mkdirat$nt(int dirfd, const char *path, uint32_t mode) { int e; char16_t *p, path16[PATH_MAX]; - if (__mkntpath(path, path16) == -1) return -1; + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (CreateDirectory(path16, NULL)) return 0; e = GetLastError(); /* WIN32 doesn't distinguish between ENOTDIR and ENOENT */ diff --git a/libc/calls/mkdirat.c b/libc/calls/mkdirat.c index 9af53ecc..403495c0 100644 --- a/libc/calls/mkdirat.c +++ b/libc/calls/mkdirat.c @@ -24,7 +24,8 @@ /** * Creates directory a.k.a. folder. * - * @param dirfd is normally AT_FDCWD + * @param dirfd is normally AT_FDCWD but if it's an open directory and + * path is relative, then path becomes relative to dirfd * @param path is a UTF-8 string, preferably relative w/ forward slashes * @param mode can be, for example, 0755 * @return 0 on success or -1 w/ errno @@ -33,12 +34,9 @@ * @see makedirs() */ int mkdirat(int dirfd, const char *path, unsigned mode) { - if (!path) return efault(); if (!IsWindows()) { return mkdirat$sysv(dirfd, path, mode); - } else if (dirfd == AT_FDCWD) { - return mkdir$nt(path, mode); } else { - return einval(); + return mkdirat$nt(dirfd, path, mode); } } diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c new file mode 100644 index 00000000..a94eee07 --- /dev/null +++ b/libc/calls/mkntpathat.c @@ -0,0 +1,44 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/macros.h" +#include "libc/nt/files.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/errfuns.h" + +int __mkntpathat(int dirfd, const char *path, int flags, + char16_t file[PATH_MAX]) { + char16_t dir[PATH_MAX]; + uint32_t dirlen, filelen; + if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; + if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */ + if (!__isfdkind(dirfd, kFdFile)) return ebadf(); + dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), + kNtFileNameNormalized | kNtVolumeNameDos); + if (!dirlen) return __winerr(); + if (dirlen + 1 + filelen + 1 > ARRAYLEN(dir)) return enametoolong(); + dir[dirlen] = u'\\'; + memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t)); + memcpy(file, dir, (dirlen + 1 + filelen + 1) * sizeof(char16_t)); + return dirlen + 1 + filelen; + } else { + return filelen; + } +} diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index f405adb6..5ada3e84 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -30,18 +30,19 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" -static textwindows int64_t open$nt$impl(const char *file, uint32_t flags, - int32_t mode) { +static textwindows int64_t open$nt$impl(int dirfd, const char *path, + uint32_t flags, int32_t mode) { uint32_t br; int64_t handle; - char16_t file16[PATH_MAX]; - if (__mkntpath2(file, file16, flags) == -1) return -1; + char16_t path16[PATH_MAX]; + if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; if ((handle = CreateFile( - file16, + path16, (flags & 0xf000000f) | (/* this is needed if we mmap(rwx+cow) nt is choosy about open() access */ @@ -82,11 +83,18 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags, */ DeviceIoControl(handle, kNtFsctlSetSparse, NULL, 0, NULL, 0, &br, NULL); } + return handle; + } else if (GetLastError() == kNtErrorFileExists && + ((flags & O_CREAT) && + (flags & O_TRUNC))) { /* TODO(jart): What was this? */ + return eisdir(); + } else { + return __winerr(); } - return handle; } -static textwindows ssize_t open$nt$console(const struct NtMagicPaths *mp, +static textwindows ssize_t open$nt$console(int dirfd, + const struct NtMagicPaths *mp, uint32_t flags, int32_t mode, size_t fd) { if (GetFileType(g_fds.p[STDIN_FILENO].handle) == kNtFileTypeChar && @@ -94,39 +102,38 @@ static textwindows ssize_t open$nt$console(const struct NtMagicPaths *mp, g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle; g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle; } else if ((g_fds.p[fd].handle = open$nt$impl( - mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode)) != -1) { + dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode)) != + -1) { g_fds.p[fd].extra = - open$nt$impl(mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode); + open$nt$impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode); assert(g_fds.p[fd].extra != -1); } else { - return __winerr(); + return -1; } g_fds.p[fd].kind = kFdConsole; g_fds.p[fd].flags = flags; return fd; } -static textwindows ssize_t open$nt$file(const char *file, uint32_t flags, - int32_t mode, size_t fd) { - if ((g_fds.p[fd].handle = open$nt$impl(file, flags, mode)) != -1) { +static textwindows ssize_t open$nt$file(int dirfd, const char *file, + uint32_t flags, int32_t mode, + size_t fd) { + if ((g_fds.p[fd].handle = open$nt$impl(dirfd, file, flags, mode)) != -1) { g_fds.p[fd].kind = kFdFile; g_fds.p[fd].flags = flags; return fd; - } else if (GetLastError() == kNtErrorFileExists && - ((flags & O_CREAT) && (flags & O_TRUNC))) { - return eisdir(); } else { - return __winerr(); + return -1; } } -textwindows ssize_t open$nt(const char *file, uint32_t flags, int32_t mode) { +textwindows ssize_t open$nt(int dirfd, const char *file, uint32_t flags, + int32_t mode) { size_t fd; if ((fd = __getemptyfd()) == -1) return -1; - if ((flags & O_ACCMODE) == O_RDWR && - strcmp(file, kNtMagicPaths.devtty) == 0) { - return open$nt$console(&kNtMagicPaths, flags, mode, fd); + if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) { + return open$nt$console(dirfd, &kNtMagicPaths, flags, mode, fd); } else { - return open$nt$file(file, flags, mode, fd); + return open$nt$file(dirfd, file, flags, mode, fd); } } diff --git a/libc/calls/open.c b/libc/calls/open.c index a34cb0cd..edf7c46b 100644 --- a/libc/calls/open.c +++ b/libc/calls/open.c @@ -16,14 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" -#include "libc/str/str.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.internal.h" /** * Opens file. @@ -34,23 +28,14 @@ * @param mode is an octal user/group/other permission signifier, that's * ignored if O_CREAT or O_TMPFILE weren't passed * @return number needing close(), or -1 w/ errno - * @note don't call open() from signal handlers * @asyncsignalsafe * @vforksafe */ nodiscard int open(const char *file, int flags, ...) { va_list va; unsigned mode; - struct ZiposUri zipname; va_start(va, flags); mode = va_arg(va, unsigned); va_end(va); - if (!file) return efault(); - if (weaken(__zipos_open) && weaken(__zipos_parseuri)(file, &zipname) != -1) { - return weaken(__zipos_open)(&zipname, flags, mode); - } else if (!IsWindows()) { - return openat$sysv(AT_FDCWD, file, flags, mode); - } else { - return open$nt(file, flags, mode); - } + return openat(AT_FDCWD, file, flags, mode); } diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 1195f3a0..ccac9b90 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -16,15 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" +#include "libc/zipos/zipos.internal.h" /** - * Opens file, the modern way. + * Opens file. * - * @param dirfd is normally AT_FDCWD or an open relative directory thing + * @param dirfd is normally AT_FDCWD but if it's an open directory and + * file is a relative path, then file is opened relative to dirfd * @param file is a UTF-8 string, preferably relative w/ forward slashes * @param flags should be O_RDONLY, O_WRONLY, or O_RDWR, and can be or'd * with O_CREAT, O_TRUNC, O_APPEND, O_EXCL, O_CLOEXEC, O_TMPFILE @@ -33,16 +38,23 @@ * @return number needing close(), or -1 w/ errno * @asyncsignalsafe */ -int openat(int dirfd, const char *pathname, int flags, ...) { +nodiscard int openat(int dirfd, const char *file, int flags, ...) { va_list va; unsigned mode; + struct ZiposUri zipname; va_start(va, flags); mode = va_arg(va, unsigned); va_end(va); - if (!pathname) return efault(); - if (dirfd == AT_FDCWD) { - return open(pathname, flags, mode); + if (!file) return efault(); + if (weaken(__zipos_open) && weaken(__zipos_parseuri)(file, &zipname) != -1) { + if (dirfd == AT_FDCWD) { + return weaken(__zipos_open)(&zipname, flags, mode); + } else { + return eopnotsupp(); /* TODO */ + } + } else if (!IsWindows()) { + return openat$sysv(dirfd, file, flags, mode); } else { - return openat$sysv(dirfd, pathname, flags, mode); + return open$nt(dirfd, file, flags, mode); } } diff --git a/libc/calls/rename.c b/libc/calls/rename.c index 43da3da4..f01f42eb 100644 --- a/libc/calls/rename.c +++ b/libc/calls/rename.c @@ -17,10 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" /** * Moves file the Unix way. @@ -29,10 +26,5 @@ * @asyncsignalsafe */ int rename(const char *oldpathname, const char *newpathname) { - if (!oldpathname || !newpathname) return efault(); - if (!IsWindows()) { - return renameat$sysv(AT_FDCWD, oldpathname, AT_FDCWD, newpathname); - } else { - return rename$nt(oldpathname, newpathname); - } + return renameat(AT_FDCWD, oldpathname, AT_FDCWD, newpathname); } diff --git a/libc/calls/rename-nt.c b/libc/calls/renameat-nt.c similarity index 89% rename from libc/calls/rename-nt.c rename to libc/calls/renameat-nt.c index b175469e..31e670c3 100644 --- a/libc/calls/rename-nt.c +++ b/libc/calls/renameat-nt.c @@ -23,11 +23,12 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" -textwindows int rename$nt(const char *oldpath, const char *newpath) { +textwindows int renameat$nt(int olddirfd, const char *oldpath, int newdirfd, + const char *newpath) { char16_t oldpath16[PATH_MAX]; char16_t newpath16[PATH_MAX]; - if (__mkntpath(oldpath, oldpath16) == -1 || - __mkntpath(newpath, newpath16) == -1) { + if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || + __mkntpathat(newdirfd, newpath, 0, newpath16) == -1) { return -1; } if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { diff --git a/libc/calls/renameat.c b/libc/calls/renameat.c index 4e98fb78..2285f62d 100644 --- a/libc/calls/renameat.c +++ b/libc/calls/renameat.c @@ -19,16 +19,23 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/sysv/consts/at.h" +#include "libc/sysv/errfuns.h" /** * Renames files relative to directories. + * + * @param olddirfd is normally AT_FDCWD but if it's an open directory + * and oldpath is relative, then oldpath become relative to dirfd + * @param newdirfd is normally AT_FDCWD but if it's an open directory + * and newpath is relative, then newpath become relative to dirfd + * @return 0 on success, or -1 w/ errno */ int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - unsigned mode; - if (olddirfd == AT_FDCWD && newdirfd == AT_FDCWD) { - return rename(oldpath, newpath); - } else { + if (!oldpath || !newpath) return efault(); + if (!IsWindows()) { return renameat$sysv(olddirfd, oldpath, newdirfd, newpath); + } else { + return renameat$nt(olddirfd, oldpath, newdirfd, newpath); } } diff --git a/libc/calls/rmdir.c b/libc/calls/rmdir.c index 1e6d7b1f..c2f93518 100644 --- a/libc/calls/rmdir.c +++ b/libc/calls/rmdir.c @@ -16,11 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/nt/files.h" -#include "libc/str/str.h" -#include "libc/calls/internal.h" -#include "libc/sysv/errfuns.h" +#include "libc/calls/calls.h" #include "libc/sysv/consts/at.h" /** @@ -29,10 +25,5 @@ * @return 0 on success or -1 w/ errno on error */ int rmdir(const char *path) { - if (!path) return efault(); - if (!IsWindows()) { - return unlinkat$sysv(AT_FDCWD, path, AT_REMOVEDIR); - } else { - return rmdir$nt(path); - } + return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); } diff --git a/libc/calls/stat.c b/libc/calls/stat.c index 47ef1430..19c89ce4 100644 --- a/libc/calls/stat.c +++ b/libc/calls/stat.c @@ -16,29 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" -#include "libc/str/str.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.internal.h" /** * Returns information about thing. * + * @param st is where result is stored * @see S_ISDIR(st.st_mode), S_ISREG(), etc. * @asyncsignalsafe */ -int stat(const char *pathname, struct stat *st) { - struct ZiposUri zipname; - if (weaken(__zipos_stat) && - weaken(__zipos_parseuri)(pathname, &zipname) != -1) { - return weaken(__zipos_stat)(&zipname, st); - } else if (!IsWindows()) { - return fstatat$sysv(AT_FDCWD, pathname, st, 0); - } else { - return stat$nt(pathname, st); - } +int stat(const char *path, struct stat *st) { + return fstatat(AT_FDCWD, path, st, 0); } diff --git a/libc/calls/symlink.c b/libc/calls/symlink.c index 42dd3e81..08ec545c 100644 --- a/libc/calls/symlink.c +++ b/libc/calls/symlink.c @@ -17,10 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" /** * Creates symbolic link. @@ -36,10 +33,5 @@ * @asyncsignalsafe */ int symlink(const char *target, const char *linkpath) { - if (!target || !linkpath) return efault(); - if (!IsWindows()) { - return symlinkat$sysv(target, AT_FDCWD, linkpath); - } else { - return symlink$nt(target, linkpath); - } + return symlinkat(target, AT_FDCWD, linkpath); } diff --git a/libc/calls/symlink-nt.c b/libc/calls/symlinkat-nt.c similarity index 86% rename from libc/calls/symlink-nt.c rename to libc/calls/symlinkat-nt.c index b54aa895..509a1773 100644 --- a/libc/calls/symlink-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -20,10 +20,13 @@ #include "libc/calls/internal.h" #include "libc/nt/files.h" -textwindows int symlink$nt(const char *target, const char *linkpath) { - char16_t linkpath16[PATH_MAX], target16[PATH_MAX]; - uint32_t flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0; - if (__mkntpath(linkpath, linkpath16) == -1) return -1; +textwindows int symlinkat$nt(const char *target, int newdirfd, + const char *linkpath) { + uint32_t flags; + char16_t target16[PATH_MAX]; + char16_t linkpath16[PATH_MAX]; + flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0; + if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; if (__mkntpath(target, target16) == -1) return -1; if (CreateSymbolicLink(linkpath16, target16, flags)) { return 0; diff --git a/libc/calls/symlinkat.c b/libc/calls/symlinkat.c new file mode 100644 index 00000000..d1ebaf26 --- /dev/null +++ b/libc/calls/symlinkat.c @@ -0,0 +1,43 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/sysv/consts/at.h" + +/** + * Creates symbolic link. + * + * This is like link() but adds a tiny indirection to make the fact that + * the file is a link obvious. It also enables certain other features, + * like the ability to be broken. + * + * @param target can be relative and needn't exist + * @param linkpath is what gets created + * @return 0 on success, or -1 w/ errno + * @note Windows NT only lets admins do this + * @asyncsignalsafe + */ +int symlinkat(const char *target, int newdirfd, const char *linkpath) { + if (!IsWindows()) { + return symlinkat$sysv(target, newdirfd, linkpath); + } else { + return symlinkat$nt(target, newdirfd, linkpath); + } +} diff --git a/libc/calls/unlink.c b/libc/calls/unlink.c index 0df25121..840a8990 100644 --- a/libc/calls/unlink.c +++ b/libc/calls/unlink.c @@ -16,12 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/dce.h" -#include "libc/nt/files.h" -#include "libc/str/str.h" +#include "libc/calls/calls.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" /** * Deletes file. @@ -35,10 +31,5 @@ * @asyncsignalsafe */ int unlink(const char *name) { - if (!name) return efault(); - if (!IsWindows()) { - return unlinkat$sysv(AT_FDCWD, name, 0); - } else { - return unlink$nt(name); - } + return unlinkat(AT_FDCWD, name, 0); } diff --git a/libc/calls/rmdir-nt.c b/libc/calls/unlinkat-nt.c similarity index 81% rename from libc/calls/rmdir-nt.c rename to libc/calls/unlinkat-nt.c index bf2d625d..b6066b3f 100644 --- a/libc/calls/rmdir-nt.c +++ b/libc/calls/unlinkat-nt.c @@ -17,20 +17,25 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/synchronization.h" -#include "libc/sysv/errfuns.h" +#include "libc/sysv/consts/at.h" -textwindows int rmdir$nt(const char *path) { - int e, ms, len; - char16_t path16[PATH_MAX]; - if ((len = __mkntpath(path, path16)) == -1) return -1; - while (path16[len - 1] == u'\\') path16[--len] = u'\0'; - if (len + 2 + 1 > PATH_MAX) return enametoolong(); +static textwindows int unlink$nt(const char16_t *path) { + if (DeleteFile(path)) { + return 0; + } else { + return __winerr(); + } +} + +static textwindows int rmdir$nt(const char16_t *path) { + int e, ms; for (ms = 1;; ms *= 2) { - if (RemoveDirectory(path16)) return 0; + if (RemoveDirectory(path)) return 0; /* * Files can linger, for absolutely no reason. * Possibly some Windows Defender bug on Win7. @@ -48,3 +53,13 @@ textwindows int rmdir$nt(const char *path) { errno = e; return -1; } + +textwindows int unlinkat$nt(int dirfd, const char *path, int flags) { + uint16_t path16[PATH_MAX]; + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; + if (flags & AT_REMOVEDIR) { + return rmdir$nt(path16); + } else { + return unlink$nt(path16); + } +} diff --git a/libc/calls/unlinkat.c b/libc/calls/unlinkat.c index dd1cb96d..0c682db9 100644 --- a/libc/calls/unlinkat.c +++ b/libc/calls/unlinkat.c @@ -18,12 +18,22 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/dce.h" #include "libc/sysv/consts/at.h" -int unlinkat(int dirfd, const char *pathname, int flags) { - if (dirfd == AT_FDCWD) { - return unlink(pathname); +/** + * Deletes inode and maybe the file too. + * + * @param dirfd is normally AT_FDCWD but if it's an open directory and + * path is relative, then path becomes relative to dirfd + * @param path is the thing to delete + * @param flags can have AT_REMOVEDIR + * @return 0 on success, or -1 w/ errno + */ +int unlinkat(int dirfd, const char *path, int flags) { + if (!IsWindows()) { + return unlinkat$sysv(dirfd, path, flags); } else { - return unlinkat$sysv(dirfd, pathname, flags); + return unlinkat$nt(dirfd, path, flags); } } diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index c214aa83..fbe24990 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -35,25 +35,12 @@ textwindows int utimensat$nt(int dirfd, const char *path, const struct timespec ts[2], int flags) { int i, rc; int64_t fh; - bool closeme; uint16_t path16[PATH_MAX]; struct NtFileTime ft[2], *ftp[2]; - if (flags) return einval(); - if (path) { - if (dirfd == AT_FDCWD) { - if (__mkntpath(path, path16) == -1) return -1; - if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, - NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != - -1) { - closeme = true; - } else { - return __winerr(); - } - } else { - return einval(); - } - } else { - return ebadf(); + if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; + if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL, + kNtOpenExisting, kNtFileAttributeNormal, 0)) == -1) { + return __winerr(); } if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) { GetSystemTimeAsFileTime(ft); @@ -78,8 +65,6 @@ textwindows int utimensat$nt(int dirfd, const char *path, } else { rc = __winerr(); } - if (closeme) { - CloseHandle(fh); - } + CloseHandle(fh); return rc; } diff --git a/libc/nt/files.h b/libc/nt/files.h index f95ce68d..8536dca8 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -200,13 +200,13 @@ bool32 WriteFileGather(int64_t hFileOpenedWithOverlappedAndNoBuffering, struct NtOverlapped inout_lpOverlapped) paramsnonnull(); #define kNtFileNameNormalized 0x0 -#define kNtVolumeNameDos 0x0 -#define kNtVolumeNameGuid 0x1 -#define kNtVolumeNameNt 0x2 -#define kNtVolumeNameNone 0x4 #define kNtFileNameOpened 0x8 +#define kNtVolumeNameDos 0x0 /* e.g. \\?\C:\Users\jart */ +#define kNtVolumeNameGuid 0x1 /* e.g. \\?\Volume{ea38-etc.}\Users\jart */ +#define kNtVolumeNameNt 0x2 /* e.g. \Device\HarddiskVolume4\Users\jart */ +#define kNtVolumeNameNone 0x4 /* e.g. \Users\jart */ uint32_t GetFinalPathNameByHandle(int64_t hFile, char16_t *out_path, - uint32_t size, uint32_t flags); + uint32_t arraylen, uint32_t flags); #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/files.inc" diff --git a/libc/str/str.h b/libc/str/str.h index 9684dd26..9a6dbdc5 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -151,6 +151,7 @@ int strcasecmpzbw(const uint16_t *, const char *) strlenesque; char *stpcpy(char *, const char *) memcpyesque; char *stpncpy(char *, const char *, size_t) memcpyesque; char *strcat(char *, const char *) memcpyesque; +char16_t *strcat16(char16_t *, const char16_t *); size_t strlcpy(char *, const char *, size_t); size_t strlcat(char *, const char *, size_t); char *strcpy(char *, const char *) memcpyesque; diff --git a/libc/calls/unlink-nt.c b/libc/str/strcat16.c similarity index 86% rename from libc/calls/unlink-nt.c rename to libc/str/strcat16.c index 1716f629..c692b8f9 100644 --- a/libc/calls/unlink-nt.c +++ b/libc/str/strcat16.c @@ -16,16 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/nt/files.h" -#include "libc/nt/runtime.h" +#include "libc/str/str.h" -textwindows int unlink$nt(const char *name) { - uint16_t name16[PATH_MAX]; - if (__mkntpath(name, name16) == -1) return -1; - if (DeleteFile(name16)) { - return 0; - } else { - return __winerr(); - } +/** + * Appends 𝑠 to 𝑑. + * + * @param 𝑑 is a NUL-terminated 16-bit string buffer + * @param 𝑠 is a NUL-terminated 16-bit string + * @return 𝑑 + * @asyncsignalsafe + */ +char16_t *strcat16(char16_t *d, const char16_t *s) { + return strcpy16(d + strlen16(d), s); } diff --git a/libc/sysv/calls/fchdir-sysv.s b/libc/sysv/calls/fchdir-sysv.s new file mode 100644 index 00000000..9111c328 --- /dev/null +++ b/libc/sysv/calls/fchdir-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall fchdir$sysv 0x000d000d200d0051 globl hidden diff --git a/libc/sysv/calls/fchdir.s b/libc/sysv/calls/fchdir.s deleted file mode 100644 index fe50522b..00000000 --- a/libc/sysv/calls/fchdir.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall fchdir 0x000d000d200d0051 globl diff --git a/libc/sysv/calls/mknodat.s b/libc/sysv/calls/mknodat.s index 43253564..22bb155b 100644 --- a/libc/sysv/calls/mknodat.s +++ b/libc/sysv/calls/mknodat.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall mknodat 0x014022ffffff0103 globl +.scall mknodat 0x0140022fffff0103 globl diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index f57e55e2..431267a9 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -118,7 +118,7 @@ scall '__truncate$sysv' 0x00c801df20c8004c globl hidden # openbsd:pad scall '__ftruncate$sysv' 0x00c901e020c9004d globl hidden # openbsd:pad scall 'getcwd$sysv' 0x01300146ffff004f globl hidden scall 'chdir$sysv' 0x000c000c200c0050 globl hidden -scall fchdir 0x000d000d200d0051 globl +scall 'fchdir$sysv' 0x000d000d200d0051 globl hidden scall 'rename$sysv' 0x0080008020800052 globl hidden scall 'mkdir$sysv' 0x0088008820880053 globl hidden scall 'rmdir$sysv' 0x0089008920890054 globl hidden @@ -164,7 +164,7 @@ scall sigpending 0x003400342034007f globl scall 'sigsuspend$sysv' 0x006f0155206f0082 globl hidden scall sigaltstack 0x0120003520350083 globl scall 'mknod$sysv' 0x000e000e200e0085 globl hidden -scall mknodat 0x014022ffffff0103 globl # FreeBSD 12+ +scall mknodat 0x0140022fffff0103 globl # FreeBSD 12+ scall 'mkfifo$sysv' 0x008400842084ffff globl hidden scall mkfifoat 0x013f01f1ffffffff globl scall statfs 0x003f022b21590089 globl diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index 9becf356..a1f3bbc2 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -21,6 +21,7 @@ #include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/runtime/runtime.h" +#include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -68,3 +69,13 @@ TEST(makedirs, testEmptyString_ENOENT) { EXPECT_EQ(-1, makedirs("", 0755)); EXPECT_EQ(ENOENT, errno); } + +TEST(mkdirat, testRelativePath_opensRelativeToDirFd) { + int dirfd; + ASSERT_NE(-1, mkdir("foo", 0755)); + ASSERT_NE(-1, (dirfd = open("foo", O_RDONLY | O_DIRECTORY))); + EXPECT_NE(-1, mkdirat(dirfd, "bar", 0755)); + EXPECT_TRUE(isdirectory("foo/bar")); + EXPECT_EQ(-1, makedirs("", 0755)); + EXPECT_NE(-1, close(dirfd)); +}