Make improvements

- Introduce ualarm() function
- Make rename() report EISEMPTY on Windows
- Always raise EINVAL upon open(O_RDONLY|O_TRUNC)
- Add macro so ./configure will detect SOCK_CLOEXEC
- Fix O_TRUNC without O_CREAT not working on Windows
- Let fcntl(F_SETFL) change O_APPEND status on Windows
- Make sure pwrite() / pread() report ESPIPE on sockets
- Raise ESPIPE on Windows when pwrite() is used on pipe
- Properly compute O_APPEND CreateFile() flags on Windows
- Don't require O_DIRECTORY to open directories on Windows
- Fix more instances of Windows reporting EISDIR and ENOTDIR
- Normalize EFTYPE and EMLINK to ELOOP on NetBSD and FreeBSD
- Make unlink() / rmdir() work on read-only files on Windows
- Validate UTF-8 on Windows paths to fix bug with overlong NUL
- Always print signal name to stderr when crashing due to SIG_DFL
- Fix Windows bug where denormalized paths >260 chars didn't work
- Block signals on BSDs when thread exits before trashing its own stack
This commit is contained in:
Justine Tunney 2023-08-21 02:28:24 -07:00
parent ec957491ea
commit ebf784d4f5
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
76 changed files with 1019 additions and 568 deletions

View file

@ -34,17 +34,14 @@ cosmocc --update # pull cosmo and rebuild toolchain
``` ```
You've now successfully installed your very own cosmos. Now let's build You've now successfully installed your very own cosmos. Now let's build
an example program, which demonstrates the crash reporting feature: an example program:
```c ```c
// hello.c // hello.c
#include <stdio.h> #include <stdio.h>
#include <cosmo.h>
int main() { int main() {
ShowCrashReports();
printf("hello world\n"); printf("hello world\n");
__builtin_trap();
} }
``` ```

View file

@ -181,13 +181,14 @@ textwindows bool __sig_handle(int sigops, int sig, int si_code,
switch (__sighandrvas[sig]) { switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL: case (intptr_t)SIG_DFL:
if (__sig_is_fatal(sig)) { if (__sig_is_fatal(sig)) {
size_t len; intptr_t hStderr;
char name[16]; const char *signame;
strsignal_r(sig, name); char *end, sigbuf[15], output[16];
len = strlen(name); signame = strsignal_r(sig, sigbuf);
name[len++] = '\n'; STRACE("terminating due to uncaught %s", signame);
WriteFile(GetStdHandle(kNtStdErrorHandle), name, len, 0, 0); hStderr = GetStdHandle(kNtStdErrorHandle);
STRACE("terminating on %s", name); end = stpcpy(stpcpy(output, signame), "\n");
WriteFile(hStderr, output, end - output, 0, 0);
if (_weaken(__restore_console_win32)) { if (_weaken(__restore_console_win32)) {
_weaken(__restore_console_win32)(); _weaken(__restore_console_win32)();
} }

View file

@ -193,6 +193,7 @@ unsigned geteuid(void) nosideeffect;
unsigned getgid(void) nosideeffect; unsigned getgid(void) nosideeffect;
unsigned getuid(void) libcesque; unsigned getuid(void) libcesque;
unsigned sleep(unsigned); unsigned sleep(unsigned);
unsigned ualarm(unsigned, unsigned);
unsigned umask(unsigned); unsigned umask(unsigned);
void sync(void); void sync(void);

View file

@ -35,7 +35,7 @@
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics #define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose #define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering #define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough #define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough (not sent to win32)
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess #define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
#define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan #define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed #define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
@ -62,16 +62,22 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
break; break;
case O_WRONLY: case O_WRONLY:
perm = kNtFileGenericWrite; perm = kNtFileGenericWrite;
if (flags & _O_APPEND) {
// kNtFileAppendData is already present in kNtFileGenericWrite.
// WIN32 wont act on append access when write is already there.
perm = kNtFileAppendData;
}
break; break;
case O_RDWR: case O_RDWR:
perm = kNtFileGenericRead | kNtFileGenericWrite; if (flags & _O_APPEND) {
perm = kNtFileGenericRead | kNtFileAppendData;
} else {
perm = kNtFileGenericRead | kNtFileGenericWrite;
}
break; break;
default: default:
return einval(); return einval();
} }
if (flags & _O_APPEND) {
perm = kNtFileAppendData; // todo: this is part of generic write above
}
attr = 0; attr = 0;
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE; is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;

View file

@ -26,7 +26,7 @@ textwindows int sys_fchmodat_nt(int dirfd, const char *path, uint32_t mode,
uint16_t path16[PATH_MAX]; uint16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((attr = GetFileAttributes(path16)) != -1u) { if ((attr = GetFileAttributes(path16)) != -1u) {
if (mode & 0200) { if (mode & 0222) {
attr &= ~kNtFileAttributeReadonly; attr &= ~kNtFileAttributeReadonly;
} else { } else {
attr |= kNtFileAttributeReadonly; attr |= kNtFileAttributeReadonly;

View file

@ -26,10 +26,13 @@
#include "libc/calls/wincrash.internal.h" #include "libc/calls/wincrash.internal.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/kmalloc.h" #include "libc/intrin/kmalloc.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filelockflags.h" #include "libc/nt/enum/filelockflags.h"
#include "libc/nt/errors.h" #include "libc/nt/errors.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
@ -310,52 +313,63 @@ static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start);
} }
static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags, unsigned arg, static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags,
unsigned supported) { unsigned mode, unsigned arg,
unsigned old, neu, changed, other, allowed; intptr_t *handle) {
old = *flags & supported;
other = *flags & ~supported; // you may change the following:
neu = arg & supported;
changed = old ^ neu;
// you may change the following access mode flags:
// //
// - O_NONBLOCK make read() raise EAGAIN // - O_NONBLOCK make read() raise EAGAIN
// - O_NDELAY same thing as O_NONBLOCK // - O_APPEND for toggling append mode
// - O_ACCMODE but has a minimal effect // - O_RANDOM alt. for posix_fadvise()
// - O_SEQUENTIAL alt. for posix_fadvise()
// - O_DIRECT works but haven't tested
// //
allowed = O_ACCMODE | O_NONBLOCK; // the other bits are ignored.
if (changed & ~allowed) { unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK;
// the following access mode flags are supported, but it's currently unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT;
// not possible to change them on windows. unsigned newflag = (*flags & ~allowed) | (arg & allowed);
//
// - O_APPEND tried to support but failed if ((*flags & needreo) ^ (arg & needreo)) {
// - O_RANDOM use posix_fadvise() instead unsigned perm, share, attr;
// - O_SEQUENTIAL use posix_fadvise() instead if (GetNtOpenFlags(newflag, mode, &perm, &share, 0, &attr) == -1) {
// - O_DIRECT possibly in future? return -1;
// - O_DSYNC possibly in future? }
// - O_RSYNC possibly in future? // MSDN says only these are allowed, otherwise it returns EINVAL.
// - O_SYNC possibly in future? attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
// kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |
return enotsup(); kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped |
kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess |
kNtFileFlagSequentialScan | kNtFileFlagWriteThrough;
intptr_t hand;
if ((hand = ReOpenFile(*handle, perm, share, attr)) != -1) {
if (hand != *handle) {
CloseHandle(*handle);
*handle = hand;
}
} else {
return __winerr();
}
} }
// 1. ignore flags that aren't access mode flags // 1. ignore flags that aren't access mode flags
// 2. return zero if nothing's changed // 2. return zero if nothing's changed
*flags = other | neu; *flags = newflag;
return 0; return 0;
} }
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
int rc; int rc;
uint32_t flags; uint32_t flags;
int access_mode_flags = O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT |
O_NOATIME | O_NONBLOCK | O_RANDOM | O_SEQUENTIAL;
if (__isfdkind(fd, kFdFile) || // if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || // __isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole)) { __isfdkind(fd, kFdConsole)) {
if (cmd == F_GETFL) { if (cmd == F_GETFL) {
rc = g_fds.p[fd].flags & access_mode_flags; rc = g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_DIRECT | O_NONBLOCK |
O_RANDOM | O_SEQUENTIAL);
} else if (cmd == F_SETFL) { } else if (cmd == F_SETFL) {
rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, arg, access_mode_flags); rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, g_fds.p[fd].mode, arg,
&g_fds.p[fd].handle);
} else if (cmd == F_GETFD) { } else if (cmd == F_GETFD) {
if (g_fds.p[fd].flags & O_CLOEXEC) { if (g_fds.p[fd].flags & O_CLOEXEC) {
rc = FD_CLOEXEC; rc = FD_CLOEXEC;

View file

@ -27,9 +27,9 @@
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/** /**
* Does things with file descriptor, e.g. * Does things with file descriptor, e.g.
@ -57,6 +57,12 @@
* - `F_SETFD` sets `FD_CLOEXEC` status of `arg` file descriptor * - `F_SETFD` sets `FD_CLOEXEC` status of `arg` file descriptor
* - `F_GETFL` returns file descriptor status flags * - `F_GETFL` returns file descriptor status flags
* - `F_SETFL` sets file descriptor status flags * - `F_SETFL` sets file descriptor status flags
* - `O_NONBLOCK` may be changed
* - `O_APPEND` may be changed
* - `O_DIRECT` may be changed
* - `O_SEQUENTIAL` may be changed (no-op on non-Windows)
* - `O_RANDOM` may be changed (no-op on non-Windows)
* - Other bits (`O_ACCMODE`, `O_CREAT`, etc.) are ignored
* - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3 * - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3
* - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd * - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd
* - `F_SETLK` for record locking where `arg` is `struct flock *` * - `F_SETLK` for record locking where `arg` is `struct flock *`
@ -131,11 +137,15 @@ int fcntl(int fd, int cmd, ...) {
} }
#ifdef SYSDEBUG #ifdef SYSDEBUG
if (cmd == F_GETFD || // if (rc != -1 && cmd == F_GETFL) {
cmd == F_GETOWN || // STRACE("fcntl(%d, F_GETFL) → %s", fd, DescribeOpenFlags(rc));
cmd == F_FULLFSYNC || // } else if (cmd == F_SETFL) {
cmd == F_BARRIERFSYNC || // STRACE("fcntl(%d, F_SETFL, %s) → %d% m", fd, DescribeOpenFlags(arg), rc);
cmd == F_MAXFD) { } else if (cmd == F_GETFD || //
cmd == F_GETOWN || //
cmd == F_FULLFSYNC || //
cmd == F_BARRIERFSYNC || //
cmd == F_MAXFD) {
STRACE("fcntl(%d, %s) → %d% m", fd, DescribeFcntlCmd(cmd), rc); STRACE("fcntl(%d, %s) → %d% m", fd, DescribeFcntlCmd(cmd), rc);
} else if (cmd == F_GETFL) { } else if (cmd == F_GETFL) {
STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd), STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd),

View file

@ -52,27 +52,31 @@ static inline const char *__strace_fstatat_flags(char buf[12], int flags) {
* file is a relative path, then `path` becomes relative to `dirfd` * file is a relative path, then `path` becomes relative to `dirfd`
* @param st is where the result is stored * @param st is where the result is stored
* @param flags can have `AT_SYMLINK_NOFOLLOW` * @param flags can have `AT_SYMLINK_NOFOLLOW`
* @param st is where result is stored
* @raise EACCES if denied access to component in path prefix * @raise EACCES if denied access to component in path prefix
* @raise EIO if i/o error occurred while reading from filesystem * @raise EIO if i/o error occurred while reading from filesystem
* @raise ELOOP if a symbolic link loop exists in `path` * @raise ELOOP if a symbolic link loop exists in `path`
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX` * @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
* @raise ENOENT on empty string or if component in path doesn't exist * @raise ENOENT on empty string or if component in path doesn't exist
* @raise ENOTDIR if a parent component existed that wasn't a directory * @raise ENOTDIR if a parent component existed that wasn't a directory
* @raise EILSEQ if `path` contains illegal UTF-8 sequences (Windows/MacOS)
* @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory * @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
* @raise ENOEXEC if `path` is a zip path and this executable isn't a zip file
* @raise EOVERFLOW shouldn't be possible on 64-bit systems * @raise EOVERFLOW shouldn't be possible on 64-bit systems
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced * @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX` * @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
* @raise EFAULT if `path` or `st` point to invalid memory
* @raise EINVAL if `flags` has unsupported bits
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @see S_ISDIR(st.st_mode), S_ISREG() * @see S_ISDIR(st.st_mode), S_ISREG()
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
int fstatat(int dirfd, const char *path, struct stat *st, int flags) { int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
/* execve() depends on this */ // execve() depends on this
int rc; int rc;
struct ZiposUri zipname; struct ZiposUri zipname;
if (IsAsan() && !__asan_is_valid(st, sizeof(*st))) { if (IsAsan() && (!__asan_is_valid_str(path) || //
!__asan_is_valid(st, sizeof(*st)))) {
rc = efault(); rc = efault();
} else if (flags & ~AT_SYMLINK_NOFOLLOW) { } else if (flags & ~AT_SYMLINK_NOFOLLOW) {
return einval(); return einval();

View file

@ -596,68 +596,68 @@ static int ioctl_siocgifflags(int fd, void *arg) {
/** /**
* Performs special i/o operation on file descriptor. * Performs special i/o operation on file descriptor.
* *
* @param request can be any of: * The following i/o requests are available.
* *
* - `FIONREAD` takes an `int *` and returns how many bytes of input * - `FIONREAD` takes an `int *` and returns how many bytes of input are
* are available on a terminal or socket, waiting to be read. * available on a terminal/socket/pipe, waiting to be read. Be sure to
* only use it on the reading end of a pipe.
* *
* - `TIOCGWINSZ` populates `struct winsize *` with the dimensions * - `TIOCGWINSZ` populates `struct winsize *` with the dimensions of
* of your teletypewriter. It's an alias for tcgetwinsize(). * your teletypewriter. It's an alias for tcgetwinsize().
* *
* - `TIOCSWINSZ` with the dimensions of your teletypewriter to * - `TIOCSWINSZ` with the dimensions of your teletypewriter to `struct
* `struct winsize *`. It's an alias for tcsetwinsize(). * winsize *`. It's an alias for tcsetwinsize().
* *
* - `TIOCOUTQ` takes an `int *` and returns the number of bytes in * - `TIOCOUTQ` takes an `int *` and returns the number of bytes in the
* the terminal's output buffer. Only available on UNIX. * terminal's output buffer. Only available on UNIX.
* *
* - `TIOCSTI` takes a `const char *` and may be used to fake input * - `TIOCSTI` takes a `const char *` and may be used to fake input to a
* to a tty. This API isn't available on OpenBSD. Only available * tty. This API isn't available on OpenBSD. Only available on UNIX.
* on UNIX.
* *
* - `TIOCNOTTY` takes an `int tty_fd` arg and makes it the * - `TIOCNOTTY` takes an `int tty_fd` arg and makes it the controlling
* controlling terminal of the calling process, which should have * terminal of the calling process, which should have called setsid()
* called setsid() beforehand. * beforehand.
* *
* - `TIOCNOTTY` to give up the controlling terminal. Only available * - `TIOCNOTTY` to give up the controlling terminal. Only available on
* on UNIX. * UNIX.
* *
* - `TIOCNXCL` to give up exclusive mode on terminal. Only * - `TIOCNXCL` to give up exclusive mode on terminal. Only available on
* available on UNIX. * UNIX.
* *
* - `SIOCGIFCONF` takes an struct ifconf object of a given size, * - `SIOCGIFCONF` takes an struct ifconf object of a given size,
* whose arg is `struct ifconf *`. It implements the Linux style * whose arg is `struct ifconf *`. It implements the Linux style
* and modifies the following: * and modifies the following:
* - ifc_len: set it to the number of valid ifreq structures * - ifc_len: set it to the number of valid ifreq structures
* representingthe interfaces * representingthe interfaces
* - ifc_ifcu.ifcu_req: sets the name of the interface for each * - ifc_ifcu.ifcu_req: sets the name of the interface for each
* interface * interface
* The ifc_len is an input/output parameter: set it to the total * The ifc_len is an input/output parameter: set it to the total
* size of the ifcu_buf (ifcu_req) buffer on input. * size of the ifcu_buf (ifcu_req) buffer on input.
* *
* - `SIOCGIFNETMASK` populates a `struct ifconf *` record with the * - `SIOCGIFNETMASK` populates a `struct ifconf *` record with the
* network interface mask. This data structure should be obtained * network interface mask. This data structure should be obtained by
* by calling `SIOCGIFCONF`. * calling `SIOCGIFCONF`.
* *
* - `SIOCGIFBRDADDR` populates a `struct ifconf *` record with the * - `SIOCGIFBRDADDR` populates a `struct ifconf *` record with the
* network broadcast addr. This data structure should be obtained * network broadcast addr. This data structure should be obtained by
* by calling `SIOCGIFCONF`. * calling `SIOCGIFCONF`.
* *
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)` * - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)` * - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)` * - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
* - `TCGETS` isn't polyfilled; use tcgetattr() * - `TCGETS` isn't polyfilled; use tcgetattr()
* - `TCSETS` isn't polyfilled; use tcsetattr() * - `TCSETS` isn't polyfilled; use tcsetattr()
* - `TCSETSW` isn't polyfilled; use tcsetattr() * - `TCSETSW` isn't polyfilled; use tcsetattr()
* - `TCSETSF` isn't polyfilled; use tcsetattr() * - `TCSETSF` isn't polyfilled; use tcsetattr()
* - `TCXONC` isn't polyfilled; use tcflow() * - `TCXONC` isn't polyfilled; use tcflow()
* - `TCSBRK` isn't polyfilled; use tcdrain() * - `TCSBRK` isn't polyfilled; use tcdrain()
* - `TCFLSH` isn't polyfilled; use tcflush() * - `TCFLSH` isn't polyfilled; use tcflush()
* - `TIOCGPTN` isn't polyfilled; use ptsname() * - `TIOCGPTN` isn't polyfilled; use ptsname()
* - `TIOCGSID` isn't polyfilled; use tcgetsid() * - `TIOCGSID` isn't polyfilled; use tcgetsid()
* - `TCSBRK` isn't polyfilled; use tcsendbreak() * - `TCSBRK` isn't polyfilled; use tcsendbreak()
* - `TCSBRK` isn't polyfilled; use tcsendbreak() * - `TCSBRK` isn't polyfilled; use tcsendbreak()
* - `TIOCSPGRP` isn't polyfilled; use tcsetpgrp() * - `TIOCSPGRP` isn't polyfilled; use tcsetpgrp()
* - `TIOCSPTLCK` isn't polyfilled; use unlockpt() * - `TIOCSPTLCK` isn't polyfilled; use unlockpt()
* *
* @restartable * @restartable
* @vforksafe * @vforksafe

View file

@ -16,15 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_mkdirat_nt(int dirfd, const char *path, uint32_t mode) { textwindows int sys_mkdirat_nt(int dirfd, const char *path, uint32_t mode) {
int e; char16_t path16[PATH_MAX];
char16_t *p, path16[PATH_MAX];
/* if (strlen(path) > 248) return enametoolong(); */
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if (CreateDirectory(path16, 0)) return 0; if (CreateDirectory(path16, 0)) return 0;
return __fix_enotdir(-1, path16); return __fix_enotdir(-1, path16);

View file

@ -18,6 +18,8 @@
*/ */
#include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/ntmagicpaths.internal.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/systeminfo.h" #include "libc/nt/systeminfo.h"
@ -75,22 +77,21 @@ textwindows int __mkntpath(const char *path,
*/ */
textwindows int __mkntpath2(const char *path, textwindows int __mkntpath2(const char *path,
char16_t path16[hasatleast PATH_MAX], int flags) { char16_t path16[hasatleast PATH_MAX], int flags) {
/*
* 1. Need +1 for NUL-terminator
* 2. Need +1 for UTF-16 overflow
* 3. Need 2 for SetCurrentDirectory trailing slash requirement
* 4. Need 13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0"
* which is an "8.3 filename" from the DOS days
*/
const char *q;
bool isdospath;
char16_t c, *p;
size_t i, j, n, m, x, z;
if (!path) return efault();
path = FixNtMagicPath(path, flags);
p = path16;
q = path;
// 1. Need +1 for NUL-terminator
// 2. Need +1 for UTF-16 overflow
// 3. Need ≥2 for SetCurrentDirectory trailing slash requirement
// 4. Need ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0"
// which is an "8.3 filename" from the DOS days
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
return efault();
}
path = FixNtMagicPath(path, flags);
size_t x, z;
char16_t *p = path16;
const char *q = path;
if (IsSlash(q[0]) && IsAlpha(q[1]) && IsSlash(q[2])) { if (IsSlash(q[0]) && IsAlpha(q[1]) && IsSlash(q[2])) {
z = MIN(32767, PATH_MAX); z = MIN(32767, PATH_MAX);
// turn "\c\foo" into "\\?\c:\foo" // turn "\c\foo" into "\\?\c:\foo"
@ -142,6 +143,7 @@ textwindows int __mkntpath2(const char *path,
} }
// turn /tmp into GetTempPath() // turn /tmp into GetTempPath()
size_t m;
if (!x && IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' && if (!x && IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' &&
(IsSlash(q[4]) || !q[4])) { (IsSlash(q[4]) || !q[4])) {
m = GetTempPath(z, p); m = GetTempPath(z, p);
@ -154,23 +156,37 @@ textwindows int __mkntpath2(const char *path,
} }
// turn utf-8 into utf-16 // turn utf-8 into utf-16
n = tprecode8to16(p, z, q).ax; size_t n = tprecode8to16(p, z, q).ax;
if (n >= z - 1) { if (n >= z - 1) {
STRACE("path too long for windows: %#s", path);
return enametoolong(); return enametoolong();
} }
// 1. turn `/` into `\` // normalize path
// 2. turn `\\` into `\` if not at beginning // we need it because \\?\... paths have to be normalized
// we don't remove the trailing slash since it is special
size_t i, j;
for (j = i = 0; i < n; ++i) { for (j = i = 0; i < n; ++i) {
c = p[i]; int c = p[i];
if (c == '/') { if (c == '/') {
c = '\\'; c = '\\';
} }
if (j > 1 && c == '\\' && p[j - 1] == '\\') { if (j > 1 && c == '\\' && p[j - 1] == '\\') {
continue; // matched "^/" or "//" but not "^//"
} else if ((j && p[j - 1] == '\\') && //
c == '.' && //
(i + 1 == n || IsSlash(p[i + 1]))) {
// matched "/./" or "/.$"
i += !(i + 1 == n);
} else if ((j && p[j - 1] == '\\') && //
c == '.' && //
(i + 1 < n && p[i + 1] == '.') && //
(i + 2 == n || IsSlash(p[i + 2]))) {
// matched "/../" or "/..$"
while (j && p[j - 1] == '\\') --j;
while (j && p[j - 1] != '\\') --j;
} else {
p[j++] = c;
} }
p[j++] = c;
} }
p[j] = 0; p[j] = 0;
n = j; n = j;
@ -180,11 +196,11 @@ textwindows int __mkntpath2(const char *path,
// To avoid toil like this: // To avoid toil like this:
// //
// CMD.EXE was started with the above path as the current directory. // "CMD.EXE was started with the above path as the current
// UNC paths are not supported. Defaulting to Windows directory. // directory. UNC paths are not supported. Defaulting to Windows
// Access is denied. // directory. Access is denied." -Quoth CMD.EXE
// //
// Remove \\?\ prefix if we're within 260 character limit. // Remove \\?\ prefix if we're within the 260 character limit.
if (n > 4 && n < 260 && // if (n > 4 && n < 260 && //
path16[0] == '\\' && // path16[0] == '\\' && //
path16[1] == '\\' && // path16[1] == '\\' && //
@ -194,10 +210,5 @@ textwindows int __mkntpath2(const char *path,
n -= 4; n -= 4;
} }
// turn "foo\\." into "foo\\"
if (n > 2 && path16[n - 1] == u'.' && path16[n - 2] == u'\\') {
path16[--n] = 0;
}
return n; return n;
} }

View file

@ -29,9 +29,10 @@ int __mkntpathat(int dirfd, const char *path, int flags,
char16_t file[hasatleast PATH_MAX]) { char16_t file[hasatleast PATH_MAX]) {
char16_t dir[PATH_MAX]; char16_t dir[PATH_MAX];
uint32_t dirlen, filelen; uint32_t dirlen, filelen;
if (!isutf8(path, -1)) return eilseq(); // thwart overlong nul in conversion
if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1;
if (!filelen) return enoent(); if (!filelen) return enoent();
if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */ if (file[0] != u'\\' && dirfd != AT_FDCWD) { // ProTip: \\?\C:\foo
if (!__isfdkind(dirfd, kFdFile)) return ebadf(); if (!__isfdkind(dirfd, kFdFile)) return ebadf();
dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
kNtFileNameNormalized | kNtVolumeNameDos); kNtFileNameNormalized | kNtVolumeNameDos);

View file

@ -22,34 +22,92 @@
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/createfile.h" #include "libc/nt/createfile.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filetype.h" #include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
uint32_t flags, int32_t mode, uint32_t flags, int32_t mode,
uint32_t extra_attr) { uint32_t extra_attr) {
// join(topath(dirfd), path) and translate from utf-8 to utf-16
char16_t path16[PATH_MAX]; char16_t path16[PATH_MAX];
uint32_t perm, share, disp, attr;
if (__mkntpathat(dirfd, path, flags, path16) == -1) { if (__mkntpathat(dirfd, path, flags, path16) == -1) {
return kNtInvalidHandleValue; return kNtInvalidHandleValue;
} }
// strip trailing slash
size_t n = strlen16(path16);
if (n > 1 && path16[n - 1] == '\\') {
// path denormalization only goes so far. when a trailing / or /.
// exists the kernel interprets that as having O_DIRECTORY intent
// furthermore, windows will throw an error on unc paths with it!
flags |= O_DIRECTORY;
path16[--n] = 0;
}
// implement no follow flag
// you can't open symlinks; use readlink
// this flag only applies to the final path component
// if O_NOFOLLOW_ANY is passed (-1 on NT) it'll be rejected later
uint32_t fattr = __imp_GetFileAttributesW(path16);
if (flags & O_NOFOLLOW) { if (flags & O_NOFOLLOW) {
if ((attr = GetFileAttributes(path16)) != -1u && // if (fattr != -1u && (fattr & kNtFileAttributeReparsePoint)) {
(attr & kNtFileAttributeReparsePoint)) {
return eloop(); return eloop();
} }
flags &= ~O_NOFOLLOW; flags &= ~O_NOFOLLOW; // don't actually pass this to win32
} }
// handle some obvious cases while we have the attributes
// we should ideally resolve symlinks ourself before doing this
if (fattr != -1u) {
if (fattr & kNtFileAttributeDirectory) {
if ((flags & O_ACCMODE) != O_RDONLY || (flags & O_CREAT)) {
// tried to open directory for writing. note that our
// undocumented O_TMPFILE support on windows requires that a
// filename be passed, rather than a directory like linux.
return eisdir();
}
// on posix, the o_directory flag is an advisory safeguard that
// isn't required. on windows, it's mandatory for opening a dir
flags |= O_DIRECTORY;
} else if (!(fattr & kNtFileAttributeReparsePoint)) {
// we know for certain file isn't a directory
if (flags & O_DIRECTORY) {
return enotdir();
}
}
}
// translate posix flags to win32 flags
uint32_t perm, share, disp, attr;
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) { if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) {
return kNtInvalidHandleValue; return kNtInvalidHandleValue;
} }
// kNtTruncateExisting always returns kNtErrorInvalidParameter :'(
if (disp == kNtTruncateExisting) {
if (fattr != -1u) {
disp = kNtCreateAlways; // file exists (wish it could be more atomic)
} else {
return __fix_enotdir(enotdir(), path16);
}
}
// open the file, following symlinks
return __fix_enotdir(CreateFile(path16, perm, share, &kNtIsInheritable, disp, return __fix_enotdir(CreateFile(path16, perm, share, &kNtIsInheritable, disp,
attr | extra_attr, 0), attr | extra_attr, 0),
path16); path16);

View file

@ -24,6 +24,7 @@
#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
@ -56,7 +57,7 @@
* *
* __static_yoink("zipos"); * __static_yoink("zipos");
* *
* Then you can read zip assets by adding a `"/zip/..."` prefix to `file`, e.g. * Then you can read zip assets by adding a `"/zip/..."` prefix to `path`, e.g.
* *
* // run `zip program.com hi.txt` beforehand * // run `zip program.com hi.txt` beforehand
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY); * openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
@ -64,10 +65,12 @@
* Cosmopolitan's general approach on Windows to path translation is to * Cosmopolitan's general approach on Windows to path translation is to
* *
* - replace `/' with `\` * - replace `/' with `\`
* - normalize `.' and `..`
* - translate utf-8 into utf-16 * - translate utf-8 into utf-16
* - turn `"\X\foo"` into `"\\?\X:\foo"` * - turn `"\X\foo"` into `"\\?\X:\foo"`
* - turn `"\X"` into `"\\?\X:\"` * - turn `"\X"` into `"\\?\X:\"`
* - turn `"X:\foo"` into `"\\?\X:\foo"` * - turn `"X:\foo"` into `"\\?\X:\foo"`
* - turn `"\\?\X:\foo"` back into `X:\foo` if less than 260 chars
* *
* On Windows, opening files in `/tmp` will open them in GetTempPath(), * On Windows, opening files in `/tmp` will open them in GetTempPath(),
* which is a secure per-user directory. Opening `/dev/tty` will open a * which is a secure per-user directory. Opening `/dev/tty` will open a
@ -75,31 +78,24 @@
* which can't be fully closed. Opening `/dev/null` will open up `NUL`. * which can't be fully closed. Opening `/dev/null` will open up `NUL`.
* *
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and * @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* `file` names a relative path then it's opened relative to `dirfd` * `path` names a relative path then it's opened relative to `dirfd`
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`, * @param path is a UTF-8 string naming a filesystem entity
* which on Windows is bludgeoned into a WIN32 path automatically, e.g.
* - `foo/bar.txt` becomes `foo\bar.txt`
* - `/tmp/...` becomes whatever GetTempPath() is
* - `\\...` or `//...` is passed through to WIN32 unchanged
* - `/c/foo` or `\c\foo` becomes `\\?\c:\foo`
* - `c:/foo` or `c:\foo` becomes `\\?\c:\foo`
* - `/D` becomes `\\?\D:\`
* @param flags must have one of the following under the `O_ACCMODE` bits: * @param flags must have one of the following under the `O_ACCMODE` bits:
* - `O_RDONLY` to open `file` for reading only * - `O_RDONLY` to open `path` for reading only
* - `O_WRONLY` to open `file` for writing * - `O_WRONLY` to open `path` for writing
* - `O_RDWR` to open `file` for reading and writing * - `O_RDWR` to open `path` for reading and writing
* The following may optionally be bitwise or'd into `flags`: * The following may optionally be bitwise or'd into `flags`:
* - `O_CREAT` create file if it doesn't exist * - `O_CREAT` create file if it doesn't exist
* - `O_TRUNC` automatic `ftruncate(fd,0)` if exists * - `O_TRUNC` automatic `ftruncate(fd,0)` if exists (atomic on unix)
* - `O_CLOEXEC` automatic close() upon execve() * - `O_CLOEXEC` automatic close() upon execve()
* - `O_EXCL` exclusive access (see below) * - `O_EXCL` exclusive access (see below)
* - `O_APPEND` open file for appending only * - `O_APPEND` open file for appending only
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink * - `O_NOFOLLOW` fail with ELOOP if it's a symlink
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block * - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_EXEC` open file for execution only; see fexecve() * - `O_EXEC` open file for execution only; see fexecve()
* - `O_NOCTTY` prevents `file` possibly becoming controlling terminal * - `O_NOCTTY` prevents `path` from becoming the controlling terminal
* - `O_DIRECTORY` advisory feature for avoiding accidentally opening files
* - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD) * - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
* - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
* - `O_DSYNC` it's complicated (zero on non-Linux/Apple) * - `O_DSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_RSYNC` it's complicated (zero on non-Linux/Apple) * - `O_RSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_VERIFY` it's complicated (zero on non-FreeBSD) * - `O_VERIFY` it's complicated (zero on non-FreeBSD)
@ -131,40 +127,48 @@
* the executable bit is set thrice too * the executable bit is set thrice too
* @return file descriptor (which needs to be close()'d), or -1 w/ errno * @return file descriptor (which needs to be close()'d), or -1 w/ errno
* @raise EPERM if pledge() is in play w/o appropriate rpath/wpath/cpath * @raise EPERM if pledge() is in play w/o appropriate rpath/wpath/cpath
* @raise EACCES if unveil() is in play and didn't unveil your `file` path * @raise EACCES if unveil() is in play and didn't unveil your `path` path
* @raise EACCES if we don't have permission to search a component of `file` * @raise EACCES if we don't have permission to search a component of `path`
* @raise EACCES if file exists but requested `flags & O_ACCMODE` was denied * @raise EACCES if file exists but requested `flags & O_ACCMODE` was denied
* @raise EACCES if file doesn't exist and parent dir lacks write permissions * @raise EACCES if file doesn't exist and parent dir lacks write permissions
* @raise EACCES if `O_TRUNC` was specified in `flags` but writing was denied * @raise EACCES if `O_TRUNC` was specified in `flags` but writing was denied
* @raise ENOTSUP if `file` is on zip file system and `dirfd` isn't `AT_FDCWD` * @raise ENOTSUP if `path` is on zip file system and `dirfd` isn't `AT_FDCWD`
* @raise ENOTDIR if a directory component in `file` exists as non-directory * @raise ENOEXEC if `path` is a zip path and this executable isn't a zip file
* @raise ENOTDIR if `file` is relative and `dirfd` isn't an open directory * @raise ENOTDIR if a directory component in `path` exists as non-directory
* @raise EROFS when writing is requested w/ `file` on read-only filesystem * @raise ENOTDIR if `path` ends with a trailing slash and refers to a file
* @raise ENAMETOOLONG if symlink-resolved `file` length exceeds `PATH_MAX` * @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
* @raise ENAMETOOLONG if component in `file` exists longer than `NAME_MAX` * @raise ENOTDIR if `path` isn't a directory and `O_DIRECTORY` was passed
* @raise ENOTSUP if `file` is on zip file system and process is vfork()'d * @raise EILSEQ if `path` contains illegal UTF-8 sequences (Windows/MacOS)
* @raise ENOSPC if file system is full when `file` would be `O_CREAT`ed * @raise EROFS when writing is requested w/ `path` on read-only filesystem
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise ENAMETOOLONG if `path` is relative and longer than 260 characters
* @raise ENOTSUP if `path` is on zip file system and process is vfork()'d
* @raise ENOSPC if file system is full when `path` would be `O_CREAT`ed
* @raise EINTR if we needed to block and a signal was delivered instead * @raise EINTR if we needed to block and a signal was delivered instead
* @raise EEXIST if `O_CREAT|O_EXCL` are used and `file` already existed * @raise EEXIST if `O_CREAT|O_EXCL` are used and `path` already existed
* @raise EINVAL if ASCII control codes are used in `path` on Windows
* @raise EINVAL if `O_TRUNC` is specified in `O_RDONLY` mode
* @raise EINVAL if `flags` contains unsupported bits
* @raise ECANCELED if thread was cancelled in masked mode * @raise ECANCELED if thread was cancelled in masked mode
* @raise ENOENT if `file` doesn't exist when `O_CREAT` isn't in `flags` * @raise ENOENT if `path` doesn't exist when `O_CREAT` isn't in `flags`
* @raise ENOENT if `file` points to a string that's empty * @raise ENOENT if `path` points to a string that's empty
* @raise ENOMEM if insufficient memory was available * @raise ENOMEM if insufficient memory was available
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached * @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached * @raise ENFILE if system-wide file limit has been reached
* @raise EOPNOTSUPP if `file` names a named socket * @raise EOPNOTSUPP if `path` names a named socket
* @raise EFAULT if `file` points to invalid memory * @raise EFAULT if `path` points to invalid memory
* @raise ETXTBSY if writing is requested on `file` that's being executed * @raise ETXTBSY if writing is requested on `path` that's being executed
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `file` is a symbolic link * @raise ELOOP if `flags` had `O_NOFOLLOW` and `path` is a symbolic link
* @raise ELOOP if a loop was detected resolving components of `file` * @raise ELOOP if a loop was detected resolving components of `path`
* @raise EISDIR if writing is requested and `file` names a directory * @raise EISDIR if writing is requested and `path` names a directory
* @cancellationpoint * @cancellationpoint
* @asyncsignalsafe * @asyncsignalsafe
* @restartable * @restartable
* @threadsafe * @threadsafe
* @vforksafe * @vforksafe
*/ */
int openat(int dirfd, const char *file, int flags, ...) { int openat(int dirfd, const char *path, int flags, ...) {
int rc; int rc;
va_list va; va_list va;
unsigned mode; unsigned mode;
@ -174,32 +178,48 @@ int openat(int dirfd, const char *file, int flags, ...) {
va_end(va); va_end(va);
BEGIN_CANCELLATION_POINT; BEGIN_CANCELLATION_POINT;
if (file && (!IsAsan() || __asan_is_valid_str(file))) { if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
if (!__isfdkind(dirfd, kFdZip)) {
if (_weaken(__zipos_open) &&
_weaken(__zipos_parseuri)(file, &zipname) != -1) {
if (!__vforked && dirfd == AT_FDCWD) {
rc = _weaken(__zipos_open)(&zipname, flags);
} else {
rc = enotsup(); /* TODO */
}
} else if (!IsWindows() && !IsMetal()) {
rc = sys_openat(dirfd, file, flags, mode);
} else if (IsMetal()) {
rc = sys_openat_metal(dirfd, file, flags, mode);
} else {
rc = sys_open_nt(dirfd, file, flags, mode);
}
} else {
rc = enotsup(); /* TODO */
}
} else {
rc = efault(); rc = efault();
} else if (__isfdkind(dirfd, kFdZip)) {
rc = enotsup(); // TODO
} else if (_weaken(__zipos_open) &&
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
if (!__vforked && dirfd == AT_FDCWD) {
rc = _weaken(__zipos_open)(&zipname, flags);
} else {
rc = enotsup(); // TODO
}
} else if ((flags & O_ACCMODE) == O_RDONLY && (flags & O_TRUNC)) {
rc = einval(); // Every OS except OpenBSD actually does this D:
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_openat(dirfd, path, flags, mode);
if (IsFreebsd()) {
// Address FreeBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used, but fails due to symlink.
if (rc == -1 && errno == EMLINK) {
errno = ELOOP;
}
}
if (IsNetbsd()) {
// Address NetBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used but fails due to symlink.
if (rc == -1 && errno == EFTYPE) {
errno = ELOOP;
}
}
} else if (IsMetal()) {
rc = sys_openat_metal(dirfd, path, flags, mode);
} else if (IsWindows()) {
rc = sys_open_nt(dirfd, path, flags, mode);
} else {
rc = enosys();
} }
END_CANCELLATION_POINT; END_CANCELLATION_POINT;
STRACE("openat(%s, %#s, %s, %#o) → %d% m", DescribeDirfd(dirfd), file, STRACE("openat(%s, %#s, %s, %#o) → %d% m", DescribeDirfd(dirfd), path,
DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0,
rc); rc);
return rc; return rc;
} }
__strong_reference(openat, openat64);

View file

@ -58,11 +58,11 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable, if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable,
kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) { kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) {
g_fds.p[reader].kind = kFdFile; g_fds.p[reader].kind = kFdFile;
g_fds.p[reader].flags = flags; g_fds.p[reader].flags = O_RDONLY | flags;
g_fds.p[reader].mode = 0010444; g_fds.p[reader].mode = 0010444;
g_fds.p[reader].handle = hin; g_fds.p[reader].handle = hin;
g_fds.p[writer].kind = kFdFile; g_fds.p[writer].kind = kFdFile;
g_fds.p[writer].flags = flags; g_fds.p[writer].flags = O_WRONLY | flags;
g_fds.p[writer].mode = 0010222; g_fds.p[writer].mode = 0010222;
g_fds.p[writer].handle = hout; g_fds.p[writer].handle = hout;
pipefd[0] = reader; pipefd[0] = reader;

View file

@ -30,7 +30,8 @@
* This function offers atomic operation on all supported platforms * This function offers atomic operation on all supported platforms
* except for XNU and RHEL5 where it's polyfilled. * except for XNU and RHEL5 where it's polyfilled.
* *
* @params flags may contain `O_CLOEXEC`, `O_NONBLOCK`, and `O_DIRECT` * @params flags may contain `O_CLOEXEC`, `O_NONBLOCK`, or the non-POSIX
* packet mode flag `O_DIRECT`, which is `EINVAL` on MacOS / OpenBSD
* @raise EINVAL if flags has invalid or unsupported bits * @raise EINVAL if flags has invalid or unsupported bits
* @raise EFAULT if `pipefd` doesn't point to valid memory * @raise EFAULT if `pipefd` doesn't point to valid memory
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached * @raise EMFILE if process `RLIMIT_NOFILE` has been reached
@ -41,7 +42,7 @@
*/ */
int pipe2(int pipefd[hasatleast 2], int flags) { int pipe2(int pipefd[hasatleast 2], int flags) {
int rc; int rc;
if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT)) { if (flags & ~(O_CLOEXEC | O_NONBLOCK | (O_DIRECT != -1u ? O_DIRECT : 0))) {
return einval(); return einval();
} else if (!pipefd || } else if (!pipefd ||
(IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2))) { (IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2))) {

View file

@ -18,6 +18,8 @@
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
@ -41,13 +43,16 @@ int sys_fadvise_netbsd(int, int, int64_t, int64_t, int) asm("sys_fadvise");
* @raise EBADF if `fd` isn't a valid file descriptor * @raise EBADF if `fd` isn't a valid file descriptor
* @raise EINVAL if `advice` is invalid or `len` is huge * @raise EINVAL if `advice` is invalid or `len` is huge
* @raise ESPIPE if `fd` refers to a pipe * @raise ESPIPE if `fd` refers to a pipe
* @raise ENOTSUP if `fd` is a /zip file
* @raise ENOSYS on XNU and OpenBSD * @raise ENOSYS on XNU and OpenBSD
* @returnserrno * @returnserrno
* @threadsafe * @threadsafe
*/ */
errno_t posix_fadvise(int fd, int64_t offset, int64_t len, int advice) { errno_t posix_fadvise(int fd, int64_t offset, int64_t len, int advice) {
int rc, e = errno; int rc, e = errno;
if (IsLinux()) { if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsup();
} else if (IsLinux()) {
rc = sys_fadvise(fd, offset, len, advice); rc = sys_fadvise(fd, offset, len, advice);
} else if (IsFreebsd()) { } else if (IsFreebsd()) {
rc = sys_fadvise(fd, offset, len, advice); rc = sys_fadvise(fd, offset, len, advice);

View file

@ -29,8 +29,8 @@
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h" #include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
/** /**
* Reads from file at offset. * Reads from file at offset.
@ -71,6 +71,8 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
(struct iovec[]){{buf, size}}, 1, offset); (struct iovec[]){{buf, size}}, 1, offset);
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_pread(fd, buf, size, offset, offset); rc = sys_pread(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile)) { } else if (__isfdkind(fd, kFdFile)) {
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset);
} else { } else {

View file

@ -59,7 +59,11 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
if (IsWindows()) { if (IsWindows()) {
if (fd < g_fds.n) { if (fd < g_fds.n) {
return sys_read_nt(g_fds.p + fd, iov, iovlen, off); if (g_fds.p[fd].kind == kFdSocket) {
return espipe();
} else {
return sys_read_nt(g_fds.p + fd, iov, iovlen, off);
}
} else { } else {
return ebadf(); return ebadf();
} }

View file

@ -64,6 +64,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
rc = ebadf(); rc = ebadf();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_pwrite(fd, buf, size, offset, offset); rc = sys_pwrite(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile)) { } else if (__isfdkind(fd, kFdFile)) {
rc = sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, offset); rc = sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, offset);
} else { } else {

View file

@ -54,7 +54,11 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
if (IsWindows()) { if (IsWindows()) {
if (fd < g_fds.n) { if (fd < g_fds.n) {
return sys_write_nt(fd, iov, iovlen, off); if (g_fds.p[fd].kind == kFdSocket) {
return espipe();
} else {
return sys_write_nt(fd, iov, iovlen, off);
}
} else { } else {
return ebadf(); return ebadf();
} }

View file

@ -17,17 +17,74 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/movefileexflags.h" #include "libc/nt/enum/movefileexflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
__msabi extern typeof(RemoveDirectory) *const __imp_RemoveDirectoryW;
static textwindows bool StripTrailingSlash(char16_t *path) {
size_t n = strlen16(path);
bool had_trailing_slash = false;
if (n > 1 && path[n - 1] == '\\') {
had_trailing_slash = true;
path[--n] = 0;
}
return had_trailing_slash;
}
textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) { const char *newpath) {
// translate unix to windows paths
char16_t oldpath16[PATH_MAX]; char16_t oldpath16[PATH_MAX];
char16_t newpath16[PATH_MAX]; char16_t newpath16[PATH_MAX];
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 ||
__mkntpathat(newdirfd, newpath, 0, newpath16) == -1) { __mkntpathat(newdirfd, newpath, 0, newpath16) == -1) {
return -1; return -1;
} }
// strip trailing slash
// win32 will einval otherwise
// normally this is handled by __fix_enotdir()
bool old_must_be_dir = StripTrailingSlash(oldpath16);
bool new_must_be_dir = StripTrailingSlash(newpath16);
// test for some known error conditions ahead of time
// the enotdir check can't be done reactively
// ideally we should resolve symlinks first
uint32_t oldattr = __imp_GetFileAttributesW(oldpath16);
uint32_t newattr = __imp_GetFileAttributesW(newpath16);
if ((old_must_be_dir && oldattr != -1u &&
!(oldattr & kNtFileAttributeDirectory)) ||
(new_must_be_dir && newattr != -1u &&
!(newattr & kNtFileAttributeDirectory))) {
return enotdir();
}
if (oldattr != -1u && newattr != -1u) {
if (!(oldattr & kNtFileAttributeDirectory) &&
(newattr & kNtFileAttributeDirectory)) {
return eisdir(); // new is directory, but old isn't a directory
} else if ((oldattr & kNtFileAttributeDirectory) &&
!(newattr & kNtFileAttributeDirectory)) {
return enotdir(); // old is directory, but new isn't a directory
} else if ((oldattr & kNtFileAttributeDirectory) &&
(newattr & kNtFileAttributeDirectory)) {
// both old and new are directories
if (!__imp_RemoveDirectoryW(newpath16) &&
GetLastError() == kNtErrorDirNotEmpty) {
return enotempty();
}
}
}
if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) {
return 0; return 0;
} else { } else {

View file

@ -30,6 +30,7 @@ struct timeval timeval_add(struct timeval, struct timeval) pureconst;
struct timeval timeval_sub(struct timeval, struct timeval) pureconst; struct timeval timeval_sub(struct timeval, struct timeval) pureconst;
struct timeval timeval_subz(struct timeval, struct timeval) pureconst; struct timeval timeval_subz(struct timeval, struct timeval) pureconst;
int64_t timeval_toseconds(struct timeval); int64_t timeval_toseconds(struct timeval);
int64_t timeval_tomicros(struct timeval);
struct timeval timespec_totimeval(struct timespec) pureconst; struct timeval timespec_totimeval(struct timespec) pureconst;
static inline struct timeval timeval_fromseconds(int64_t __x) { static inline struct timeval timeval_fromseconds(int64_t __x) {
return (struct timeval){__x}; return (struct timeval){__x};

40
libc/calls/ualarm.c Normal file
View file

@ -0,0 +1,40 @@
/*-*- 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/timeval.h"
#include "libc/sysv/consts/itimer.h"
/**
* Sets microsecond-precision alarm.
*
* This generates a `SIGALRM` signal after `usecs` microseconds. If
* `reload` is non-zero, then it'll keep generating `SIGALRM` every
* `reload` microseconds after the first signal.
*
* @asyncsignalsafe
*/
unsigned ualarm(unsigned usecs, unsigned reload) {
struct itimerval it, old;
it.it_value = timeval_frommicros(usecs);
it.it_interval = timeval_frommicros(reload);
npassert(!setitimer(ITIMER_REAL, &it, &old));
return timeval_tomicros(old.it_value);
}

View file

@ -33,48 +33,7 @@
#include "libc/nt/synchronization.h" #include "libc/nt/synchronization.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Performs synchronization on directory of pathname.
*
* This code is intended to help prevent subsequent i/o operations
* from failing for no reason at all. For example a unit test that
* repeatedly opens and unlinks the same filename.
*/
static textwindows int SyncDirectory(int df, char16_t path[PATH_MAX], int n) {
int rc;
int64_t fh;
char16_t *p;
if ((p = memrchr16(path, '\\', n))) {
if (p - path == 2 && path[1] == ':') return 0; // XXX: avoid syncing volume
*p = 0;
} else {
if (df != AT_FDCWD) {
if (FlushFileBuffers(df)) {
return 0;
} else {
return -1;
}
}
path[0] = '.';
path[1] = 0;
}
if ((fh = CreateFile(
path, kNtFileGenericWrite,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
kNtOpenExisting, kNtFileAttributeNormal | kNtFileFlagBackupSemantics,
0)) != -1) {
if (FlushFileBuffers(fh)) {
rc = 0;
} else {
rc = -1;
}
CloseHandle(fh);
} else {
rc = -1;
}
return rc;
}
static textwindows bool IsDirectorySymlink(const char16_t *path) { static textwindows bool IsDirectorySymlink(const char16_t *path) {
int e; int e;
@ -102,13 +61,11 @@ static textwindows int sys_rmdir_nt(const char16_t *path) {
if (RemoveDirectory(path)) { if (RemoveDirectory(path)) {
return 0; return 0;
} }
/* // Files can linger, for absolutely no reason.
* Files can linger, for absolutely no reason. // Possibly some Windows Defender bug on Win7.
* Possibly some Windows Defender bug on Win7. // Sleep for up to one second w/ expo backoff.
* Sleep for up to one second w/ expo backoff. // Alternative is use Microsoft internal APIs.
* Alternative is use Microsoft internal APIs. // Never could have imagined it'd be this bad.
* Never could have imagined it'd be this bad.
*/
if (GetLastError() == kNtErrorDirNotEmpty && ms <= 2048) { if (GetLastError() == kNtErrorDirNotEmpty && ms <= 2048) {
errno = e; errno = e;
Sleep(ms); Sleep(ms);
@ -130,19 +87,45 @@ static textwindows int sys_unlink_nt(const char16_t *path) {
} }
} }
textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) { textwindows int sys_unlinkat_nt_impl(const char16_t *path, int flags) {
int n, rc; if (flags & AT_REMOVEDIR) {
char16_t path16[PATH_MAX]; return sys_rmdir_nt(path);
if ((n = __mkntpathat(dirfd, path, 0, path16)) == -1) {
rc = -1;
} else if (flags & AT_REMOVEDIR) {
rc = sys_rmdir_nt(path16);
} else { } else {
rc = sys_unlink_nt(path16); return sys_unlink_nt(path);
if (rc != -1) { }
// TODO(jart): prove that it helps first }
// rc = SyncDirectory(dirfd, path16, n);
textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) {
char16_t path16[PATH_MAX];
// check validity of flags
if (flags & ~AT_REMOVEDIR) {
return einval();
}
// translate unix to windows path
int n;
if ((n = __mkntpathat(dirfd, path, 0, path16)) == -1) {
return -1;
}
// optimistic first attempt
int e = errno;
int rc = sys_unlinkat_nt_impl(path16, flags);
// reactively ensure unlink() deletes read-only files
if (rc == -1 && errno == kNtErrorAccessDenied) {
uint32_t attr;
if ((attr = GetFileAttributes(path16)) != -1u &&
(attr & kNtFileAttributeReadonly) &&
SetFileAttributes(path16, attr & ~kNtFileAttributeReadonly)) {
errno = e;
rc = sys_unlinkat_nt_impl(path16, flags);
} else {
errno = kNtErrorAccessDenied;
} }
} }
// return status
return __fix_enotdir(rc, path16); return __fix_enotdir(rc, path16);
} }

View file

@ -28,7 +28,7 @@ typedef int64_t register_t;
typedef uint16_t sa_family_t; /* bsd:uint8_t */ typedef uint16_t sa_family_t; /* bsd:uint8_t */
typedef uint32_t socklen_t; typedef uint32_t socklen_t;
typedef uint32_t speed_t; typedef uint32_t speed_t;
typedef int64_t suseconds_t; /* int32_t on xnu */ typedef uint32_t suseconds_t;
typedef uint64_t useconds_t; /* uint32_t on xnu */ typedef uint64_t useconds_t; /* uint32_t on xnu */
typedef int64_t syscall_arg_t; /* uint64_t on xnu */ typedef int64_t syscall_arg_t; /* uint64_t on xnu */
typedef uint32_t tcflag_t; typedef uint32_t tcflag_t;

View file

@ -26,11 +26,13 @@
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/errors.h" #include "libc/nt/errors.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/overlapped.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
@ -42,11 +44,21 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
bool32 ok; bool32 ok;
uint32_t sent; uint32_t sent;
int64_t handle; int64_t handle;
if (g_fds.p[fd].kind == kFdConsole) { if (g_fds.p[fd].kind == kFdConsole) {
handle = g_fds.p[fd].extra; // get write end of console handle = g_fds.p[fd].extra; // get write end of console
} else { } else {
handle = g_fds.p[fd].handle; handle = g_fds.p[fd].handle;
} }
// don't use pread() or pwrite() on a pipe
if (offset != -1) {
uint32_t filetype = GetFileType(handle);
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
return espipe();
}
}
size = MIN(size, 0x7ffff000); size = MIN(size, 0x7ffff000);
if (offset == -1) { if (offset == -1) {
// perform simple blocking write // perform simple blocking write

View file

@ -66,6 +66,7 @@ TryAgain:
if ((dwDesiredAccess & kNtGenericExecute) && if ((dwDesiredAccess & kNtGenericExecute) &&
(dwCreationDisposition == kNtOpenExisting || (dwCreationDisposition == kNtOpenExisting ||
dwCreationDisposition == kNtTruncateExisting)) { dwCreationDisposition == kNtTruncateExisting)) {
NTTRACE("CreateFile removed kNtGenericExecute");
dwDesiredAccess &= ~kNtGenericExecute; dwDesiredAccess &= ~kNtGenericExecute;
goto TryAgain; goto TryAgain;
} }

View file

@ -20,50 +20,59 @@
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/filesharemode.h" #include "libc/nt/enum/filesharemode.h"
// clang-format off
static const struct DescribeFlags kFileAccessflags[] = { static const struct DescribeFlags kFileAccessflags[] = {
{kNtFileAllAccess, "FileAllAccess"}, // order matters {kNtFileAllAccess, "kNtFileAllAccess"},
{kNtFileGenericRead, "FileGenericRead"}, // order matters {kNtFileGenericRead|kNtFileGenericWrite|kNtFileGenericExecute,
{kNtFileGenericWrite, "FileGenericWrite"}, // order matters "kNtFileGenericRead|kNtFileGenericWrite|kNtFileGenericExecute"},
{kNtFileGenericExecute, "FileGenericExecute"}, // order matters {kNtFileGenericRead|kNtFileGenericWrite,
{kNtGenericRead, "GenericRead"}, // "kNtFileGenericRead|kNtFileGenericWrite"},
{kNtGenericWrite, "GenericWrite"}, // {kNtFileGenericRead|kNtFileGenericExecute,
{kNtGenericExecute, "GenericExecute"}, // "kNtFileGenericRead|kNtFileGenericExecute"},
{kNtGenericAll, "GenericAll"}, // {kNtFileGenericWrite|kNtFileGenericExecute,
{kNtDelete, "Delete"}, // "kNtFileGenericWrite|kNtFileGenericExecute"},
{kNtReadControl, "ReadControl"}, // {kNtFileGenericRead, "kNtFileGenericRead"},
{kNtWriteDac, "WriteDac"}, // {kNtFileGenericWrite, "kNtFileGenericWrite"},
{kNtWriteOwner, "WriteOwner"}, // {kNtFileGenericExecute, "kNtFileGenericExecute"},
{kNtSynchronize, "Synchronize"}, // {kNtGenericRead, "kNtGenericRead"},
{kNtStandardRightsRequired, "StandardRightsRequired"}, // {kNtGenericWrite, "kNtGenericWrite"},
{kNtAccessSystemSecurity, "AccessSystemSecurity"}, // {kNtGenericExecute, "kNtGenericExecute"},
{kNtMaximumAllowed, "MaximumAllowed"}, // {kNtGenericAll, "kNtGenericAll"},
{kNtFileReadData, "FileReadData"}, // {kNtDelete, "kNtDelete"},
{kNtFileListDirectory, "FileListDirectory"}, // {kNtReadControl, "kNtReadControl"},
{kNtFileWriteData, "FileWriteData"}, // {kNtWriteDac, "kNtWriteDac"},
{kNtFileAddFile, "FileAddFile"}, // {kNtWriteOwner, "kNtWriteOwner"},
{kNtFileAppendData, "FileAppendData"}, // {kNtSynchronize, "kNtSynchronize"},
{kNtFileAddSubdirectory, "FileAddSubdirectory"}, // {kNtStandardRightsRequired, "kNtStandardRightsRequired"},
{kNtFileCreatePipeInstance, "FileCreatePipeInstance"}, // {kNtAccessSystemSecurity, "kNtAccessSystemSecurity"},
{kNtFileReadEa, "FileReadEa"}, // {kNtMaximumAllowed, "kNtMaximumAllowed"},
{kNtFileWriteEa, "FileWriteEa"}, // {kNtFileReadData, "kNtFileReadData"},
{kNtFileExecute, "FileExecute"}, // {kNtFileListDirectory, "kNtFileListDirectory"},
{kNtFileTraverse, "FileTraverse"}, // {kNtFileWriteData, "kNtFileWriteData"},
{kNtFileDeleteChild, "FileDeleteChild"}, // {kNtFileAddFile, "kNtFileAddFile"},
{kNtFileReadAttributes, "FileReadAttributes"}, // {kNtFileAppendData, "kNtFileAppendData"},
{kNtFileWriteAttributes, "FileWriteAttributes"}, // {kNtFileAddSubdirectory, "kNtFileAddSubdirectory"},
{kNtTokenAssignPrimary, "TokenAssignPrimary"}, // {kNtFileCreatePipeInstance, "kNtFileCreatePipeInstance"},
{kNtTokenDuplicate, "TokenDuplicate"}, // {kNtFileReadEa, "kNtFileReadEa"},
{kNtTokenImpersonate, "TokenImpersonate"}, // {kNtFileWriteEa, "kNtFileWriteEa"},
{kNtTokenQuery, "TokenQuery"}, // {kNtFileExecute, "kNtFileExecute"},
{kNtTokenQuerySource, "TokenQuerySource"}, // {kNtFileTraverse, "kNtFileTraverse"},
{kNtTokenAdjustPrivileges, "TokenAdjustPrivileges"}, // {kNtFileDeleteChild, "kNtFileDeleteChild"},
{kNtTokenAdjustGroups, "TokenAdjustGroups"}, // {kNtFileReadAttributes, "kNtFileReadAttributes"},
{kNtTokenAdjustDefault, "TokenAdjustDefault"}, // {kNtFileWriteAttributes, "kNtFileWriteAttributes"},
{kNtTokenAdjustSessionid, "TokenAdjustSessionid"}, // {kNtTokenAssignPrimary, "kNtTokenAssignPrimary"},
{kNtTokenDuplicate, "kNtTokenDuplicate"},
{kNtTokenImpersonate, "kNtTokenImpersonate"},
{kNtTokenQuery, "kNtTokenQuery"},
{kNtTokenQuerySource, "kNtTokenQuerySource"},
{kNtTokenAdjustPrivileges, "kNtTokenAdjustPrivileges"},
{kNtTokenAdjustGroups, "kNtTokenAdjustGroups"},
{kNtTokenAdjustDefault, "kNtTokenAdjustDefault"},
{kNtTokenAdjustSessionid, "kNtTokenAdjustSessionid"},
}; };
const char *(DescribeNtFileAccessFlags)(char buf[512], uint32_t x) { const char *(DescribeNtFileAccessFlags)(char buf[512], uint32_t x) {
return DescribeFlags(buf, 512, kFileAccessflags, ARRAYLEN(kFileAccessflags), return DescribeFlags(buf, 512, kFileAccessflags, ARRAYLEN(kFileAccessflags),
"kNt", x); "", x);
} }

View file

@ -36,12 +36,16 @@
* @asyncsignalsafe * @asyncsignalsafe
* @threadsafe * @threadsafe
*/ */
privileged char *strsignal_r(int sig, char buf[hasatleast 15]) { privileged dontdiscard char *strsignal_r(int sig, char buf[15]) {
int i; int i;
char *p; char *p;
const char *s; const char *s;
if (!sig) return "0"; if (!sig) {
if ((s = GetMagnumStr(kSignalNames, sig))) return s; return "0";
}
if ((s = GetMagnumStr(kSignalNames, sig))) {
return s;
}
if (SIGRTMIN <= sig && sig <= SIGRTMAX) { if (SIGRTMIN <= sig && sig <= SIGRTMAX) {
sig -= SIGRTMIN; sig -= SIGRTMIN;
buf[0] = 'S'; buf[0] = 'S';

View file

@ -18,8 +18,7 @@ int64_t CreateFileA(
uint32_t dwFlagsAndAttributes, /* libc/nt/enum/fileflagandattributes.h */ uint32_t dwFlagsAndAttributes, /* libc/nt/enum/fileflagandattributes.h */
int64_t opt_hTemplateFile) paramsnonnull((1)); int64_t opt_hTemplateFile) paramsnonnull((1));
int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm, uint32_t *out_share, int GetNtOpenFlags(int, int, uint32_t *, uint32_t *, uint32_t *, uint32_t *);
uint32_t *out_disp, uint32_t *out_attr);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -20,6 +20,7 @@
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/atomic.h" #include "libc/atomic.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/calls/struct/ucontext-netbsd.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/wincrash.internal.h" #include "libc/calls/wincrash.internal.h"
@ -195,6 +196,9 @@ XnuThreadMain(void *pthread, // rdi
func(arg, tid); func(arg, tid);
// avoid signal handler being triggered after we trash our stack
_sigblockall();
// we no longer use the stack after this point // we no longer use the stack after this point
// %rax = int bsdthread_terminate(%rdi = void *stackaddr, // %rax = int bsdthread_terminate(%rdi = void *stackaddr,
// %rsi = size_t freesize, // %rsi = size_t freesize,
@ -234,6 +238,8 @@ static wontreturn void FreebsdThreadMain(void *p) {
struct CloneArgs *wt = p; struct CloneArgs *wt = p;
*wt->ctid = wt->tid; *wt->ctid = wt->tid;
wt->func(wt->arg, wt->tid); wt->func(wt->arg, wt->tid);
// avoid signal handler being triggered after we trash our stack
_sigblockall();
// we no longer use the stack after this point // we no longer use the stack after this point
// void thr_exit(%rdi = long *state); // void thr_exit(%rdi = long *state);
asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0 asm volatile("movl\t$0,%0\n\t" // *wt->ztid = 0
@ -349,6 +355,8 @@ static wontreturn void NetbsdThreadMain(void *arg, // rdi
ax = sys_gettid(); ax = sys_gettid();
*ctid = ax; *ctid = ax;
func(arg, ax); func(arg, ax);
// avoid signal handler being triggered after we trash our stack
_sigblockall();
// we no longer use the stack after this point // we no longer use the stack after this point
// %eax = int __lwp_exit(void); // %eax = int __lwp_exit(void);
asm volatile("movl\t$0,%2\n\t" // *wt->ztid = 0 asm volatile("movl\t$0,%2\n\t" // *wt->ztid = 0

View file

@ -34,7 +34,6 @@ size_t __zipos_normpath(char *d, const char *s, size_t n) {
s[0] == '.' && // s[0] == '.' && //
(!s[1] || s[1] == '/')) { (!s[1] || s[1] == '/')) {
// matched "/./" or "^.$" or "^./" or "/.$" // matched "/./" or "^.$" or "^./" or "/.$"
s += !!s[1];
} else if ((p == d || p[-1] == '/') && // } else if ((p == d || p[-1] == '/') && //
s[0] == '.' && // s[0] == '.' && //
s[1] == '.' && // s[1] == '.' && //

View file

@ -76,7 +76,7 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
} else if (__isfdkind(fd, kFdSocket)) { } else if (__isfdkind(fd, kFdSocket)) {
rc = sys_sendto_nt(fd, msg->msg_iov, msg->msg_iovlen, flags, rc = sys_sendto_nt(fd, msg->msg_iov, msg->msg_iovlen, flags,
msg->msg_name, msg->msg_namelen); msg->msg_name, msg->msg_namelen);
} else if (__isfdkind(fd, kFdFile)) { } else if (__isfdkind(fd, kFdFile)) { // e.g. socketpair
rc = sys_write_nt(fd, msg->msg_iov, msg->msg_iovlen, -1); rc = sys_write_nt(fd, msg->msg_iov, msg->msg_iovlen, -1);
} else { } else {
rc = enotsock(); rc = enotsock();

View file

@ -80,12 +80,12 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) {
if (h1 != -1) { if (h1 != -1) {
g_fds.p[reader].kind = kFdFile; g_fds.p[reader].kind = kFdFile;
g_fds.p[reader].flags = oflags; g_fds.p[reader].flags = O_RDWR | oflags;
g_fds.p[reader].mode = 0140444; g_fds.p[reader].mode = 0140444;
g_fds.p[reader].handle = hpipe; g_fds.p[reader].handle = hpipe;
g_fds.p[writer].kind = kFdFile; g_fds.p[writer].kind = kFdFile;
g_fds.p[writer].flags = oflags; g_fds.p[writer].flags = O_RDWR | oflags;
g_fds.p[writer].mode = 0140222; g_fds.p[writer].mode = 0140222;
g_fds.p[writer].handle = h1; g_fds.p[writer].handle = h1;

View file

@ -21,7 +21,7 @@
/** /**
* Returns true if buffer is most likely plaintext. * Returns true if buffer is most likely plaintext.
*/ */
bool _istext(const void *data, size_t size) { bool istext(const void *data, size_t size) {
const unsigned char *p, *pe; const unsigned char *p, *pe;
for (p = data, pe = p + size; p < pe; ++p) { for (p = data, pe = p + size; p < pe; ++p) {
if (*p <= 3) { if (*p <= 3) {

View file

@ -21,8 +21,6 @@
#include "libc/intrin/likely.h" #include "libc/intrin/likely.h"
#include "libc/str/str.h" #include "libc/str/str.h"
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
static const char kUtf8Dispatch[] = { static const char kUtf8Dispatch[] = {
0, 0, 1, 1, 1, 1, 1, 1, // 0300 utf8-2 0, 0, 1, 1, 1, 1, 1, 1, // 0300 utf8-2
1, 1, 1, 1, 1, 1, 1, 1, // 0310 1, 1, 1, 1, 1, 1, 1, 1, // 0310
@ -37,10 +35,10 @@ static const char kUtf8Dispatch[] = {
/** /**
* Returns true if text is utf-8. * Returns true if text is utf-8.
* *
* _isutf8 n=0 1 nanoseconds * isutf8 n=0 1 nanoseconds
* _isutf8 n=5 661 ps/byte 1,476 mb/s * isutf8 n=5 661 ps/byte 1,476 mb/s
* _isutf8 ascii n=22851 26 ps/byte 35 GB/s * isutf8 ascii n=22851 26 ps/byte 35 GB/s
* _isutf8 unicode n=3193 543 ps/byte 1,795 mb/s * isutf8 unicode n=3193 543 ps/byte 1,795 mb/s
* *
* This function considers all ASCII characters including NUL to be * This function considers all ASCII characters including NUL to be
* valid UTF-8. The conditions for something not being valid are: * valid UTF-8. The conditions for something not being valid are:
@ -51,7 +49,7 @@ static const char kUtf8Dispatch[] = {
* *
* @param size if -1 implies strlen * @param size if -1 implies strlen
*/ */
dontasan bool _isutf8(const void *data, size_t size) { dontasan bool isutf8(const void *data, size_t size) {
long c; long c;
unsigned m; unsigned m;
const char *p, *e; const char *p, *e;
@ -61,6 +59,7 @@ dontasan bool _isutf8(const void *data, size_t size) {
e = p + size; e = p + size;
while (p < e) { while (p < e) {
#if defined(__x86_64__) && !defined(__chibicc__) #if defined(__x86_64__) && !defined(__chibicc__)
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
if (!((intptr_t)p & 15)) { if (!((intptr_t)p & 15)) {
for (;;) { for (;;) {
if ((m = __builtin_ia32_pmovmskb128(*(xmm_t *)p >= (xmm_t){0}) ^ if ((m = __builtin_ia32_pmovmskb128(*(xmm_t *)p >= (xmm_t){0}) ^

View file

@ -11,6 +11,8 @@
#define chomp16 _chomp16 #define chomp16 _chomp16
#define wchomp _wchomp #define wchomp _wchomp
#define tpenc _tpenc #define tpenc _tpenc
#define isutf8 _isutf8
#define istext _istext
#define startswith _startswith #define startswith _startswith
#define startswithi _startswithi #define startswithi _startswithi
#define endswith _endswith #define endswith _endswith
@ -187,9 +189,9 @@ wchar_t *wchomp(wchar_t *) libcesque;
bool startswith(const char *, const char *) strlenesque; bool startswith(const char *, const char *) strlenesque;
bool startswithi(const char *, const char *) strlenesque; bool startswithi(const char *, const char *) strlenesque;
bool endswith(const char *, const char *) strlenesque; bool endswith(const char *, const char *) strlenesque;
bool _istext(const void *, size_t) libcesque; bool istext(const void *, size_t) libcesque;
bool _isutf8(const void *, size_t) libcesque; bool isutf8(const void *, size_t) libcesque;
char *strsignal_r(int, char[hasatleast 15]) returnsnonnull libcesque; char *strsignal_r(int, char[15]) returnsnonnull libcesque dontdiscard;
int strerror_wr(int, uint32_t, char *, size_t) int strerror_wr(int, uint32_t, char *, size_t)
dontthrow nocallback; dontthrow nocallback;
char16_t *chomp16(char16_t *) libcesque; char16_t *chomp16(char16_t *) libcesque;

View file

@ -185,30 +185,28 @@ syscon open O_CREAT 0x00000040 0x00000040 0x00000200 0x00000200 0x000002
syscon open O_EXCL 0x00000080 0x00000080 0x00000800 0x00000800 0x00000800 0x00000800 0x00000800 0x00000080 # bsd consensus & NT faked as Linux [SYNC libc/calls/open-nt.c] syscon open O_EXCL 0x00000080 0x00000080 0x00000800 0x00000800 0x00000800 0x00000800 0x00000800 0x00000080 # bsd consensus & NT faked as Linux [SYNC libc/calls/open-nt.c]
syscon open O_TRUNC 0x00000200 0x00000200 0x00000400 0x00000400 0x00000400 0x00000400 0x00000400 0x00000200 # bsd consensus & NT faked as Linux [SYNC libc/calls/open-nt.c] syscon open O_TRUNC 0x00000200 0x00000200 0x00000400 0x00000400 0x00000400 0x00000400 0x00000400 0x00000200 # bsd consensus & NT faked as Linux [SYNC libc/calls/open-nt.c]
syscon open O_DIRECTORY 0x00010000 0x00004000 0x00100000 0x00100000 0x00020000 0x00020000 0x00200000 0x00010000 # useful hint on UNIX, but required on NT (see kNtFileFlagBackupSemantics) [SYNC libc/calls/open-nt.c] syscon open O_DIRECTORY 0x00010000 0x00004000 0x00100000 0x00100000 0x00020000 0x00020000 0x00200000 0x00010000 # useful hint on UNIX, but required on NT (see kNtFileFlagBackupSemantics) [SYNC libc/calls/open-nt.c]
syscon open O_NOFOLLOW 0x00020000 0x00008000 0x00000100 0x00000100 0x00000100 0x00000100 0x00000100 0x00020000 # bsd consensus; kNtFileFlagOpenReparsePoint syscon open O_NOFOLLOW 0x00020000 0x00008000 0x00000100 0x00000100 0x00000100 0x00000100 0x00000100 0x00020000 # don't follow symlinks in the final path component; bsd consensus; kNtFileFlagOpenReparsePoint
syscon open O_DIRECT 0x00004000 0x00010000 0 0 0x00010000 0 0x00080000 0x00004000 # kNtFileFlagNoBuffering [SYNC libc/calls/open-nt.c] syscon open O_DIRECT 0x00004000 0x00010000 0xffffffff 0xffffffff 0x00010000 0xffffffff 0x00080000 0x00004000 # kNtFileFlagNoBuffering [SYNC libc/calls/open-nt.c]
syscon open O_NDELAY 0x00000800 0x00000800 0x00000004 0x00000004 0x00000004 0x00000004 0x00000004 0x00000800 # kNtFileFlagWriteThrough [SYNC libc/calls/open-nt.c] syscon open O_NONBLOCK 0x00000800 0x00000800 0x00000004 0x00000004 0x00000004 0x00000004 0x00000004 0x00000800 # same as O_NDELAY; overlaps with kNtFileFlagWriteThrough which we don't actually pass to win32 (we implement non-blocking ourselves using overlapped i/o)
syscon open O_RANDOM 0 0 0 0 0 0 0 0x80000000 # kNtFileFlagRandomAccess [SYNC libc/calls/open-nt.c] syscon open O_RANDOM 0 0 0 0 0 0 0 0x80000000 # kNtFileFlagRandomAccess [SYNC libc/calls/open-nt.c]
syscon open O_SEQUENTIAL 0 0 0 0 0 0 0 0x40000000 # kNtFileFlagSequentialScan [SYNC libc/calls/open-nt.c] syscon open O_SEQUENTIAL 0 0 0 0 0 0 0 0x40000000 # kNtFileFlagSequentialScan [SYNC libc/calls/open-nt.c]
syscon open O_COMPRESSED 0 0 0 0 0 0 0 0x20000000 # kNtFileAttributeCompressed [SYNC libc/calls/open-nt.c] syscon open O_COMPRESSED 0 0 0 0 0 0 0 0x20000000 # kNtFileAttributeCompressed [SYNC libc/calls/open-nt.c]
syscon open O_INDEXED 0 0 0 0 0 0 0 0x10000000 # !kNtFileAttributeNotContentIndexed [SYNC libc/calls/open-nt.c] syscon open O_INDEXED 0 0 0 0 0 0 0 0x10000000 # !kNtFileAttributeNotContentIndexed [SYNC libc/calls/open-nt.c]
syscon open O_CLOEXEC 0x00080000 0x00080000 0x01000000 0x01000000 0x00100000 0x00010000 0x00400000 0x00080000 # NT faked as Linux [SYNC libc/calls/open-nt.c] syscon open O_CLOEXEC 0x00080000 0x00080000 0x01000000 0x01000000 0x00100000 0x00010000 0x00400000 0x00080000 # NT faked as Linux [SYNC libc/calls/open-nt.c]
syscon open O_TMPFILE 0x00410000 0x00404000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff # please use tmpfd(); Linux 3.11+ (c. 2013) __O_TMPFILE | O_DIRECTORY; kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose [SYNC libc/calls/open-nt.c] syscon open O_TMPFILE 0x00410000 0x00404000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff # please use tmpfd(); Linux 3.11+ (c. 2013) __O_TMPFILE | O_DIRECTORY; kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose [SYNC libc/calls/open-nt.c]
syscon open O_SPARSE 0 0 0 0 0 0 0 0 # wut syscon open O_ASYNC 0x00002000 0x00002000 0x00000040 0x00000040 0x00000040 0x00000040 0x00000040 0xffffffff # bsd consensus
syscon open O_NONBLOCK 0x00000800 0x00000800 0x00000004 0x00000004 0x00000004 0x00000004 0x00000004 0x00000800 # bsd consensus syscon open O_NOFOLLOW_ANY 0xffffffff 0xffffffff 0x20000000 0x20000000 0xffffffff 0xffffffff 0xffffffff 0xffffffff # don't follow symlinks in any path component
syscon open O_ASYNC 0x00002000 0x00002000 0x00000040 0x00000040 0x00000040 0x00000040 0x00000040 0 # bsd consensus syscon open O_SYNC 0x00101000 0x00101000 0x00000080 0x00000080 0x00000080 0x00000080 0x00000080 0xffffffff # bsd consensus
syscon open O_NOFOLLOW_ANY 0 0 0x20000000 0x20000000 0 0 0 0 #
syscon open O_SYNC 0x00101000 0x00101000 0x00000080 0x00000080 0x00000080 0x00000080 0x00000080 0 # bsd consensus
syscon open O_NOCTTY 0x00000100 0x00000100 0x00020000 0x00020000 0x00008000 0x00008000 0x00008000 0 # used for remote viewing (default behavior on freebsd) syscon open O_NOCTTY 0x00000100 0x00000100 0x00020000 0x00020000 0x00008000 0x00008000 0x00008000 0 # used for remote viewing (default behavior on freebsd)
syscon open O_NOATIME 0x00040000 0x00040000 0 0 0 0 0 0 # optimize away access time update syscon open O_NOATIME 0x00040000 0x00040000 0 0 0 0 0 0 # optimize away access time update
syscon open O_EXEC 0x00200000 0x00200000 0 0x40000000 0x00040000 0 0x04000000 0 # open only for executing (POSIX.1 hack for when file mode is 0111); see fexecve(); O_PATH on Linux syscon open O_EXEC 0x00200000 0x00200000 0 0x40000000 0x00040000 0 0x04000000 0 # open only for executing (POSIX.1 hack for when file mode is 0111); see fexecve(); O_PATH on Linux
syscon open O_SEARCH 0 0 0 0x40100000 0x00040000 0 0x00800000 0 # it's specified by posix what does it mean syscon open O_SEARCH 0xffffffff 0xffffffff 0xffffffff 0x40100000 0x00040000 0xffffffff 0x00800000 0xffffffff # it's specified by posix what does it mean
syscon open O_DSYNC 0x00001000 0x00001000 0x00400000 0x00400000 0 0x00000080 0x00010000 0 # syscon open O_DSYNC 0x00001000 0x00001000 0x00400000 0x00400000 0xffffffff 0x00000080 0x00010000 0xffffffff #
syscon open O_RSYNC 0x00101000 0x00101000 0 0 0 0x00000080 0x00020000 0 # syscon open O_RSYNC 0x00101000 0x00101000 0xffffffff 0xffffffff 0xffffffff 0x00000080 0x00020000 0xffffffff #
syscon open O_PATH 0x00200000 0x00200000 0 0 0 0 0 0 # Linux 2.6.39+ syscon open O_PATH 0x00200000 0x00200000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff # Linux 2.6.39+
syscon open O_VERIFY 0 0 0 0 0x00200000 0 0 0 # syscon open O_VERIFY 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0x00200000 0xffffffff 0xffffffff 0xffffffff #
syscon open O_SHLOCK 0 0 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0 # syscon open O_SHLOCK 0xffffffff 0xffffffff 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0xffffffff #
syscon open O_EXLOCK 0 0 0x00000020 0x00000020 0x00000020 0x00000020 0x00000020 0 # syscon open O_EXLOCK 0xffffffff 0xffffffff 0x00000020 0x00000020 0x00000020 0x00000020 0x00000020 0xffffffff #
syscon open O_TTY_INIT 0 0 0 0 0x00080000 0 0 0 # syscon open O_TTY_INIT 0 0 0 0 0x00080000 0 0 0 #
syscon compat O_LARGEFILE 0x00008000 0x00020000 0 0 0 0 0 0 # syscon compat O_LARGEFILE 0x00008000 0x00020000 0 0 0 0 0 0 #
@ -1047,7 +1045,7 @@ syscon limits MAX_INPUT 255 255 1024 1024 255 255 255 255 # w
syscon limits SOMAXCONN 4096 4096 128 128 128 128 128 2147483647 # maximum backlog for listen() syscon limits SOMAXCONN 4096 4096 128 128 128 128 128 2147483647 # maximum backlog for listen()
syscon limits _ARG_MAX 128*1024 128*1024 1024*1024 1024*1024 512*1024 512*1024 256*1024 32767*2 # bsd consensus syscon limits _ARG_MAX 128*1024 128*1024 1024*1024 1024*1024 512*1024 512*1024 256*1024 32767*2 # bsd consensus
syscon limits _NAME_MAX 255 255 255 255 255 255 511 255 # probably higher on windows? syscon limits _NAME_MAX 255 255 255 255 255 255 511 255 # probably higher on windows?
syscon limits _PATH_MAX 4096 4096 1024 1024 1024 1024 1024 512 # cosmopolitan libc imposes a lower 512 limit; nt theoretically goes up to 32767 syscon limits _PATH_MAX 4096 4096 1024 1024 1024 1024 1024 32767 # win32 paths are 260 characters max. even with unc paths, cosmo wrappers won't go beyond 1024 chars
syscon limits _NSIG 64 64 32 32 128 32 64 64 # _SIG_MAXSIG on FreeBSD syscon limits _NSIG 64 64 32 32 128 32 64 64 # _SIG_MAXSIG on FreeBSD
# unmount() flags # unmount() flags

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_ASYNC,0x00002000,0x00002000,0x00000040,0x00000040,0x00000040,0x00000040,0x00000040,0 .syscon open,O_ASYNC,0x00002000,0x00002000,0x00000040,0x00000040,0x00000040,0x00000040,0x00000040,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_DIRECT,0x00004000,0x00010000,0,0,0x00010000,0,0x00080000,0x00004000 .syscon open,O_DIRECT,0x00004000,0x00010000,0xffffffff,0xffffffff,0x00010000,0xffffffff,0x00080000,0x00004000

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_DSYNC,0x00001000,0x00001000,0x00400000,0x00400000,0,0x00000080,0x00010000,0 .syscon open,O_DSYNC,0x00001000,0x00001000,0x00400000,0x00400000,0xffffffff,0x00000080,0x00010000,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_EXLOCK,0,0,0x00000020,0x00000020,0x00000020,0x00000020,0x00000020,0 .syscon open,O_EXLOCK,0xffffffff,0xffffffff,0x00000020,0x00000020,0x00000020,0x00000020,0x00000020,0xffffffff

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_NDELAY,0x00000800,0x00000800,0x00000004,0x00000004,0x00000004,0x00000004,0x00000004,0x00000800

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_NOFOLLOW_ANY,0,0,0x20000000,0x20000000,0,0,0,0 .syscon open,O_NOFOLLOW_ANY,0xffffffff,0xffffffff,0x20000000,0x20000000,0xffffffff,0xffffffff,0xffffffff,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_PATH,0x00200000,0x00200000,0,0,0,0,0,0 .syscon open,O_PATH,0x00200000,0x00200000,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_RSYNC,0x00101000,0x00101000,0,0,0,0x00000080,0x00020000,0 .syscon open,O_RSYNC,0x00101000,0x00101000,0xffffffff,0xffffffff,0xffffffff,0x00000080,0x00020000,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_SEARCH,0,0,0,0x40100000,0x00040000,0,0x00800000,0 .syscon open,O_SEARCH,0xffffffff,0xffffffff,0xffffffff,0x40100000,0x00040000,0xffffffff,0x00800000,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_SHLOCK,0,0,0x00000010,0x00000010,0x00000010,0x00000010,0x00000010,0 .syscon open,O_SHLOCK,0xffffffff,0xffffffff,0x00000010,0x00000010,0x00000010,0x00000010,0x00000010,0xffffffff

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_SPARSE,0,0,0,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_SYNC,0x00101000,0x00101000,0x00000080,0x00000080,0x00000080,0x00000080,0x00000080,0 .syscon open,O_SYNC,0x00101000,0x00101000,0x00000080,0x00000080,0x00000080,0x00000080,0x00000080,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon open,O_VERIFY,0,0,0,0,0x00200000,0,0,0 .syscon open,O_VERIFY,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0x00200000,0xffffffff,0xffffffff,0xffffffff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon limits,_PATH_MAX,4096,4096,1024,1024,1024,1024,1024,512 .syscon limits,_PATH_MAX,4096,4096,1024,1024,1024,1024,1024,32767

View file

@ -22,7 +22,6 @@ extern const unsigned O_EXEC;
extern const unsigned O_EXLOCK; extern const unsigned O_EXLOCK;
extern const unsigned O_INDEXED; extern const unsigned O_INDEXED;
extern const unsigned O_LARGEFILE; extern const unsigned O_LARGEFILE;
extern const unsigned O_NDELAY;
extern const unsigned O_NOATIME; extern const unsigned O_NOATIME;
extern const unsigned O_NOCTTY; extern const unsigned O_NOCTTY;
extern const unsigned O_NOFOLLOW; extern const unsigned O_NOFOLLOW;
@ -34,7 +33,6 @@ extern const unsigned O_RSYNC;
extern const unsigned O_SEARCH; extern const unsigned O_SEARCH;
extern const unsigned O_SEQUENTIAL; extern const unsigned O_SEQUENTIAL;
extern const unsigned O_SHLOCK; extern const unsigned O_SHLOCK;
extern const unsigned O_SPARSE;
extern const unsigned O_SYNC; extern const unsigned O_SYNC;
extern const unsigned O_TMPFILE; /* use tmpfd() or tmpfile() */ extern const unsigned O_TMPFILE; /* use tmpfd() or tmpfile() */
extern const unsigned O_TRUNC; extern const unsigned O_TRUNC;
@ -46,13 +44,12 @@ extern const unsigned O_VERIFY;
#define O_CLOEXEC O_CLOEXEC #define O_CLOEXEC O_CLOEXEC
#define O_COMPRESSED O_COMPRESSED #define O_COMPRESSED O_COMPRESSED
#define O_CREAT O_CREAT #define O_CREAT O_CREAT
#define O_DIRECT O_DIRECT
#define O_DIRECTORY O_DIRECTORY #define O_DIRECTORY O_DIRECTORY
#define O_EXCL O_EXCL #define O_EXCL O_EXCL
#define O_EXEC O_EXEC #define O_EXEC O_EXEC
#define O_INDEXED O_INDEXED #define O_INDEXED O_INDEXED
#define O_LARGEFILE O_LARGEFILE #define O_LARGEFILE O_LARGEFILE
#define O_NDELAY O_NDELAY #define O_NDELAY O_NONBLOCK
#define O_NOATIME O_NOATIME #define O_NOATIME O_NOATIME
#define O_NOCTTY O_NOCTTY #define O_NOCTTY O_NOCTTY
#define O_NOFOLLOW O_NOFOLLOW #define O_NOFOLLOW O_NOFOLLOW

View file

@ -18,6 +18,8 @@ extern const int SOCK_STREAM;
#define SOCK_RAW 3 #define SOCK_RAW 3
#define SOCK_RDM 4 #define SOCK_RDM 4
#define SOCK_SEQPACKET 5 #define SOCK_SEQPACKET 5
#define SOCK_CLOEXEC SOCK_CLOEXEC
#define SOCK_NONBLOCK SOCK_NONBLOCK
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -53,6 +53,7 @@ dos kNtErrorInvalidAddress EADDRNOTAVAIL
dos kNtErrorNotAReparsePoint EINVAL dos kNtErrorNotAReparsePoint EINVAL
dos kNtErrorInvalidFunction EINVAL dos kNtErrorInvalidFunction EINVAL
dos kNtErrorNegativeSeek EINVAL dos kNtErrorNegativeSeek EINVAL
dos kNtErrorInvalidName EINVAL
dos kNtErrorInvalidNetname EADDRNOTAVAIL dos kNtErrorInvalidNetname EADDRNOTAVAIL
dos kNtErrorInvalidUserBuffer EMSGSIZE dos kNtErrorInvalidUserBuffer EMSGSIZE
dos kNtErrorIoPending EINPROGRESS dos kNtErrorIoPending EINPROGRESS

View file

@ -14,4 +14,5 @@ kDos2Errno.EINVAL:
.e kNtErrorNotAReparsePoint,EINVAL .e kNtErrorNotAReparsePoint,EINVAL
.e kNtErrorInvalidFunction,EINVAL .e kNtErrorInvalidFunction,EINVAL
.e kNtErrorNegativeSeek,EINVAL .e kNtErrorNegativeSeek,EINVAL
.e kNtErrorInvalidName,EINVAL
.e WSAEINVAL,EINVAL .e WSAEINVAL,EINVAL

View file

@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_
* Test cases are guaranteed by the linker to be run in order, sorted by * Test cases are guaranteed by the linker to be run in order, sorted by
* the (SUITE, NAME) tuple passed here. * the (SUITE, NAME) tuple passed here.
*/ */
#define TEST(SUITE, NAME) \ #define TEST(SUITE, NAME) \
__static_yoink("__testcase_start"); \ __static_yoink("__testcase_start"); \
__TEST_PROTOTYPE(SUITE, NAME, __TEST_ARRAY, ) __TEST_PROTOTYPE(SUITE, NAME, __TEST_ARRAY, )
@ -25,7 +25,7 @@ COSMOPOLITAN_C_START_
* temorarilly by the runtime while calling fixture functions. Fixtures * temorarilly by the runtime while calling fixture functions. Fixtures
* are also guaranteed by the linker to be run in sorted order. * are also guaranteed by the linker to be run in sorted order.
*/ */
#define FIXTURE(SUITE, NAME) \ #define FIXTURE(SUITE, NAME) \
__static_yoink("__fixture_start"); \ __static_yoink("__fixture_start"); \
__FIXTURE("fixture", SUITE, NAME) __FIXTURE("fixture", SUITE, NAME)
@ -36,7 +36,7 @@ COSMOPOLITAN_C_START_
* Cartesian product of groups. That makes this similar to fixture, but * Cartesian product of groups. That makes this similar to fixture, but
* more appropriate for testing pure code (i.e. no syscalls) like math. * more appropriate for testing pure code (i.e. no syscalls) like math.
*/ */
#define COMBO(GROUP, ENTRY) \ #define COMBO(GROUP, ENTRY) \
__static_yoink("__combo_start"); \ __static_yoink("__combo_start"); \
__FIXTURE("combo", GROUP, ENTRY) __FIXTURE("combo", GROUP, ENTRY)
@ -49,7 +49,7 @@ COSMOPOLITAN_C_START_
* *
* @see EZBENCH() * @see EZBENCH()
*/ */
#define BENCH(SUITE, NAME) \ #define BENCH(SUITE, NAME) \
__static_yoink("__bench_start"); \ __static_yoink("__bench_start"); \
__TEST_PROTOTYPE(SUITE, NAME, __BENCH_ARRAY, optimizespeed) __TEST_PROTOTYPE(SUITE, NAME, __BENCH_ARRAY, optimizespeed)
@ -223,11 +223,12 @@ void TearDownOnce(void);
#define EXPECT_SYS(ERRNO, WANT, GOT, ...) \ #define EXPECT_SYS(ERRNO, WANT, GOT, ...) \
do { \ do { \
testlib_seterrno(0); \ int e = testlib_geterrno(); \
__TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, \ __TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, \
GOT, __VA_ARGS__); \ GOT, __VA_ARGS__); \
__TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #ERRNO, \ __TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #ERRNO, \
testlib_strerror(), ERRNO, testlib_geterrno(), __VA_ARGS__); \ testlib_strerror(), ERRNO, testlib_geterrno(), __VA_ARGS__); \
testlib_seterrno(e); \
} while (0) } while (0)
#define EXPECT_FALSE(X) _TEST2("EXPECT_FALSE", false, ==, (X), #X, "", "", 0) #define EXPECT_FALSE(X) _TEST2("EXPECT_FALSE", false, ==, (X), #X, "", "", 0)
@ -347,8 +348,6 @@ struct TestFixture {
}; };
extern char g_fixturename[256]; extern char g_fixturename[256];
extern char g_testlib_olddir[PATH_MAX];
extern char g_testlib_tmpdir[PATH_MAX];
extern bool g_testlib_shoulddebugbreak; /* set by testmain */ extern bool g_testlib_shoulddebugbreak; /* set by testmain */
extern _Atomic(unsigned) g_testlib_ran; /* set by wrappers */ extern _Atomic(unsigned) g_testlib_ran; /* set by wrappers */
extern _Atomic(unsigned) g_testlib_failed; /* set by wrappers */ extern _Atomic(unsigned) g_testlib_failed; /* set by wrappers */

View file

@ -16,42 +16,27 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/log/check.h" #include "libc/limits.h"
#include "libc/log/internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/process.h" #include "libc/nt/process.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h" #include "libc/stdio/rand.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/x/x.h" #include "libc/x/x.h"
static int x; static char g_olddir[PATH_MAX];
char g_testlib_olddir[PATH_MAX]; static char g_tmpdir[PATH_MAX];
char g_testlib_tmpdir[PATH_MAX];
struct sigaction wanthandlers[31];
static pthread_mutex_t testlib_error_lock; static pthread_mutex_t testlib_error_lock;
void testlib_finish(void) { void testlib_finish(void) {
@ -85,139 +70,64 @@ wontreturn void testlib_abort(void) {
} }
static void SetupTmpDir(void) { static void SetupTmpDir(void) {
char *p = g_testlib_tmpdir; char number[21];
p = stpcpy(p, kTmpPath); FormatInt64(number, _rand64() & INT64_MAX);
p = stpcpy(p, program_invocation_short_name), *p++ = '.'; g_tmpdir[0] = 0;
p = FormatInt64(p, getpid()), *p++ = '.'; if (*kTmpPath != '/') {
p = FormatInt64(p, x++); strlcat(g_tmpdir, g_olddir, sizeof(g_tmpdir));
p[0] = '\0'; strlcat(g_tmpdir, "/", sizeof(g_tmpdir));
CHECK_NE(-1, makedirs(g_testlib_tmpdir, 0755), "%s", g_testlib_tmpdir); }
CHECK_NOTNULL(realpath(g_testlib_tmpdir, g_testlib_tmpdir), "%`'s", strlcat(g_tmpdir, kTmpPath, sizeof(g_tmpdir));
g_testlib_tmpdir); strlcat(g_tmpdir, program_invocation_short_name, sizeof(g_tmpdir));
CHECK_NE(-1, chdir(g_testlib_tmpdir), "%s", g_testlib_tmpdir); strlcat(g_tmpdir, ".", sizeof(g_tmpdir));
strlcat(g_tmpdir, number, sizeof(g_tmpdir));
if (makedirs(g_tmpdir, 0755) || chdir(g_tmpdir)) {
perror(g_tmpdir);
exit(1);
}
} }
static void TearDownTmpDir(void) { static void TearDownTmpDir(void) {
CHECK_NE(-1, chdir(g_testlib_olddir)); if (chdir(g_olddir)) {
CHECK_NE(-1, rmrf(g_testlib_tmpdir), "%s", g_testlib_tmpdir); perror(g_olddir);
} exit(1);
static void DoNothing(int sig) {
// function intentionally empty
}
static void CopySignalHandlers(void) {
#if 0
int i;
for (i = 0; i < ARRAYLEN(wanthandlers); ++i) {
if (i + 1 == SIGKILL || i + 1 == SIGSTOP) continue;
CHECK_EQ(0, sigaction(i + 1, 0, wanthandlers + i), "sig=%d", i + 1);
} }
#endif if (rmrf(g_tmpdir)) {
} perror(g_tmpdir);
exit(1);
static void CheckSignalHandler(int sig) {
#if 0
int i;
struct sigaction sa = {0};
unassert(0 <= sig - 1 && sig - 1 < ARRAYLEN(wanthandlers));
CHECK_EQ(0, sigaction(sig, 0, &sa));
CHECK_EQ(0, memcmp(wanthandlers + sig - 1, &sa, sizeof(sa)),
"signal handler for %s was %p/%#x/%#x:%x "
"but should have been restored to %p/%#x/%#x:%x",
strsignal(sig), sa.sa_handler, sa.sa_flags, sa.sa_mask.__bits[0],
sa.sa_mask.__bits[1], wanthandlers[sig - 1].sa_handler,
wanthandlers[sig - 1].sa_flags,
wanthandlers[sig - 1].sa_mask.__bits[0],
wanthandlers[sig - 1].sa_mask.__bits[1]);
#endif
}
static void CheckForSignalHandlers(void) {
#if 0
CheckSignalHandler(SIGINT);
CheckSignalHandler(SIGQUIT);
CheckSignalHandler(SIGCHLD);
CheckSignalHandler(SIGFPE);
CheckSignalHandler(SIGILL);
CheckSignalHandler(SIGSEGV);
CheckSignalHandler(SIGTRAP);
CheckSignalHandler(SIGABRT);
CheckSignalHandler(SIGBUS);
CheckSignalHandler(SIGSYS);
CheckSignalHandler(SIGWINCH);
#endif
}
static void CheckForFileDescriptors(void) {
#if 0
// TODO: race condition on fd cleanup :'(
int i;
struct pollfd pfds[16];
if (!_weaken(open) && !_weaken(socket)) return;
for (i = 0; i < ARRAYLEN(pfds); ++i) {
pfds[i].fd = i + 3;
pfds[i].events = POLLIN;
} }
if (poll(pfds, ARRAYLEN(pfds), 0) > 0) {
for (i = 0; i < ARRAYLEN(pfds); ++i) {
if (pfds[i].revents & POLLNVAL) continue;
++g_testlib_failed;
kprintf("error: test failed to close() fd %d\n", pfds[i].fd);
}
}
#endif
}
static void CheckForZombies(void) {
#if 0
int e, pid;
sigset_t ss, oldss;
struct sigaction oldsa;
struct sigaction ignore = {.sa_handler = DoNothing};
if (!_weaken(fork)) return;
for (;;) {
if ((pid = wait(0)) == -1) {
CHECK_EQ(ECHILD, errno);
break;
} else {
++g_testlib_failed;
kprintf("error: test failed to reap zombies %d\n", pid);
}
}
#endif
} }
/** /**
* Runs all test case functions in sorted order. * Runs all test case functions in sorted order.
*/ */
void testlib_runtestcases(testfn_t *start, testfn_t *end, testfn_t warmup) { void testlib_runtestcases(testfn_t *start, testfn_t *end, testfn_t warmup) {
/* // getpid() calls are inserted to help visually see tests in traces
* getpid() calls are inserted to help visually see tests in traces // which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
* which can be performed on Linux, FreeBSD, OpenBSD, and XNU: //
* // strace -f o/default/test.com |& less
* strace -f o/default/test.com |& less // truss o/default/test.com |& less
* truss o/default/test.com |& less // ktrace -f trace o/default/test.com </dev/null; kdump -f trace | less
* ktrace -f trace o/default/test.com </dev/null; kdump -f trace | less // dtruss o/default/test.com |& less
* dtruss o/default/test.com |& less //
* // Test cases are iterable via a decentralized section. Your TEST()
* Test cases are iterable via a decentralized section. Your TEST() // macro inserts .testcase.SUITENAME sections into the binary which
* macro inserts .testcase.SUITENAME sections into the binary which // the linker sorts into an array.
* the linker sorts into an array. //
* // @see ape/ape.lds
* @see ape/ape.lds
*/
const testfn_t *fn; const testfn_t *fn;
CopySignalHandlers();
if (_weaken(testlib_enable_tmp_setup_teardown) || if (_weaken(testlib_enable_tmp_setup_teardown) ||
_weaken(testlib_enable_tmp_setup_teardown_once)) { _weaken(testlib_enable_tmp_setup_teardown_once)) {
CHECK_NOTNULL(getcwd(g_testlib_olddir, sizeof(g_testlib_olddir))); if (!getcwd(g_olddir, sizeof(g_olddir))) {
perror("getcwd");
exit(1);
}
} }
if (_weaken(testlib_enable_tmp_setup_teardown_once)) { if (_weaken(testlib_enable_tmp_setup_teardown_once)) {
SetupTmpDir(); SetupTmpDir();
} }
if (_weaken(SetUpOnce)) _weaken(SetUpOnce)(); if (_weaken(SetUpOnce)) _weaken(SetUpOnce)();
for (x = 0, fn = start; fn != end; ++fn) { for (fn = start; fn != end; ++fn) {
STRACE(""); STRACE("");
STRACE("# setting up %t", fn); STRACE("# setting up %t", fn);
if (_weaken(testlib_enable_tmp_setup_teardown)) SetupTmpDir(); if (_weaken(testlib_enable_tmp_setup_teardown)) SetupTmpDir();
@ -237,9 +147,6 @@ void testlib_runtestcases(testfn_t *start, testfn_t *end, testfn_t warmup) {
if (!IsWindows()) sys_getpid(); if (!IsWindows()) sys_getpid();
if (_weaken(TearDown)) _weaken(TearDown)(); if (_weaken(TearDown)) _weaken(TearDown)();
if (_weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir(); if (_weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir();
CheckForFileDescriptors();
CheckForSignalHandlers();
CheckForZombies();
} }
if (_weaken(TearDownOnce)) { if (_weaken(TearDownOnce)) {
_weaken(TearDownOnce)(); _weaken(TearDownOnce)();

View file

@ -17,10 +17,19 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/stdio/ftw.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "libc/stdio/ftw.h"
static inline bool IsSlash(char c) {
return c == '/' || c == '\\';
}
static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
static int rmrf_callback(const char *fpath, // static int rmrf_callback(const char *fpath, //
const struct stat *st, // const struct stat *st, //
@ -48,6 +57,12 @@ static int rmrf_callback(const char *fpath, //
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
*/ */
int rmrf(const char *path) { int rmrf(const char *path) {
if (path[0] == '/' && !path[1]) return enotsup(); if ((IsSlash(path[0]) && !path[1]) ||
(IsWindows() && ((IsSlash(path[0]) && IsAlpha(path[1]) &&
(!path[2] || (IsSlash(path[2]) && !path[3]))) ||
(IsAlpha(path[0]) && path[1] == ':' &&
(!path[2] || (IsSlash(path[2]) && !path[3])))))) {
return enotsup(); // if you really want rmrf("/") try rmrf("/.")
}
return nftw(path, rmrf_callback, 128, FTW_PHYS | FTW_DEPTH); return nftw(path, rmrf_callback, 128, FTW_PHYS | FTW_DEPTH);
} }

View file

@ -65,8 +65,25 @@ TEST(fcntl_getfl, testRemembersAccessMode) {
EXPECT_NE(-1, close(fd)); EXPECT_NE(-1, close(fd));
} }
TEST(fcntl_setfl, testChangeAppendStatus) { TEST(fcntl_setfl, testChangeAppendStatus_proper) {
if (IsWindows()) return; // Can't ReOpenFile() w/ O_APPEND char buf[8] = {0};
ASSERT_SYS(0, 3, open("foo", O_CREAT | O_WRONLY, 0644));
// F_GETFL on XNU reports FWASWRITTEN (0x00010000) after write()
int old = fcntl(3, F_GETFL);
EXPECT_SYS(0, 3, write(3, "foo", 3));
EXPECT_SYS(0, 0, lseek(3, 0, SEEK_SET));
EXPECT_SYS(0, 0, fcntl(3, F_SETFL, old | O_APPEND));
EXPECT_SYS(0, 3, write(3, "bar", 3));
EXPECT_SYS(0, 0, lseek(3, 0, SEEK_SET));
EXPECT_SYS(0, 0, fcntl(3, F_SETFL, old));
EXPECT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open("foo", 0));
EXPECT_SYS(0, 6, read(3, buf, 6));
EXPECT_STREQ("foobar", buf);
EXPECT_SYS(0, 0, close(3));
}
TEST(fcntl_setfl, testChangeAppendStatus_sloppy) {
char buf[8] = {0}; char buf[8] = {0};
ASSERT_SYS(0, 3, open("foo", O_CREAT | O_WRONLY, 0644)); ASSERT_SYS(0, 3, open("foo", O_CREAT | O_WRONLY, 0644));
EXPECT_SYS(0, 3, write(3, "foo", 3)); EXPECT_SYS(0, 3, write(3, "foo", 3));

View file

@ -29,6 +29,7 @@
#include "libc/sock/struct/ifreq.h" #include "libc/sock/struct/ifreq.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sio.h" #include "libc/sysv/consts/sio.h"
#include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sock.h"
@ -82,3 +83,18 @@ TEST(siocgifconf, mkntenvblock_systemroot) {
EXITS(0); EXITS(0);
} }
#endif #endif
TEST(fionread, pipe) {
int pfds[2];
int pending;
ASSERT_SYS(0, 0, pipe(pfds));
ASSERT_SYS(0, 2, write(pfds[1], "hi", 2));
// checking the reading end is agreed upon
ASSERT_SYS(0, 0, ioctl(pfds[0], FIONREAD, &pending));
ASSERT_EQ(2, pending);
// checking the writing end is real hairy
// ASSERT_SYS(0, 0, ioctl(pfds[1], FIONREAD, &pending));
// ASSERT_EQ(2, pending);
ASSERT_SYS(0, 0, close(pfds[1]));
ASSERT_SYS(0, 0, close(pfds[0]));
}

View file

@ -23,7 +23,11 @@
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sock.h"
#include "libc/testlib/subprocess.h" #include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -31,7 +35,7 @@
char testlib_enable_tmp_setup_teardown; char testlib_enable_tmp_setup_teardown;
void SetUpOnce(void) { void SetUpOnce(void) {
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr proc", 0)); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr proc inet", 0));
} }
TEST(lseek, ebadf) { TEST(lseek, ebadf) {
@ -59,14 +63,26 @@ TEST(lseek, 64bit) {
EXPECT_SYS(0, 0, close(3)); EXPECT_SYS(0, 0, close(3));
} }
TEST(lseek, nonSeekableFd_espipe) { TEST(lseek, isPipe_ESPIPE) {
int fds[2]; int fds[2];
char buf[2];
ASSERT_SYS(0, 0, pipe(fds)); ASSERT_SYS(0, 0, pipe(fds));
ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET));
ASSERT_SYS(ESPIPE, -1, pwrite(4, "hi", 2, 0));
ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0));
EXPECT_SYS(0, 0, close(4)); EXPECT_SYS(0, 0, close(4));
EXPECT_SYS(0, 0, close(3)); EXPECT_SYS(0, 0, close(3));
} }
TEST(lseek, isSocket_ESPIPE) {
char buf[2];
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET));
ASSERT_SYS(ESPIPE, -1, pwrite(3, "hi", 2, 0));
ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0));
EXPECT_SYS(0, 0, close(3));
}
TEST(lseek, filePositionChanges_areObservableAcrossDup) { TEST(lseek, filePositionChanges_areObservableAcrossDup) {
ASSERT_SYS(0, 3, creat("wut", 0644)); ASSERT_SYS(0, 3, creat("wut", 0644));
ASSERT_SYS(0, 4, dup(3)); ASSERT_SYS(0, 4, dup(3));

View file

@ -18,14 +18,20 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/gc.internal.h" #include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "libc/x/xasprintf.h"
#define abs(rel) gc(xasprintf("%s/%s", gc(getcwd(0, 0)), rel))
char testlib_enable_tmp_setup_teardown; char testlib_enable_tmp_setup_teardown;
@ -113,6 +119,7 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) {
char buf[16] = {0}; char buf[16] = {0};
ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1)); ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1));
ASSERT_SYS(0, 3, open("hello.txt", O_WRONLY | O_APPEND)); ASSERT_SYS(0, 3, open("hello.txt", O_WRONLY | O_APPEND));
EXPECT_SYS(EBADF, -1, pread(3, buf, 4, 0)); // in O_WRONLY mode
EXPECT_SYS(0, 1, write(3, "o", 1)); EXPECT_SYS(0, 1, write(3, "o", 1));
EXPECT_SYS(0, 0, lseek(3, 0, SEEK_SET)); EXPECT_SYS(0, 0, lseek(3, 0, SEEK_SET));
EXPECT_SYS(0, 1, write(3, "!", 1)); EXPECT_SYS(0, 1, write(3, "!", 1));
@ -123,6 +130,50 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) {
EXPECT_SYS(0, 0, close(3)); EXPECT_SYS(0, 0, close(3));
} }
TEST(open, appendRwMode_readsStartZero_writesAlwaysEof) {
char buf[8] = {0};
ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1));
ASSERT_SYS(0, 3, open("hello.txt", O_RDWR | O_APPEND));
ASSERT_SYS(0, 2, read(3, buf, 2));
ASSERT_SYS(0, 2, read(3, buf + 2, 2));
EXPECT_STREQ("hell", buf);
EXPECT_SYS(0, 2, write(3, "o!", 2));
ASSERT_SYS(0, 6, pread(3, buf, 8, 0));
EXPECT_STREQ("hello!", buf);
EXPECT_SYS(0, 0, close(3));
}
TEST(open, appendReadOnlyMode_appendIsIgnored) {
char buf[8] = {0};
ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1));
ASSERT_SYS(0, 3, open("hello.txt", O_RDONLY | O_APPEND));
EXPECT_SYS(EBADF, -1, write(3, "o!", 2)); // due to O_RDONLY
ASSERT_EQ(0, errno);
ASSERT_SYS(0, 2, read(3, buf, 2));
ASSERT_SYS(0, 2, read(3, buf + 2, 2));
EXPECT_STREQ("hell", buf);
ASSERT_SYS(0, 4, pread(3, buf, 4, 0));
EXPECT_SYS(0, 0, close(3));
}
TEST(open, truncReadWriteMode_getsTruncated) {
char buf[8] = {0};
ASSERT_FALSE(fileexists("hello.txt"));
ASSERT_SYS(ENOENT, -1, open("hello.txt", O_RDWR | O_TRUNC));
ASSERT_SYS(ENOENT, -1, open("hello.txt/there", O_RDWR | O_TRUNC));
ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1));
ASSERT_SYS(ENOTDIR, -1, open("hello.txt/there", O_RDWR | O_TRUNC));
ASSERT_SYS(0, 3, open("hello.txt", O_RDWR | O_TRUNC));
ASSERT_SYS(0, 0, read(3, buf, 8));
EXPECT_STREQ("", buf);
ASSERT_SYS(0, 8, write(3, buf, 8));
EXPECT_SYS(0, 0, close(3));
}
TEST(open, truncReadOnlyMode_wontTruncate) {
ASSERT_SYS(EINVAL, -1, open("hello.txt", O_RDONLY | O_TRUNC));
}
TEST(open, testRelativePath_opensRelativeToDirFd) { TEST(open, testRelativePath_opensRelativeToDirFd) {
ASSERT_SYS(0, 0, mkdir("foo", 0755)); ASSERT_SYS(0, 0, mkdir("foo", 0755));
ASSERT_SYS(0, 3, open("foo", O_RDONLY | O_DIRECTORY)); ASSERT_SYS(0, 3, open("foo", O_RDONLY | O_DIRECTORY));
@ -132,6 +183,104 @@ TEST(open, testRelativePath_opensRelativeToDirFd) {
EXPECT_SYS(0, 0, close(3)); EXPECT_SYS(0, 0, close(3));
} }
TEST(open, eloop) {
ASSERT_SYS(0, 0, symlink("froot", "link"));
ASSERT_TRUE(issymlink("link"));
ASSERT_SYS(ELOOP, -1, open("link", O_RDONLY | O_NOFOLLOW));
}
TEST(open, norm) {
ASSERT_SYS(0, 0, mkdir("fun", 0755));
ASSERT_SYS(0, 0, mkdir("fun/house", 0755));
ASSERT_SYS(0, 0, touch("fun/house/norm", 0644));
ASSERT_SYS(0, 3, open("fun//house//norm", O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open(abs("fun//house//norm"), O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open("fun//house/./norm", O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open(abs("fun//house/./norm"), O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open("fun//house/../house/norm", O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, open(abs("fun//house/../house/norm"), O_RDONLY));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(ENOTDIR, -1, open("fun//house//norm/", O_RDONLY));
ASSERT_SYS(ENOTDIR, -1, open("fun//house//norm/.", O_RDONLY));
ASSERT_SYS(ENOTDIR, -1, open("fun//house//norm/./", O_RDONLY));
ASSERT_SYS(0, 3, open("fun//house//", O_RDONLY | O_DIRECTORY));
ASSERT_SYS(0, 0, close(3));
}
TEST(open, longNormDot) {
#define NAME \
"funfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfu" \
"nfunfunfunfunfunfunnfunfunfunfunfunfunnfunfunfunfunfunfununfunfunfunfunfun"
ASSERT_SYS(0, 0, mkdir(NAME, 0755));
ASSERT_SYS(0, 0, mkdir(abs(NAME "/" NAME), 0755));
ASSERT_SYS(0, 3, creat(abs(NAME "//" NAME "/./norm"), 0644));
ASSERT_TRUE(fileexists(abs(NAME "//" NAME "/norm")));
ASSERT_SYS(0, 0, close(3));
}
TEST(open, longNormDotDot) {
#define NAME \
"funfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfunfu" \
"nfunfunfunfunfunfunnfunfunfunfunfunfunnfunfunfunfunfunfununfunfunfunfunfun"
ASSERT_SYS(0, 0, mkdir(NAME, 0755));
ASSERT_SYS(0, 0, mkdir(abs(NAME "/" NAME), 0755));
ASSERT_SYS(0, 0, mkdir(abs(NAME "/" NAME "/" NAME), 0755));
ASSERT_SYS(0, 3, creat(abs(NAME "//" NAME "//" NAME "/../norm"), 0644));
ASSERT_TRUE(fileexists(abs(NAME "//" NAME "/norm")));
ASSERT_SYS(0, 0, close(3));
}
TEST(open, creat_directory) {
ASSERT_SYS(ENOENT, -1, open("fun", O_WRONLY | O_DIRECTORY));
ASSERT_FALSE(fileexists("fun"));
if (1) return; // linux 5.15.122-0-lts creates file and returns error D:
ASSERT_SYS(ENOTDIR, -1, open("fun", O_CREAT | O_WRONLY | O_DIRECTORY, 0644));
ASSERT_TRUE(fileexists("fun"));
}
TEST(open, O_DIRECTORY_preventsOpeningRegularFiles) {
ASSERT_SYS(0, 0, touch("file", 0644));
ASSERT_SYS(ENOTDIR, -1, open("file", O_WRONLY | O_DIRECTORY));
}
TEST(open, O_DIRECTORY_isNotARequirementToOpenDirectory) {
ASSERT_SYS(0, 0, mkdir("dir", 0755));
ASSERT_SYS(0, 3, open("dir", O_RDONLY));
ASSERT_SYS(0, 0, close(3));
}
TEST(open, openExistingDirectoryForWriting_raisesError) {
ASSERT_SYS(0, 0, mkdir("dir", 0755));
ASSERT_SYS(EISDIR, -1, open("dir", O_WRONLY));
}
TEST(open, nameWithControlCode) {
if (IsWindows()) {
ASSERT_SYS(EINVAL, -1, touch("hi\1there", 0755));
} else {
ASSERT_SYS(0, 0, touch("hi\1there", 0755));
}
}
TEST(open, nameWithOverlongNul_doesntCreateTruncatedName) {
if (IsXnu() || IsWindows()) {
// XNU is the only one that thought to restrict this. XNU chose
// EILSEQ which makes the most sense. Linux says it'll raise EINVAL
// if invalid characters are detected. Not sure yet which characters
// those are. POSIX says nothing about invalid charaters in open.
ASSERT_SYS(EILSEQ, -1, touch("hi\300\200there", 0755));
} else {
ASSERT_SYS(0, 0, touch("hi\300\200there", 0755));
ASSERT_TRUE(fileexists("hi\300\200there"));
ASSERT_FALSE(fileexists("hi"));
}
}
int CountFds(void) { int CountFds(void) {
int i, count; int i, count;
for (count = i = 0; i < g_fds.n; ++i) { for (count = i = 0; i < g_fds.n; ++i) {
@ -154,3 +303,18 @@ TEST(open, lotsOfFds) {
EXPECT_SYS(0, 0, close(i)); EXPECT_SYS(0, 0, close(i));
} }
} }
static int64_t GetInode(const char *path) {
struct stat st;
ASSERT_SYS(0, 0, stat(path, &st));
return st.st_ino;
}
TEST(open, drive) {
if (!IsWindows()) return;
ASSERT_NE(GetInode("/"), GetInode("."));
ASSERT_EQ(GetInode("/"), GetInode("/c")); // sorry you have to run on c:/
ASSERT_EQ(GetInode("/"), GetInode("/c/"));
ASSERT_SYS(0, 3, open("/", O_RDONLY));
ASSERT_SYS(0, 0, close(3));
}

View file

@ -447,7 +447,7 @@ TEST(pledge, open_rpath) {
if (!pid) { if (!pid) {
ASSERT_SYS(0, 0, pledge("stdio rpath", 0)); ASSERT_SYS(0, 0, pledge("stdio rpath", 0));
ASSERT_SYS(0, 3, open("foo", O_RDONLY)); ASSERT_SYS(0, 3, open("foo", O_RDONLY));
ASSERT_SYS(EPERM, -1, open("foo", O_RDONLY | O_TRUNC)); ASSERT_SYS(EINVAL, -1, open("foo", O_RDONLY | O_TRUNC));
ASSERT_SYS(EPERM, -1, open("foo", O_RDONLY | O_TMPFILE)); ASSERT_SYS(EPERM, -1, open("foo", O_RDONLY | O_TMPFILE));
ASSERT_SYS(EPERM, -1, open("foo", O_RDWR | O_TRUNC | O_CREAT, 0644)); ASSERT_SYS(EPERM, -1, open("foo", O_RDWR | O_TRUNC | O_CREAT, 0644));
ASSERT_SYS(EPERM, -1, open("foo", O_WRONLY | O_TRUNC | O_CREAT, 0644)); ASSERT_SYS(EPERM, -1, open("foo", O_WRONLY | O_TRUNC | O_CREAT, 0644));

View file

@ -62,9 +62,6 @@ TEST(readlinkat, test) {
EXPECT_EQ(255, buf[8] & 255); EXPECT_EQ(255, buf[8] & 255);
buf[8] = 0; buf[8] = 0;
EXPECT_STREQ("hello→", buf); EXPECT_STREQ("hello→", buf);
p = gc(xjoinpaths(g_testlib_tmpdir, "hello→"));
q = gc(realpath("there→", 0));
EXPECT_EQ(0, strcmp(p, q), "%`'s\n\t%`'s", p, q);
} }
TEST(readlinkat, efault) { TEST(readlinkat, efault) {
@ -89,10 +86,7 @@ TEST(readlinkat, frootloop) {
ASSERT_SYS(0, 0, symlink("froot", "froot")); ASSERT_SYS(0, 0, symlink("froot", "froot"));
ASSERT_SYS(ELOOP, -1, readlink("froot/loop", buf, sizeof(buf))); ASSERT_SYS(ELOOP, -1, readlink("froot/loop", buf, sizeof(buf)));
if (O_NOFOLLOW) { if (O_NOFOLLOW) {
ASSERT_SYS(IsFreebsd() ? EMLINK ASSERT_SYS(ELOOP, -1, open("froot", O_RDONLY | O_NOFOLLOW));
: IsNetbsd() ? EFTYPE
: ELOOP,
-1, open("froot", O_RDONLY | O_NOFOLLOW));
if (0 && O_PATH) { /* need rhel5 test */ if (0 && O_PATH) { /* need rhel5 test */
ASSERT_NE(-1, (fd = open("froot", O_RDONLY | O_NOFOLLOW | O_PATH))); ASSERT_NE(-1, (fd = open("froot", O_RDONLY | O_NOFOLLOW | O_PATH)));
ASSERT_NE(-1, close(fd)); ASSERT_NE(-1, close(fd));

View file

@ -17,7 +17,9 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/at.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -42,18 +44,76 @@ TEST(renameat, enotdir) {
// EXPECT_SYS(ENOTDIR, -1, rename("zoo", "yo/there")); // EXPECT_SYS(ENOTDIR, -1, rename("zoo", "yo/there"));
} }
TEST(rename, eisdir) {
// new is a directory but old is not a directory
ASSERT_SYS(0, 0, mkdir("foo", 0755));
ASSERT_SYS(0, 0, touch("foo/bar", 0644));
ASSERT_SYS(0, 0, touch("lol", 0644));
ASSERT_SYS(EISDIR, -1, rename("lol", "foo"));
}
TEST(rename, enotdir) {
// old is a directory but new is not a directory
ASSERT_SYS(0, 0, mkdir("lol", 0755));
ASSERT_SYS(0, 0, touch("foo", 0644));
ASSERT_SYS(ENOTDIR, -1, rename("lol", "foo"));
}
TEST(rename, eisdir_again) {
// old is a directory but new is not a directory
ASSERT_SYS(0, 0, touch("foo", 0644));
ASSERT_SYS(0, 0, touch("bar", 0644));
ASSERT_SYS(ENOTDIR, -1, rename("foo/", "bar"));
ASSERT_SYS(ENOTDIR, -1, rename("foo", "bar/"));
}
TEST(rename, moveDirectoryOverDirectory_replacesOldDirectory) {
ASSERT_SYS(0, 0, mkdir("foo//", 0755));
ASSERT_SYS(0, 0, mkdir("lol", 0755));
ASSERT_SYS(0, 0, rename("lol", "foo"));
ASSERT_TRUE(fileexists("foo"));
ASSERT_FALSE(fileexists("lol"));
}
TEST(rename, enotempty) {
// POSIX specifies EEXIST or ENOTEMPTY when new path is non-empty dir.
// Old version of Linux (e.g. RHEL7) return EEXIST.
// Everything else returns ENOTEMPTY.
int rc;
ASSERT_SYS(0, 0, mkdir("foo", 0755));
ASSERT_SYS(0, 0, touch("foo/bar", 0644));
ASSERT_SYS(0, 0, mkdir("lol", 0755));
ASSERT_EQ(-1, (rc = rename("lol", "foo")));
ASSERT_TRUE(errno == ENOTEMPTY || errno == EEXIST);
errno = 0;
}
TEST(rename, moveIntoNonWritableDirectory_raisesEacces) {
// old versions of linux allow this
// new versions of linux report exdev?!
if (IsLinux()) return;
// netbsd and openbsd allow this
if (IsNetbsd() || IsOpenbsd()) return;
// windows doesn't really have permissions
if (IsWindows()) return;
// posix specifies this behavior
ASSERT_SYS(0, 0, mkdir("foo", 0111));
ASSERT_SYS(0, 0, touch("lol", 0644));
ASSERT_SYS(EACCES, -1, rename("lol", "foo/bar"));
}
TEST(renameat, testNull_returnsEfault) { TEST(renameat, testNull_returnsEfault) {
ASSERT_SYS(0, 0, close(creat("hello", 0644))); ASSERT_SYS(0, 0, close(creat("hello", 0644)));
EXPECT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, 0)); ASSERT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, 0));
EXPECT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, "hello")); ASSERT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, "hello"));
EXPECT_SYS(EFAULT, -1, renameat(AT_FDCWD, "hello", AT_FDCWD, 0)); ASSERT_SYS(EFAULT, -1, renameat(AT_FDCWD, "hello", AT_FDCWD, 0));
} }
TEST(renameat, test) { TEST(renameat, test) {
ASSERT_SYS(0, 0, close(creat("first", 0644))); ASSERT_SYS(0, 0, close(creat("first", 0644)));
EXPECT_TRUE(fileexists("first")); ASSERT_TRUE(fileexists("first"));
EXPECT_TRUE(!fileexists("second")); ASSERT_TRUE(!fileexists("second"));
EXPECT_SYS(0, 0, renameat(AT_FDCWD, "first", AT_FDCWD, "second")); ASSERT_SYS(0, 0, renameat(AT_FDCWD, "first", AT_FDCWD, "second"));
EXPECT_TRUE(!fileexists("first")); ASSERT_TRUE(!fileexists("first"));
EXPECT_TRUE(fileexists("second")); ASSERT_TRUE(fileexists("second"));
} }

View file

@ -25,6 +25,7 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h" #include "libc/runtime/stack.h"
#include "libc/stdio/rand.h" #include "libc/stdio/rand.h"
#include "libc/str/str.h" #include "libc/str/str.h"
@ -66,13 +67,17 @@ TEST(reservefd, testGrowthOfFdsDataStructure) {
int i, n; int i, n;
struct rlimit rlim; struct rlimit rlim;
n = 1700; // pe '2**16/40' → 1638 (likely value of g_fds.n) n = 1700; // pe '2**16/40' → 1638 (likely value of g_fds.n)
if (!getrlimit(RLIMIT_NOFILE, &rlim)) n = MIN(n, rlim.rlim_cur - 3); if (!getrlimit(RLIMIT_NOFILE, &rlim)) {
n = MIN(n, rlim.rlim_cur - 3);
} else {
errno = 0;
}
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
EXPECT_SYS(0, i + 3, open("/zip/usr/share/zoneinfo/UTC", O_RDONLY)); ASSERT_SYS(0, i + 3, open("/zip/usr/share/zoneinfo/UTC", O_RDONLY));
} }
ASSERT_GT(g_fds.n, OPEN_MAX); ASSERT_GT(g_fds.n, OPEN_MAX);
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
EXPECT_SYS(0, 0, close(i + 3)); ASSERT_SYS(0, 0, close(i + 3));
} }
} }

View file

@ -150,6 +150,7 @@ o/$(MODE)/test/libc/calls/zipread.com.zip.o: private \
o/$(MODE)/test/libc/calls/ioctl_test.com.runs: \ o/$(MODE)/test/libc/calls/ioctl_test.com.runs: \
private .PLEDGE = private .PLEDGE =
o/$(MODE)/test/libc/calls/lseek_test.com.runs \
o/$(MODE)/test/libc/calls/poll_test.com.runs: \ o/$(MODE)/test/libc/calls/poll_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet private .PLEDGE = stdio rpath wpath cpath fattr proc inet

View file

@ -20,6 +20,7 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
char testlib_enable_tmp_setup_teardown; char testlib_enable_tmp_setup_teardown;
@ -45,6 +46,16 @@ TEST(unlink, enotdir) {
ASSERT_SYS(ENOTDIR, -1, unlink("o/doesnotexist")); ASSERT_SYS(ENOTDIR, -1, unlink("o/doesnotexist"));
} }
TEST(rmdir, willDeleteRegardlessOfAccessBits) {
ASSERT_SYS(0, 0, mkdir("foo", 0));
ASSERT_SYS(0, 0, rmdir("foo/"));
}
TEST(unlink, willDeleteRegardlessOfAccessBits) {
ASSERT_SYS(0, 0, touch("foo", 0));
ASSERT_SYS(0, 0, unlink("foo"));
}
TEST(unlinkat, test) { TEST(unlinkat, test) {
int i, fd; int i, fd;
EXPECT_EQ(0, touch("mytmp", 0644)); EXPECT_EQ(0, touch("mytmp", 0644));

View file

@ -94,6 +94,7 @@ TEST(__zipos_normpath, vectors) {
{"./", ""}, {"./", ""},
{"..", ""}, {"..", ""},
{"../", ""}, {"../", ""},
{"./foo/", "foo/"},
{"foo/", "foo/"}, {"foo/", "foo/"},
{"../abc/def", "abc/def"}, {"../abc/def", "abc/def"},
{"../abc/def/..", "abc/"}, {"../abc/def/..", "abc/"},

View file

@ -28,25 +28,24 @@
void SetUpOnce(void) { void SetUpOnce(void) {
GetSymbolTable(); GetSymbolTable();
ASSERT_SYS(0, 0, pledge("stdio rpath", 0));
} }
TEST(isutf8, good) { TEST(isutf8, good) {
ASSERT_TRUE(_isutf8("\0\1\2\3", 4)); ASSERT_TRUE(isutf8("\0\1\2\3", 4));
EXPECT_TRUE(_isutf8(kHyperion, kHyperionSize)); EXPECT_TRUE(isutf8(kHyperion, kHyperionSize));
EXPECT_TRUE(_isutf8("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒", -1)); EXPECT_TRUE(isutf8("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒", -1));
EXPECT_TRUE(_isutf8("天地玄黄 宇宙洪荒 日月盈昃 辰宿列张 寒来暑往 秋收冬藏" EXPECT_TRUE(isutf8("天地玄黄 宇宙洪荒 日月盈昃 辰宿列张 寒来暑往 秋收冬藏"
"闰馀成岁 律吕调阳 云腾致雨 露结为霜 金生丽水 玉出昆冈" "闰馀成岁 律吕调阳 云腾致雨 露结为霜 金生丽水 玉出昆冈"
"剑号巨阙 珠称夜光 果珍李柰 菜重芥姜 海咸河淡 鳞潜羽翔" "剑号巨阙 珠称夜光 果珍李柰 菜重芥姜 海咸河淡 鳞潜羽翔"
"龙师火帝 鸟官人皇 始制文字 乃服衣裳 推位让国 有虞陶唐", "龙师火帝 鸟官人皇 始制文字 乃服衣裳 推位让国 有虞陶唐",
-1)); -1));
} }
TEST(isutf8, bad) { TEST(isutf8, bad) {
ASSERT_FALSE(_isutf8("\300\200", -1)); // overlong nul ASSERT_FALSE(isutf8("\300\200", -1)); // overlong nul
ASSERT_FALSE(_isutf8("\200\300", -1)); // latin1 c1 control code ASSERT_FALSE(isutf8("\200\300", -1)); // latin1 c1 control code
ASSERT_FALSE(_isutf8("\300\300", -1)); // missing continuation ASSERT_FALSE(isutf8("\300\300", -1)); // missing continuation
ASSERT_FALSE(_isutf8("\377\200\200\200\200", -1)); // thompson-pike varint ASSERT_FALSE(isutf8("\377\200\200\200\200", -1)); // thompson-pike varint
} }
TEST(isutf8, oob) { TEST(isutf8, oob) {
@ -54,15 +53,15 @@ TEST(isutf8, oob) {
char *p; char *p;
for (n = 0; n < 32; ++n) { for (n = 0; n < 32; ++n) {
p = memset(malloc(n), 'a', n); p = memset(malloc(n), 'a', n);
ASSERT_TRUE(_isutf8(p, n)); ASSERT_TRUE(isutf8(p, n));
free(p); free(p);
} }
} }
BENCH(isutf8, bench) { BENCH(isutf8, bench) {
EZBENCH_N("_isutf8", 0, _isutf8(0, 0)); EZBENCH_N("isutf8", 0, isutf8(0, 0));
EZBENCH_N("_isutf8", 5, _isutf8("hello", 5)); EZBENCH_N("isutf8", 5, isutf8("hello", 5));
EZBENCH_N("_isutf8 ascii", kHyperionSize, _isutf8(kHyperion, kHyperionSize)); EZBENCH_N("isutf8 ascii", kHyperionSize, isutf8(kHyperion, kHyperionSize));
EZBENCH_N("_isutf8 unicode", kBlocktronicsSize, EZBENCH_N("isutf8 unicode", kBlocktronicsSize,
_isutf8(kBlocktronics, kBlocktronicsSize)); isutf8(kBlocktronics, kBlocktronicsSize));
} }

View file

@ -192,7 +192,7 @@ static int SerializeString(lua_State *L, char **buf, int idx) {
size_t i, n; size_t i, n;
const char *s; const char *s;
s = lua_tolstring(L, idx, &n); s = lua_tolstring(L, idx, &n);
utf8 = _isutf8(s, n); utf8 = isutf8(s, n);
RETURN_ON_ERROR(appendw(buf, '"')); RETURN_ON_ERROR(appendw(buf, '"'));
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
switch ((x = kLuaStrXlat[(c = s[i] & 255)])) { switch ((x = kLuaStrXlat[(c = s[i] & 255)])) {

View file

@ -209,7 +209,7 @@ typedef unsigned char u8;
# define access(f,m) _access((f),(m)) # define access(f,m) _access((f),(m))
# endif # endif
# ifndef unlink # ifndef unlink
# define unlink _unlink # define unlink_ _unlink
# endif # endif
# ifndef strdup # ifndef strdup
# define strdup _strdup # define strdup _strdup

View file

@ -163,8 +163,8 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *cname,
lfilehdrsize = kZipLfileHdrMinSize + namesize; lfilehdrsize = kZipLfileHdrMinSize + namesize;
crc = crc32_z(0, data, uncompsize); crc = crc32_z(0, data, uncompsize);
GetDosLocalTime(mtim.tv_sec, &mtime, &mdate); GetDosLocalTime(mtim.tv_sec, &mtime, &mdate);
if (_isutf8(name, namesize)) gflags |= kZipGflagUtf8; if (isutf8(name, namesize)) gflags |= kZipGflagUtf8;
if (S_ISREG(mode) && _istext(data, size)) { if (S_ISREG(mode) && istext(data, size)) {
iattrs |= kZipIattrText; iattrs |= kZipIattrText;
} }
dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0; dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0;

View file

@ -1084,7 +1084,7 @@ static bool HasString(struct Strings *l, const char *s, size_t n) {
return false; return false;
} }
static const char* DEFAULTLUAPATH = "/zip/.lua/?.lua;/zip/.lua/?/init.lua"; static const char *DEFAULTLUAPATH = "/zip/.lua/?.lua;/zip/.lua/?/init.lua";
static void UpdateLuaPath(const char *s) { static void UpdateLuaPath(const char *s) {
#ifndef STATIC #ifndef STATIC
@ -1100,11 +1100,10 @@ static void UpdateLuaPath(const char *s) {
if (t = strstr(curpath, DEFAULTLUAPATH)) { if (t = strstr(curpath, DEFAULTLUAPATH)) {
// if the DEFAULT path is found, prepend the path in front of it // if the DEFAULT path is found, prepend the path in front of it
respath = xasprintf("%.*s%s/.lua/?.lua;%s/.lua/?/init.lua;%s", respath = xasprintf("%.*s%s/.lua/?.lua;%s/.lua/?/init.lua;%s",
t-curpath, curpath, s, s, t); t - curpath, curpath, s, s, t);
} else { } else {
// if the DEFAULT path is not found, append to the end // if the DEFAULT path is not found, append to the end
respath = xasprintf("%s;%s/.lua/?.lua;%s/.lua/?/init.lua", respath = xasprintf("%s;%s/.lua/?.lua;%s/.lua/?/init.lua", curpath, s, s);
curpath, s, s);
} }
lua_pushstring(L, _gc(respath)); lua_pushstring(L, _gc(respath));
lua_setfield(L, -3, "path"); lua_setfield(L, -3, "path");
@ -3659,8 +3658,8 @@ static void StoreAsset(char *path, size_t pathlen, char *data, size_t datalen,
} }
INFOF("(srvr) storing asset %`'s", path); INFOF("(srvr) storing asset %`'s", path);
disk = gflags = iattrs = 0; disk = gflags = iattrs = 0;
if (_isutf8(path, pathlen)) gflags |= kZipGflagUtf8; if (isutf8(path, pathlen)) gflags |= kZipGflagUtf8;
if (_istext(data, datalen)) iattrs |= kZipIattrText; if (istext(data, datalen)) iattrs |= kZipIattrText;
crc = crc32_z(0, data, datalen); crc = crc32_z(0, data, datalen);
if (datalen < 100) { if (datalen < 100) {
method = kZipCompressionNone; method = kZipCompressionNone;