mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
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:
parent
ec957491ea
commit
ebf784d4f5
76 changed files with 1019 additions and 568 deletions
|
@ -34,17 +34,14 @@ cosmocc --update # pull cosmo and rebuild toolchain
|
|||
```
|
||||
|
||||
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
|
||||
// hello.c
|
||||
#include <stdio.h>
|
||||
#include <cosmo.h>
|
||||
|
||||
int main() {
|
||||
ShowCrashReports();
|
||||
printf("hello world\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -181,13 +181,14 @@ textwindows bool __sig_handle(int sigops, int sig, int si_code,
|
|||
switch (__sighandrvas[sig]) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_is_fatal(sig)) {
|
||||
size_t len;
|
||||
char name[16];
|
||||
strsignal_r(sig, name);
|
||||
len = strlen(name);
|
||||
name[len++] = '\n';
|
||||
WriteFile(GetStdHandle(kNtStdErrorHandle), name, len, 0, 0);
|
||||
STRACE("terminating on %s", name);
|
||||
intptr_t hStderr;
|
||||
const char *signame;
|
||||
char *end, sigbuf[15], output[16];
|
||||
signame = strsignal_r(sig, sigbuf);
|
||||
STRACE("terminating due to uncaught %s", signame);
|
||||
hStderr = GetStdHandle(kNtStdErrorHandle);
|
||||
end = stpcpy(stpcpy(output, signame), "\n");
|
||||
WriteFile(hStderr, output, end - output, 0, 0);
|
||||
if (_weaken(__restore_console_win32)) {
|
||||
_weaken(__restore_console_win32)();
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ unsigned geteuid(void) nosideeffect;
|
|||
unsigned getgid(void) nosideeffect;
|
||||
unsigned getuid(void) libcesque;
|
||||
unsigned sleep(unsigned);
|
||||
unsigned ualarm(unsigned, unsigned);
|
||||
unsigned umask(unsigned);
|
||||
void sync(void);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
|
||||
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
|
||||
#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_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
|
||||
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
|
||||
|
@ -62,16 +62,22 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
|||
break;
|
||||
case O_WRONLY:
|
||||
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;
|
||||
case O_RDWR:
|
||||
perm = kNtFileGenericRead | kNtFileGenericWrite;
|
||||
if (flags & _O_APPEND) {
|
||||
perm = kNtFileGenericRead | kNtFileAppendData;
|
||||
} else {
|
||||
perm = kNtFileGenericRead | kNtFileGenericWrite;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
if (flags & _O_APPEND) {
|
||||
perm = kNtFileAppendData; // todo: this is part of generic write above
|
||||
}
|
||||
|
||||
attr = 0;
|
||||
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;
|
||||
|
|
|
@ -26,7 +26,7 @@ textwindows int sys_fchmodat_nt(int dirfd, const char *path, uint32_t mode,
|
|||
uint16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
if ((attr = GetFileAttributes(path16)) != -1u) {
|
||||
if (mode & 0200) {
|
||||
if (mode & 0222) {
|
||||
attr &= ~kNtFileAttributeReadonly;
|
||||
} else {
|
||||
attr |= kNtFileAttributeReadonly;
|
||||
|
|
|
@ -26,10 +26,13 @@
|
|||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kmalloc.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.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/errors.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);
|
||||
}
|
||||
|
||||
static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags, unsigned arg,
|
||||
unsigned supported) {
|
||||
unsigned old, neu, changed, other, allowed;
|
||||
old = *flags & supported;
|
||||
other = *flags & ~supported;
|
||||
neu = arg & supported;
|
||||
changed = old ^ neu;
|
||||
// you may change the following access mode flags:
|
||||
static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags,
|
||||
unsigned mode, unsigned arg,
|
||||
intptr_t *handle) {
|
||||
|
||||
// you may change the following:
|
||||
//
|
||||
// - O_NONBLOCK make read() raise EAGAIN
|
||||
// - O_NDELAY same thing as O_NONBLOCK
|
||||
// - O_ACCMODE but has a minimal effect
|
||||
// - O_APPEND for toggling append mode
|
||||
// - O_RANDOM alt. for posix_fadvise()
|
||||
// - O_SEQUENTIAL alt. for posix_fadvise()
|
||||
// - O_DIRECT works but haven't tested
|
||||
//
|
||||
allowed = O_ACCMODE | O_NONBLOCK;
|
||||
if (changed & ~allowed) {
|
||||
// the following access mode flags are supported, but it's currently
|
||||
// not possible to change them on windows.
|
||||
//
|
||||
// - O_APPEND tried to support but failed
|
||||
// - O_RANDOM use posix_fadvise() instead
|
||||
// - O_SEQUENTIAL use posix_fadvise() instead
|
||||
// - O_DIRECT possibly in future?
|
||||
// - O_DSYNC possibly in future?
|
||||
// - O_RSYNC possibly in future?
|
||||
// - O_SYNC possibly in future?
|
||||
//
|
||||
return enotsup();
|
||||
// the other bits are ignored.
|
||||
unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK;
|
||||
unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT;
|
||||
unsigned newflag = (*flags & ~allowed) | (arg & allowed);
|
||||
|
||||
if ((*flags & needreo) ^ (arg & needreo)) {
|
||||
unsigned perm, share, attr;
|
||||
if (GetNtOpenFlags(newflag, mode, &perm, &share, 0, &attr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
// MSDN says only these are allowed, otherwise it returns EINVAL.
|
||||
attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
|
||||
kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |
|
||||
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
|
||||
// 2. return zero if nothing's changed
|
||||
*flags = other | neu;
|
||||
*flags = newflag;
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
|
||||
int rc;
|
||||
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) || //
|
||||
__isfdkind(fd, kFdSocket) || //
|
||||
__isfdkind(fd, kFdConsole)) {
|
||||
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) {
|
||||
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) {
|
||||
if (g_fds.p[fd].flags & O_CLOEXEC) {
|
||||
rc = FD_CLOEXEC;
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
|
||||
/**
|
||||
* Does things with file descriptor, e.g.
|
||||
|
@ -57,6 +57,12 @@
|
|||
* - `F_SETFD` sets `FD_CLOEXEC` status of `arg` file descriptor
|
||||
* - `F_GETFL` returns 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_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd
|
||||
* - `F_SETLK` for record locking where `arg` is `struct flock *`
|
||||
|
@ -131,11 +137,15 @@ int fcntl(int fd, int cmd, ...) {
|
|||
}
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
if (cmd == F_GETFD || //
|
||||
cmd == F_GETOWN || //
|
||||
cmd == F_FULLFSYNC || //
|
||||
cmd == F_BARRIERFSYNC || //
|
||||
cmd == F_MAXFD) {
|
||||
if (rc != -1 && cmd == F_GETFL) {
|
||||
STRACE("fcntl(%d, F_GETFL) → %s", fd, DescribeOpenFlags(rc));
|
||||
} else if (cmd == F_SETFL) {
|
||||
STRACE("fcntl(%d, F_SETFL, %s) → %d% m", fd, DescribeOpenFlags(arg), rc);
|
||||
} 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);
|
||||
} else if (cmd == F_GETFL) {
|
||||
STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd),
|
||||
|
|
|
@ -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`
|
||||
* @param st is where the result is stored
|
||||
* @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 EIO if i/o error occurred while reading from filesystem
|
||||
* @raise ELOOP if a symbolic link loop exists in `path`
|
||||
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
|
||||
* @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 EILSEQ if `path` contains illegal UTF-8 sequences (Windows/MacOS)
|
||||
* @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 ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
|
||||
* @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
|
||||
* @see S_ISDIR(st.st_mode), S_ISREG()
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
|
||||
/* execve() depends on this */
|
||||
// execve() depends on this
|
||||
int rc;
|
||||
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();
|
||||
} else if (flags & ~AT_SYMLINK_NOFOLLOW) {
|
||||
return einval();
|
||||
|
|
|
@ -596,68 +596,68 @@ static int ioctl_siocgifflags(int fd, void *arg) {
|
|||
/**
|
||||
* 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
|
||||
* are available on a terminal or socket, waiting to be read.
|
||||
* - `FIONREAD` takes an `int *` and returns how many bytes of input are
|
||||
* 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
|
||||
* of your teletypewriter. It's an alias for tcgetwinsize().
|
||||
* - `TIOCGWINSZ` populates `struct winsize *` with the dimensions of
|
||||
* your teletypewriter. It's an alias for tcgetwinsize().
|
||||
*
|
||||
* - `TIOCSWINSZ` with the dimensions of your teletypewriter to
|
||||
* `struct winsize *`. It's an alias for tcsetwinsize().
|
||||
* - `TIOCSWINSZ` with the dimensions of your teletypewriter to `struct
|
||||
* winsize *`. It's an alias for tcsetwinsize().
|
||||
*
|
||||
* - `TIOCOUTQ` takes an `int *` and returns the number of bytes in
|
||||
* the terminal's output buffer. Only available on UNIX.
|
||||
* - `TIOCOUTQ` takes an `int *` and returns the number of bytes in the
|
||||
* terminal's output buffer. Only available on UNIX.
|
||||
*
|
||||
* - `TIOCSTI` takes a `const char *` and may be used to fake input
|
||||
* to a tty. This API isn't available on OpenBSD. Only available
|
||||
* on UNIX.
|
||||
* - `TIOCSTI` takes a `const char *` and may be used to fake input to a
|
||||
* tty. This API isn't available on OpenBSD. Only available on UNIX.
|
||||
*
|
||||
* - `TIOCNOTTY` takes an `int tty_fd` arg and makes it the
|
||||
* controlling terminal of the calling process, which should have
|
||||
* called setsid() beforehand.
|
||||
* - `TIOCNOTTY` takes an `int tty_fd` arg and makes it the controlling
|
||||
* terminal of the calling process, which should have called setsid()
|
||||
* beforehand.
|
||||
*
|
||||
* - `TIOCNOTTY` to give up the controlling terminal. Only available
|
||||
* on UNIX.
|
||||
* - `TIOCNOTTY` to give up the controlling terminal. Only available on
|
||||
* UNIX.
|
||||
*
|
||||
* - `TIOCNXCL` to give up exclusive mode on terminal. Only
|
||||
* available on UNIX.
|
||||
* - `TIOCNXCL` to give up exclusive mode on terminal. Only available on
|
||||
* UNIX.
|
||||
*
|
||||
* - `SIOCGIFCONF` takes an struct ifconf object of a given size,
|
||||
* whose arg is `struct ifconf *`. It implements the Linux style
|
||||
* and modifies the following:
|
||||
* - ifc_len: set it to the number of valid ifreq structures
|
||||
* representingthe interfaces
|
||||
* - ifc_ifcu.ifcu_req: sets the name of the interface for each
|
||||
* interface
|
||||
* The ifc_len is an input/output parameter: set it to the total
|
||||
* size of the ifcu_buf (ifcu_req) buffer on input.
|
||||
* - `SIOCGIFCONF` takes an struct ifconf object of a given size,
|
||||
* whose arg is `struct ifconf *`. It implements the Linux style
|
||||
* and modifies the following:
|
||||
* - ifc_len: set it to the number of valid ifreq structures
|
||||
* representingthe interfaces
|
||||
* - ifc_ifcu.ifcu_req: sets the name of the interface for each
|
||||
* interface
|
||||
* The ifc_len is an input/output parameter: set it to the total
|
||||
* size of the ifcu_buf (ifcu_req) buffer on input.
|
||||
*
|
||||
* - `SIOCGIFNETMASK` populates a `struct ifconf *` record with the
|
||||
* network interface mask. This data structure should be obtained
|
||||
* by calling `SIOCGIFCONF`.
|
||||
* - `SIOCGIFNETMASK` populates a `struct ifconf *` record with the
|
||||
* network interface mask. This data structure should be obtained by
|
||||
* calling `SIOCGIFCONF`.
|
||||
*
|
||||
* - `SIOCGIFBRDADDR` populates a `struct ifconf *` record with the
|
||||
* network broadcast addr. This data structure should be obtained
|
||||
* by calling `SIOCGIFCONF`.
|
||||
* - `SIOCGIFBRDADDR` populates a `struct ifconf *` record with the
|
||||
* network broadcast addr. This data structure should be obtained by
|
||||
* calling `SIOCGIFCONF`.
|
||||
*
|
||||
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
|
||||
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
|
||||
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
|
||||
* - `TCGETS` isn't polyfilled; use tcgetattr()
|
||||
* - `TCSETS` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSW` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSF` isn't polyfilled; use tcsetattr()
|
||||
* - `TCXONC` isn't polyfilled; use tcflow()
|
||||
* - `TCSBRK` isn't polyfilled; use tcdrain()
|
||||
* - `TCFLSH` isn't polyfilled; use tcflush()
|
||||
* - `TIOCGPTN` isn't polyfilled; use ptsname()
|
||||
* - `TIOCGSID` isn't polyfilled; use tcgetsid()
|
||||
* - `TCSBRK` isn't polyfilled; use tcsendbreak()
|
||||
* - `TCSBRK` isn't polyfilled; use tcsendbreak()
|
||||
* - `TIOCSPGRP` isn't polyfilled; use tcsetpgrp()
|
||||
* - `TIOCSPTLCK` isn't polyfilled; use unlockpt()
|
||||
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
|
||||
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
|
||||
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
|
||||
* - `TCGETS` isn't polyfilled; use tcgetattr()
|
||||
* - `TCSETS` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSW` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSF` isn't polyfilled; use tcsetattr()
|
||||
* - `TCXONC` isn't polyfilled; use tcflow()
|
||||
* - `TCSBRK` isn't polyfilled; use tcdrain()
|
||||
* - `TCFLSH` isn't polyfilled; use tcflush()
|
||||
* - `TIOCGPTN` isn't polyfilled; use ptsname()
|
||||
* - `TIOCGSID` isn't polyfilled; use tcgetsid()
|
||||
* - `TCSBRK` isn't polyfilled; use tcsendbreak()
|
||||
* - `TCSBRK` isn't polyfilled; use tcsendbreak()
|
||||
* - `TIOCSPGRP` isn't polyfilled; use tcsetpgrp()
|
||||
* - `TIOCSPTLCK` isn't polyfilled; use unlockpt()
|
||||
*
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
|
|
@ -16,15 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.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) {
|
||||
int e;
|
||||
char16_t *p, path16[PATH_MAX];
|
||||
/* if (strlen(path) > 248) return enametoolong(); */
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
if (CreateDirectory(path16, 0)) return 0;
|
||||
return __fix_enotdir(-1, path16);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ntmagicpaths.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/macros.internal.h"
|
||||
#include "libc/nt/systeminfo.h"
|
||||
|
@ -75,22 +77,21 @@ textwindows int __mkntpath(const char *path,
|
|||
*/
|
||||
textwindows int __mkntpath2(const char *path,
|
||||
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])) {
|
||||
z = MIN(32767, PATH_MAX);
|
||||
// turn "\c\foo" into "\\?\c:\foo"
|
||||
|
@ -142,6 +143,7 @@ textwindows int __mkntpath2(const char *path,
|
|||
}
|
||||
|
||||
// turn /tmp into GetTempPath()
|
||||
size_t m;
|
||||
if (!x && IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' &&
|
||||
(IsSlash(q[4]) || !q[4])) {
|
||||
m = GetTempPath(z, p);
|
||||
|
@ -154,23 +156,37 @@ textwindows int __mkntpath2(const char *path,
|
|||
}
|
||||
|
||||
// turn utf-8 into utf-16
|
||||
n = tprecode8to16(p, z, q).ax;
|
||||
size_t n = tprecode8to16(p, z, q).ax;
|
||||
if (n >= z - 1) {
|
||||
STRACE("path too long for windows: %#s", path);
|
||||
return enametoolong();
|
||||
}
|
||||
|
||||
// 1. turn `/` into `\`
|
||||
// 2. turn `\\` into `\` if not at beginning
|
||||
// normalize path
|
||||
// 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) {
|
||||
c = p[i];
|
||||
int c = p[i];
|
||||
if (c == '/') {
|
||||
c = '\\';
|
||||
}
|
||||
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;
|
||||
n = j;
|
||||
|
@ -180,11 +196,11 @@ textwindows int __mkntpath2(const char *path,
|
|||
|
||||
// To avoid toil like this:
|
||||
//
|
||||
// CMD.EXE was started with the above path as the current directory.
|
||||
// UNC paths are not supported. Defaulting to Windows directory.
|
||||
// Access is denied.
|
||||
// "CMD.EXE was started with the above path as the current
|
||||
// directory. UNC paths are not supported. Defaulting to Windows
|
||||
// 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 && //
|
||||
path16[0] == '\\' && //
|
||||
path16[1] == '\\' && //
|
||||
|
@ -194,10 +210,5 @@ textwindows int __mkntpath2(const char *path,
|
|||
n -= 4;
|
||||
}
|
||||
|
||||
// turn "foo\\." into "foo\\"
|
||||
if (n > 2 && path16[n - 1] == u'.' && path16[n - 2] == u'\\') {
|
||||
path16[--n] = 0;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,10 @@ int __mkntpathat(int dirfd, const char *path, int flags,
|
|||
char16_t file[hasatleast PATH_MAX]) {
|
||||
char16_t dir[PATH_MAX];
|
||||
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) 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();
|
||||
dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
|
||||
kNtFileNameNormalized | kNtVolumeNameDos);
|
||||
|
|
|
@ -22,34 +22,92 @@
|
|||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall-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/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.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,
|
||||
uint32_t flags, int32_t mode,
|
||||
uint32_t extra_attr) {
|
||||
|
||||
// join(topath(dirfd), path) and translate from utf-8 to utf-16
|
||||
char16_t path16[PATH_MAX];
|
||||
uint32_t perm, share, disp, attr;
|
||||
if (__mkntpathat(dirfd, path, flags, path16) == -1) {
|
||||
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 ((attr = GetFileAttributes(path16)) != -1u && //
|
||||
(attr & kNtFileAttributeReparsePoint)) {
|
||||
if (fattr != -1u && (fattr & kNtFileAttributeReparsePoint)) {
|
||||
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) {
|
||||
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,
|
||||
attr | extra_attr, 0),
|
||||
path16);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
|
@ -56,7 +57,7 @@
|
|||
*
|
||||
* __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
|
||||
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
|
||||
|
@ -64,10 +65,12 @@
|
|||
* Cosmopolitan's general approach on Windows to path translation is to
|
||||
*
|
||||
* - replace `/' with `\`
|
||||
* - normalize `.' and `..`
|
||||
* - translate utf-8 into utf-16
|
||||
* - turn `"\X\foo"` into `"\\?\X:\foo"`
|
||||
* - turn `"\X"` into `"\\?\X:\"`
|
||||
* - 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(),
|
||||
* 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`.
|
||||
*
|
||||
* @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`
|
||||
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`,
|
||||
* 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:\`
|
||||
* `path` names a relative path then it's opened relative to `dirfd`
|
||||
* @param path is a UTF-8 string naming a filesystem entity
|
||||
* @param flags must have one of the following under the `O_ACCMODE` bits:
|
||||
* - `O_RDONLY` to open `file` for reading only
|
||||
* - `O_WRONLY` to open `file` for writing
|
||||
* - `O_RDWR` to open `file` for reading and writing
|
||||
* - `O_RDONLY` to open `path` for reading only
|
||||
* - `O_WRONLY` to open `path` for writing
|
||||
* - `O_RDWR` to open `path` for reading and writing
|
||||
* The following may optionally be bitwise or'd into `flags`:
|
||||
* - `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_EXCL` exclusive access (see below)
|
||||
* - `O_APPEND` open file for appending only
|
||||
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink
|
||||
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
|
||||
* - `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_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
|
||||
* - `O_DSYNC` 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)
|
||||
|
@ -131,40 +127,48 @@
|
|||
* the executable bit is set thrice too
|
||||
* @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 EACCES if unveil() is in play and didn't unveil your `file` path
|
||||
* @raise EACCES if we don't have permission to search a component of `file`
|
||||
* @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 `path`
|
||||
* @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 `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 ENOTDIR if a directory component in `file` exists as non-directory
|
||||
* @raise ENOTDIR if `file` is relative and `dirfd` isn't an open directory
|
||||
* @raise EROFS when writing is requested w/ `file` on read-only filesystem
|
||||
* @raise ENAMETOOLONG if symlink-resolved `file` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `file` exists longer than `NAME_MAX`
|
||||
* @raise ENOTSUP if `file` is on zip file system and process is vfork()'d
|
||||
* @raise ENOSPC if file system is full when `file` would be `O_CREAT`ed
|
||||
* @raise ENOTSUP if `path` is on zip file system and `dirfd` isn't `AT_FDCWD`
|
||||
* @raise ENOEXEC if `path` is a zip path and this executable isn't a zip file
|
||||
* @raise ENOTDIR if a directory component in `path` exists as non-directory
|
||||
* @raise ENOTDIR if `path` ends with a trailing slash and refers to a file
|
||||
* @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
|
||||
* @raise ENOTDIR if `path` isn't a directory and `O_DIRECTORY` was passed
|
||||
* @raise EILSEQ if `path` contains illegal UTF-8 sequences (Windows/MacOS)
|
||||
* @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 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 ENOENT if `file` 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` doesn't exist when `O_CREAT` isn't in `flags`
|
||||
* @raise ENOENT if `path` points to a string that's empty
|
||||
* @raise ENOMEM if insufficient memory was available
|
||||
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
|
||||
* @raise ENFILE if system-wide file limit has been reached
|
||||
* @raise EOPNOTSUPP if `file` names a named socket
|
||||
* @raise EFAULT if `file` points to invalid memory
|
||||
* @raise ETXTBSY if writing is requested on `file` that's being executed
|
||||
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `file` is a symbolic link
|
||||
* @raise ELOOP if a loop was detected resolving components of `file`
|
||||
* @raise EISDIR if writing is requested and `file` names a directory
|
||||
* @raise EOPNOTSUPP if `path` names a named socket
|
||||
* @raise EFAULT if `path` points to invalid memory
|
||||
* @raise ETXTBSY if writing is requested on `path` that's being executed
|
||||
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `path` is a symbolic link
|
||||
* @raise ELOOP if a loop was detected resolving components of `path`
|
||||
* @raise EISDIR if writing is requested and `path` names a directory
|
||||
* @cancellationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int openat(int dirfd, const char *file, int flags, ...) {
|
||||
int openat(int dirfd, const char *path, int flags, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
unsigned mode;
|
||||
|
@ -174,32 +178,48 @@ int openat(int dirfd, const char *file, int flags, ...) {
|
|||
va_end(va);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (file && (!IsAsan() || __asan_is_valid_str(file))) {
|
||||
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 {
|
||||
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
|
||||
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;
|
||||
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,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__strong_reference(openat, openat64);
|
||||
|
|
|
@ -58,11 +58,11 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
|
|||
if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable,
|
||||
kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) {
|
||||
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].handle = hin;
|
||||
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].handle = hout;
|
||||
pipefd[0] = reader;
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
* This function offers atomic operation on all supported platforms
|
||||
* 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 EFAULT if `pipefd` doesn't point to valid memory
|
||||
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
|
||||
|
@ -41,7 +42,7 @@
|
|||
*/
|
||||
int pipe2(int pipefd[hasatleast 2], int flags) {
|
||||
int rc;
|
||||
if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT)) {
|
||||
if (flags & ~(O_CLOEXEC | O_NONBLOCK | (O_DIRECT != -1u ? O_DIRECT : 0))) {
|
||||
return einval();
|
||||
} else if (!pipefd ||
|
||||
(IsAsan() && !__asan_is_valid(pipefd, sizeof(int) * 2))) {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.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-sysv.internal.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 EINVAL if `advice` is invalid or `len` is huge
|
||||
* @raise ESPIPE if `fd` refers to a pipe
|
||||
* @raise ENOTSUP if `fd` is a /zip file
|
||||
* @raise ENOSYS on XNU and OpenBSD
|
||||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
errno_t posix_fadvise(int fd, int64_t offset, int64_t len, int advice) {
|
||||
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);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_fadvise(fd, offset, len, advice);
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_pread(fd, buf, size, offset, offset);
|
||||
} else if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = espipe();
|
||||
} else if (__isfdkind(fd, kFdFile)) {
|
||||
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset);
|
||||
} else {
|
||||
|
|
|
@ -59,7 +59,11 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
|
|||
|
||||
if (IsWindows()) {
|
||||
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 {
|
||||
return ebadf();
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
|
|||
rc = ebadf();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_pwrite(fd, buf, size, offset, offset);
|
||||
} else if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = espipe();
|
||||
} else if (__isfdkind(fd, kFdFile)) {
|
||||
rc = sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, offset);
|
||||
} else {
|
||||
|
|
|
@ -54,7 +54,11 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
|
|||
|
||||
if (IsWindows()) {
|
||||
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 {
|
||||
return ebadf();
|
||||
}
|
||||
|
|
|
@ -17,17 +17,74 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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/errors.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,
|
||||
const char *newpath) {
|
||||
|
||||
// translate unix to windows paths
|
||||
char16_t oldpath16[PATH_MAX];
|
||||
char16_t newpath16[PATH_MAX];
|
||||
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 ||
|
||||
__mkntpathat(newdirfd, newpath, 0, newpath16) == -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)) {
|
||||
return 0;
|
||||
} else {
|
||||
|
|
|
@ -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_subz(struct timeval, struct timeval) pureconst;
|
||||
int64_t timeval_toseconds(struct timeval);
|
||||
int64_t timeval_tomicros(struct timeval);
|
||||
struct timeval timespec_totimeval(struct timespec) pureconst;
|
||||
static inline struct timeval timeval_fromseconds(int64_t __x) {
|
||||
return (struct timeval){__x};
|
||||
|
|
40
libc/calls/ualarm.c
Normal file
40
libc/calls/ualarm.c
Normal 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);
|
||||
}
|
|
@ -33,48 +33,7 @@
|
|||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.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;
|
||||
}
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static textwindows bool IsDirectorySymlink(const char16_t *path) {
|
||||
int e;
|
||||
|
@ -102,13 +61,11 @@ static textwindows int sys_rmdir_nt(const char16_t *path) {
|
|||
if (RemoveDirectory(path)) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Files can linger, for absolutely no reason.
|
||||
* Possibly some Windows Defender bug on Win7.
|
||||
* Sleep for up to one second w/ expo backoff.
|
||||
* Alternative is use Microsoft internal APIs.
|
||||
* Never could have imagined it'd be this bad.
|
||||
*/
|
||||
// Files can linger, for absolutely no reason.
|
||||
// Possibly some Windows Defender bug on Win7.
|
||||
// Sleep for up to one second w/ expo backoff.
|
||||
// Alternative is use Microsoft internal APIs.
|
||||
// Never could have imagined it'd be this bad.
|
||||
if (GetLastError() == kNtErrorDirNotEmpty && ms <= 2048) {
|
||||
errno = e;
|
||||
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) {
|
||||
int n, rc;
|
||||
char16_t path16[PATH_MAX];
|
||||
if ((n = __mkntpathat(dirfd, path, 0, path16)) == -1) {
|
||||
rc = -1;
|
||||
} else if (flags & AT_REMOVEDIR) {
|
||||
rc = sys_rmdir_nt(path16);
|
||||
textwindows int sys_unlinkat_nt_impl(const char16_t *path, int flags) {
|
||||
if (flags & AT_REMOVEDIR) {
|
||||
return sys_rmdir_nt(path);
|
||||
} else {
|
||||
rc = sys_unlink_nt(path16);
|
||||
if (rc != -1) {
|
||||
// TODO(jart): prove that it helps first
|
||||
// rc = SyncDirectory(dirfd, path16, n);
|
||||
return sys_unlink_nt(path);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ typedef int64_t register_t;
|
|||
typedef uint16_t sa_family_t; /* bsd:uint8_t */
|
||||
typedef uint32_t socklen_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 int64_t syscall_arg_t; /* uint64_t on xnu */
|
||||
typedef uint32_t tcflag_t;
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.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;
|
||||
uint32_t sent;
|
||||
int64_t handle;
|
||||
|
||||
if (g_fds.p[fd].kind == kFdConsole) {
|
||||
handle = g_fds.p[fd].extra; // get write end of console
|
||||
} else {
|
||||
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);
|
||||
if (offset == -1) {
|
||||
// perform simple blocking write
|
||||
|
|
|
@ -66,6 +66,7 @@ TryAgain:
|
|||
if ((dwDesiredAccess & kNtGenericExecute) &&
|
||||
(dwCreationDisposition == kNtOpenExisting ||
|
||||
dwCreationDisposition == kNtTruncateExisting)) {
|
||||
NTTRACE("CreateFile removed kNtGenericExecute");
|
||||
dwDesiredAccess &= ~kNtGenericExecute;
|
||||
goto TryAgain;
|
||||
}
|
||||
|
|
|
@ -20,50 +20,59 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
// clang-format off
|
||||
|
||||
static const struct DescribeFlags kFileAccessflags[] = {
|
||||
{kNtFileAllAccess, "FileAllAccess"}, // order matters
|
||||
{kNtFileGenericRead, "FileGenericRead"}, // order matters
|
||||
{kNtFileGenericWrite, "FileGenericWrite"}, // order matters
|
||||
{kNtFileGenericExecute, "FileGenericExecute"}, // order matters
|
||||
{kNtGenericRead, "GenericRead"}, //
|
||||
{kNtGenericWrite, "GenericWrite"}, //
|
||||
{kNtGenericExecute, "GenericExecute"}, //
|
||||
{kNtGenericAll, "GenericAll"}, //
|
||||
{kNtDelete, "Delete"}, //
|
||||
{kNtReadControl, "ReadControl"}, //
|
||||
{kNtWriteDac, "WriteDac"}, //
|
||||
{kNtWriteOwner, "WriteOwner"}, //
|
||||
{kNtSynchronize, "Synchronize"}, //
|
||||
{kNtStandardRightsRequired, "StandardRightsRequired"}, //
|
||||
{kNtAccessSystemSecurity, "AccessSystemSecurity"}, //
|
||||
{kNtMaximumAllowed, "MaximumAllowed"}, //
|
||||
{kNtFileReadData, "FileReadData"}, //
|
||||
{kNtFileListDirectory, "FileListDirectory"}, //
|
||||
{kNtFileWriteData, "FileWriteData"}, //
|
||||
{kNtFileAddFile, "FileAddFile"}, //
|
||||
{kNtFileAppendData, "FileAppendData"}, //
|
||||
{kNtFileAddSubdirectory, "FileAddSubdirectory"}, //
|
||||
{kNtFileCreatePipeInstance, "FileCreatePipeInstance"}, //
|
||||
{kNtFileReadEa, "FileReadEa"}, //
|
||||
{kNtFileWriteEa, "FileWriteEa"}, //
|
||||
{kNtFileExecute, "FileExecute"}, //
|
||||
{kNtFileTraverse, "FileTraverse"}, //
|
||||
{kNtFileDeleteChild, "FileDeleteChild"}, //
|
||||
{kNtFileReadAttributes, "FileReadAttributes"}, //
|
||||
{kNtFileWriteAttributes, "FileWriteAttributes"}, //
|
||||
{kNtTokenAssignPrimary, "TokenAssignPrimary"}, //
|
||||
{kNtTokenDuplicate, "TokenDuplicate"}, //
|
||||
{kNtTokenImpersonate, "TokenImpersonate"}, //
|
||||
{kNtTokenQuery, "TokenQuery"}, //
|
||||
{kNtTokenQuerySource, "TokenQuerySource"}, //
|
||||
{kNtTokenAdjustPrivileges, "TokenAdjustPrivileges"}, //
|
||||
{kNtTokenAdjustGroups, "TokenAdjustGroups"}, //
|
||||
{kNtTokenAdjustDefault, "TokenAdjustDefault"}, //
|
||||
{kNtTokenAdjustSessionid, "TokenAdjustSessionid"}, //
|
||||
{kNtFileAllAccess, "kNtFileAllAccess"},
|
||||
{kNtFileGenericRead|kNtFileGenericWrite|kNtFileGenericExecute,
|
||||
"kNtFileGenericRead|kNtFileGenericWrite|kNtFileGenericExecute"},
|
||||
{kNtFileGenericRead|kNtFileGenericWrite,
|
||||
"kNtFileGenericRead|kNtFileGenericWrite"},
|
||||
{kNtFileGenericRead|kNtFileGenericExecute,
|
||||
"kNtFileGenericRead|kNtFileGenericExecute"},
|
||||
{kNtFileGenericWrite|kNtFileGenericExecute,
|
||||
"kNtFileGenericWrite|kNtFileGenericExecute"},
|
||||
{kNtFileGenericRead, "kNtFileGenericRead"},
|
||||
{kNtFileGenericWrite, "kNtFileGenericWrite"},
|
||||
{kNtFileGenericExecute, "kNtFileGenericExecute"},
|
||||
{kNtGenericRead, "kNtGenericRead"},
|
||||
{kNtGenericWrite, "kNtGenericWrite"},
|
||||
{kNtGenericExecute, "kNtGenericExecute"},
|
||||
{kNtGenericAll, "kNtGenericAll"},
|
||||
{kNtDelete, "kNtDelete"},
|
||||
{kNtReadControl, "kNtReadControl"},
|
||||
{kNtWriteDac, "kNtWriteDac"},
|
||||
{kNtWriteOwner, "kNtWriteOwner"},
|
||||
{kNtSynchronize, "kNtSynchronize"},
|
||||
{kNtStandardRightsRequired, "kNtStandardRightsRequired"},
|
||||
{kNtAccessSystemSecurity, "kNtAccessSystemSecurity"},
|
||||
{kNtMaximumAllowed, "kNtMaximumAllowed"},
|
||||
{kNtFileReadData, "kNtFileReadData"},
|
||||
{kNtFileListDirectory, "kNtFileListDirectory"},
|
||||
{kNtFileWriteData, "kNtFileWriteData"},
|
||||
{kNtFileAddFile, "kNtFileAddFile"},
|
||||
{kNtFileAppendData, "kNtFileAppendData"},
|
||||
{kNtFileAddSubdirectory, "kNtFileAddSubdirectory"},
|
||||
{kNtFileCreatePipeInstance, "kNtFileCreatePipeInstance"},
|
||||
{kNtFileReadEa, "kNtFileReadEa"},
|
||||
{kNtFileWriteEa, "kNtFileWriteEa"},
|
||||
{kNtFileExecute, "kNtFileExecute"},
|
||||
{kNtFileTraverse, "kNtFileTraverse"},
|
||||
{kNtFileDeleteChild, "kNtFileDeleteChild"},
|
||||
{kNtFileReadAttributes, "kNtFileReadAttributes"},
|
||||
{kNtFileWriteAttributes, "kNtFileWriteAttributes"},
|
||||
{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) {
|
||||
return DescribeFlags(buf, 512, kFileAccessflags, ARRAYLEN(kFileAccessflags),
|
||||
"kNt", x);
|
||||
"", x);
|
||||
}
|
||||
|
|
|
@ -36,12 +36,16 @@
|
|||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
privileged char *strsignal_r(int sig, char buf[hasatleast 15]) {
|
||||
privileged dontdiscard char *strsignal_r(int sig, char buf[15]) {
|
||||
int i;
|
||||
char *p;
|
||||
const char *s;
|
||||
if (!sig) return "0";
|
||||
if ((s = GetMagnumStr(kSignalNames, sig))) return s;
|
||||
if (!sig) {
|
||||
return "0";
|
||||
}
|
||||
if ((s = GetMagnumStr(kSignalNames, sig))) {
|
||||
return s;
|
||||
}
|
||||
if (SIGRTMIN <= sig && sig <= SIGRTMAX) {
|
||||
sig -= SIGRTMIN;
|
||||
buf[0] = 'S';
|
||||
|
|
|
@ -18,8 +18,7 @@ int64_t CreateFileA(
|
|||
uint32_t dwFlagsAndAttributes, /* libc/nt/enum/fileflagandattributes.h */
|
||||
int64_t opt_hTemplateFile) paramsnonnull((1));
|
||||
|
||||
int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm, uint32_t *out_share,
|
||||
uint32_t *out_disp, uint32_t *out_attr);
|
||||
int GetNtOpenFlags(int, int, uint32_t *, uint32_t *, uint32_t *, uint32_t *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/ucontext-netbsd.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
|
@ -195,6 +196,9 @@ XnuThreadMain(void *pthread, // rdi
|
|||
|
||||
func(arg, tid);
|
||||
|
||||
// avoid signal handler being triggered after we trash our stack
|
||||
_sigblockall();
|
||||
|
||||
// we no longer use the stack after this point
|
||||
// %rax = int bsdthread_terminate(%rdi = void *stackaddr,
|
||||
// %rsi = size_t freesize,
|
||||
|
@ -234,6 +238,8 @@ static wontreturn void FreebsdThreadMain(void *p) {
|
|||
struct CloneArgs *wt = p;
|
||||
*wt->ctid = 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
|
||||
// void thr_exit(%rdi = long *state);
|
||||
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();
|
||||
*ctid = ax;
|
||||
func(arg, ax);
|
||||
// avoid signal handler being triggered after we trash our stack
|
||||
_sigblockall();
|
||||
// we no longer use the stack after this point
|
||||
// %eax = int __lwp_exit(void);
|
||||
asm volatile("movl\t$0,%2\n\t" // *wt->ztid = 0
|
||||
|
|
|
@ -34,7 +34,6 @@ size_t __zipos_normpath(char *d, const char *s, size_t n) {
|
|||
s[0] == '.' && //
|
||||
(!s[1] || s[1] == '/')) {
|
||||
// matched "/./" or "^.$" or "^./" or "/.$"
|
||||
s += !!s[1];
|
||||
} else if ((p == d || p[-1] == '/') && //
|
||||
s[0] == '.' && //
|
||||
s[1] == '.' && //
|
||||
|
|
|
@ -76,7 +76,7 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
|
|||
} else if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_sendto_nt(fd, msg->msg_iov, msg->msg_iovlen, flags,
|
||||
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);
|
||||
} else {
|
||||
rc = enotsock();
|
||||
|
|
|
@ -80,12 +80,12 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) {
|
|||
if (h1 != -1) {
|
||||
|
||||
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].handle = hpipe;
|
||||
|
||||
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].handle = h1;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* 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;
|
||||
for (p = data, pe = p + size; p < pe; ++p) {
|
||||
if (*p <= 3) {
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include "libc/intrin/likely.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
|
||||
|
||||
static const char kUtf8Dispatch[] = {
|
||||
0, 0, 1, 1, 1, 1, 1, 1, // 0300 utf8-2
|
||||
1, 1, 1, 1, 1, 1, 1, 1, // 0310
|
||||
|
@ -37,10 +35,10 @@ static const char kUtf8Dispatch[] = {
|
|||
/**
|
||||
* Returns true if text is utf-8.
|
||||
*
|
||||
* _isutf8 n=0 1 nanoseconds
|
||||
* _isutf8 n=5 661 ps/byte 1,476 mb/s
|
||||
* _isutf8 ascii n=22851 26 ps/byte 35 GB/s
|
||||
* _isutf8 unicode n=3193 543 ps/byte 1,795 mb/s
|
||||
* isutf8 n=0 1 nanoseconds
|
||||
* isutf8 n=5 661 ps/byte 1,476 mb/s
|
||||
* isutf8 ascii n=22851 26 ps/byte 35 GB/s
|
||||
* isutf8 unicode n=3193 543 ps/byte 1,795 mb/s
|
||||
*
|
||||
* This function considers all ASCII characters including NUL to be
|
||||
* 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
|
||||
*/
|
||||
dontasan bool _isutf8(const void *data, size_t size) {
|
||||
dontasan bool isutf8(const void *data, size_t size) {
|
||||
long c;
|
||||
unsigned m;
|
||||
const char *p, *e;
|
||||
|
@ -61,6 +59,7 @@ dontasan bool _isutf8(const void *data, size_t size) {
|
|||
e = p + size;
|
||||
while (p < e) {
|
||||
#if defined(__x86_64__) && !defined(__chibicc__)
|
||||
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
|
||||
if (!((intptr_t)p & 15)) {
|
||||
for (;;) {
|
||||
if ((m = __builtin_ia32_pmovmskb128(*(xmm_t *)p >= (xmm_t){0}) ^
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#define chomp16 _chomp16
|
||||
#define wchomp _wchomp
|
||||
#define tpenc _tpenc
|
||||
#define isutf8 _isutf8
|
||||
#define istext _istext
|
||||
#define startswith _startswith
|
||||
#define startswithi _startswithi
|
||||
#define endswith _endswith
|
||||
|
@ -187,9 +189,9 @@ wchar_t *wchomp(wchar_t *) libcesque;
|
|||
bool startswith(const char *, const char *) strlenesque;
|
||||
bool startswithi(const char *, const char *) strlenesque;
|
||||
bool endswith(const char *, const char *) strlenesque;
|
||||
bool _istext(const void *, size_t) libcesque;
|
||||
bool _isutf8(const void *, size_t) libcesque;
|
||||
char *strsignal_r(int, char[hasatleast 15]) returnsnonnull libcesque;
|
||||
bool istext(const void *, size_t) libcesque;
|
||||
bool isutf8(const void *, size_t) libcesque;
|
||||
char *strsignal_r(int, char[15]) returnsnonnull libcesque dontdiscard;
|
||||
int strerror_wr(int, uint32_t, char *, size_t)
|
||||
dontthrow nocallback;
|
||||
char16_t *chomp16(char16_t *) libcesque;
|
||||
|
|
|
@ -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_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_NOFOLLOW 0x00020000 0x00008000 0x00000100 0x00000100 0x00000100 0x00000100 0x00000100 0x00020000 # 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_NDELAY 0x00000800 0x00000800 0x00000004 0x00000004 0x00000004 0x00000004 0x00000004 0x00000800 # kNtFileFlagWriteThrough [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_COMPRESSED 0 0 0 0 0 0 0 0x20000000 # kNtFileAttributeCompressed [SYNC libc/calls/open-nt.c]
|
||||
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 0xffffffff 0xffffffff 0x00010000 0xffffffff 0x00080000 0x00004000 # kNtFileFlagNoBuffering [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_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_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_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_NONBLOCK 0x00000800 0x00000800 0x00000004 0x00000004 0x00000004 0x00000004 0x00000004 0x00000800 # bsd consensus
|
||||
syscon open O_ASYNC 0x00002000 0x00002000 0x00000040 0x00000040 0x00000040 0x00000040 0x00000040 0 # 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_ASYNC 0x00002000 0x00002000 0x00000040 0x00000040 0x00000040 0x00000040 0x00000040 0xffffffff # 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_SYNC 0x00101000 0x00101000 0x00000080 0x00000080 0x00000080 0x00000080 0x00000080 0xffffffff # 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_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_SEARCH 0 0 0 0x40100000 0x00040000 0 0x00800000 0 # it's specified by posix what does it mean
|
||||
syscon open O_DSYNC 0x00001000 0x00001000 0x00400000 0x00400000 0 0x00000080 0x00010000 0 #
|
||||
syscon open O_RSYNC 0x00101000 0x00101000 0 0 0 0x00000080 0x00020000 0 #
|
||||
syscon open O_PATH 0x00200000 0x00200000 0 0 0 0 0 0 # Linux 2.6.39+
|
||||
syscon open O_VERIFY 0 0 0 0 0x00200000 0 0 0 #
|
||||
syscon open O_SHLOCK 0 0 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0 #
|
||||
syscon open O_EXLOCK 0 0 0x00000020 0x00000020 0x00000020 0x00000020 0x00000020 0 #
|
||||
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 0xffffffff 0x00000080 0x00010000 0xffffffff #
|
||||
syscon open O_RSYNC 0x00101000 0x00101000 0xffffffff 0xffffffff 0xffffffff 0x00000080 0x00020000 0xffffffff #
|
||||
syscon open O_PATH 0x00200000 0x00200000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff # Linux 2.6.39+
|
||||
syscon open O_VERIFY 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0x00200000 0xffffffff 0xffffffff 0xffffffff #
|
||||
syscon open O_SHLOCK 0xffffffff 0xffffffff 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0xffffffff #
|
||||
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 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 _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 _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
|
||||
|
||||
# unmount() flags
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon open,O_NDELAY,0x00000800,0x00000800,0x00000004,0x00000004,0x00000004,0x00000004,0x00000004,0x00000800
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon open,O_SPARSE,0,0,0,0,0,0,0,0
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#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
|
||||
|
|
|
@ -22,7 +22,6 @@ extern const unsigned O_EXEC;
|
|||
extern const unsigned O_EXLOCK;
|
||||
extern const unsigned O_INDEXED;
|
||||
extern const unsigned O_LARGEFILE;
|
||||
extern const unsigned O_NDELAY;
|
||||
extern const unsigned O_NOATIME;
|
||||
extern const unsigned O_NOCTTY;
|
||||
extern const unsigned O_NOFOLLOW;
|
||||
|
@ -34,7 +33,6 @@ extern const unsigned O_RSYNC;
|
|||
extern const unsigned O_SEARCH;
|
||||
extern const unsigned O_SEQUENTIAL;
|
||||
extern const unsigned O_SHLOCK;
|
||||
extern const unsigned O_SPARSE;
|
||||
extern const unsigned O_SYNC;
|
||||
extern const unsigned O_TMPFILE; /* use tmpfd() or tmpfile() */
|
||||
extern const unsigned O_TRUNC;
|
||||
|
@ -46,13 +44,12 @@ extern const unsigned O_VERIFY;
|
|||
#define O_CLOEXEC O_CLOEXEC
|
||||
#define O_COMPRESSED O_COMPRESSED
|
||||
#define O_CREAT O_CREAT
|
||||
#define O_DIRECT O_DIRECT
|
||||
#define O_DIRECTORY O_DIRECTORY
|
||||
#define O_EXCL O_EXCL
|
||||
#define O_EXEC O_EXEC
|
||||
#define O_INDEXED O_INDEXED
|
||||
#define O_LARGEFILE O_LARGEFILE
|
||||
#define O_NDELAY O_NDELAY
|
||||
#define O_NDELAY O_NONBLOCK
|
||||
#define O_NOATIME O_NOATIME
|
||||
#define O_NOCTTY O_NOCTTY
|
||||
#define O_NOFOLLOW O_NOFOLLOW
|
||||
|
|
|
@ -18,6 +18,8 @@ extern const int SOCK_STREAM;
|
|||
#define SOCK_RAW 3
|
||||
#define SOCK_RDM 4
|
||||
#define SOCK_SEQPACKET 5
|
||||
#define SOCK_CLOEXEC SOCK_CLOEXEC
|
||||
#define SOCK_NONBLOCK SOCK_NONBLOCK
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -53,6 +53,7 @@ dos kNtErrorInvalidAddress EADDRNOTAVAIL
|
|||
dos kNtErrorNotAReparsePoint EINVAL
|
||||
dos kNtErrorInvalidFunction EINVAL
|
||||
dos kNtErrorNegativeSeek EINVAL
|
||||
dos kNtErrorInvalidName EINVAL
|
||||
dos kNtErrorInvalidNetname EADDRNOTAVAIL
|
||||
dos kNtErrorInvalidUserBuffer EMSGSIZE
|
||||
dos kNtErrorIoPending EINPROGRESS
|
||||
|
|
|
@ -14,4 +14,5 @@ kDos2Errno.EINVAL:
|
|||
.e kNtErrorNotAReparsePoint,EINVAL
|
||||
.e kNtErrorInvalidFunction,EINVAL
|
||||
.e kNtErrorNegativeSeek,EINVAL
|
||||
.e kNtErrorInvalidName,EINVAL
|
||||
.e WSAEINVAL,EINVAL
|
||||
|
|
|
@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_
|
|||
* Test cases are guaranteed by the linker to be run in order, sorted by
|
||||
* the (SUITE, NAME) tuple passed here.
|
||||
*/
|
||||
#define TEST(SUITE, NAME) \
|
||||
#define TEST(SUITE, NAME) \
|
||||
__static_yoink("__testcase_start"); \
|
||||
__TEST_PROTOTYPE(SUITE, NAME, __TEST_ARRAY, )
|
||||
|
||||
|
@ -25,7 +25,7 @@ COSMOPOLITAN_C_START_
|
|||
* temorarilly by the runtime while calling fixture functions. Fixtures
|
||||
* are also guaranteed by the linker to be run in sorted order.
|
||||
*/
|
||||
#define FIXTURE(SUITE, NAME) \
|
||||
#define FIXTURE(SUITE, NAME) \
|
||||
__static_yoink("__fixture_start"); \
|
||||
__FIXTURE("fixture", SUITE, NAME)
|
||||
|
||||
|
@ -36,7 +36,7 @@ COSMOPOLITAN_C_START_
|
|||
* Cartesian product of groups. That makes this similar to fixture, but
|
||||
* more appropriate for testing pure code (i.e. no syscalls) like math.
|
||||
*/
|
||||
#define COMBO(GROUP, ENTRY) \
|
||||
#define COMBO(GROUP, ENTRY) \
|
||||
__static_yoink("__combo_start"); \
|
||||
__FIXTURE("combo", GROUP, ENTRY)
|
||||
|
||||
|
@ -49,7 +49,7 @@ COSMOPOLITAN_C_START_
|
|||
*
|
||||
* @see EZBENCH()
|
||||
*/
|
||||
#define BENCH(SUITE, NAME) \
|
||||
#define BENCH(SUITE, NAME) \
|
||||
__static_yoink("__bench_start"); \
|
||||
__TEST_PROTOTYPE(SUITE, NAME, __BENCH_ARRAY, optimizespeed)
|
||||
|
||||
|
@ -223,11 +223,12 @@ void TearDownOnce(void);
|
|||
|
||||
#define EXPECT_SYS(ERRNO, WANT, GOT, ...) \
|
||||
do { \
|
||||
testlib_seterrno(0); \
|
||||
int e = testlib_geterrno(); \
|
||||
__TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, \
|
||||
GOT, __VA_ARGS__); \
|
||||
__TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #ERRNO, \
|
||||
testlib_strerror(), ERRNO, testlib_geterrno(), __VA_ARGS__); \
|
||||
testlib_seterrno(e); \
|
||||
} while (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_testlib_olddir[PATH_MAX];
|
||||
extern char g_testlib_tmpdir[PATH_MAX];
|
||||
extern bool g_testlib_shoulddebugbreak; /* set by testmain */
|
||||
extern _Atomic(unsigned) g_testlib_ran; /* set by wrappers */
|
||||
extern _Atomic(unsigned) g_testlib_failed; /* set by wrappers */
|
||||
|
|
|
@ -16,42 +16,27 @@
|
|||
│ 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/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.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/thread/thread.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
static int x;
|
||||
char g_testlib_olddir[PATH_MAX];
|
||||
char g_testlib_tmpdir[PATH_MAX];
|
||||
struct sigaction wanthandlers[31];
|
||||
static char g_olddir[PATH_MAX];
|
||||
static char g_tmpdir[PATH_MAX];
|
||||
static pthread_mutex_t testlib_error_lock;
|
||||
|
||||
void testlib_finish(void) {
|
||||
|
@ -85,139 +70,64 @@ wontreturn void testlib_abort(void) {
|
|||
}
|
||||
|
||||
static void SetupTmpDir(void) {
|
||||
char *p = g_testlib_tmpdir;
|
||||
p = stpcpy(p, kTmpPath);
|
||||
p = stpcpy(p, program_invocation_short_name), *p++ = '.';
|
||||
p = FormatInt64(p, getpid()), *p++ = '.';
|
||||
p = FormatInt64(p, x++);
|
||||
p[0] = '\0';
|
||||
CHECK_NE(-1, makedirs(g_testlib_tmpdir, 0755), "%s", g_testlib_tmpdir);
|
||||
CHECK_NOTNULL(realpath(g_testlib_tmpdir, g_testlib_tmpdir), "%`'s",
|
||||
g_testlib_tmpdir);
|
||||
CHECK_NE(-1, chdir(g_testlib_tmpdir), "%s", g_testlib_tmpdir);
|
||||
char number[21];
|
||||
FormatInt64(number, _rand64() & INT64_MAX);
|
||||
g_tmpdir[0] = 0;
|
||||
if (*kTmpPath != '/') {
|
||||
strlcat(g_tmpdir, g_olddir, sizeof(g_tmpdir));
|
||||
strlcat(g_tmpdir, "/", sizeof(g_tmpdir));
|
||||
}
|
||||
strlcat(g_tmpdir, kTmpPath, sizeof(g_tmpdir));
|
||||
strlcat(g_tmpdir, program_invocation_short_name, sizeof(g_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) {
|
||||
CHECK_NE(-1, chdir(g_testlib_olddir));
|
||||
CHECK_NE(-1, rmrf(g_testlib_tmpdir), "%s", g_testlib_tmpdir);
|
||||
}
|
||||
|
||||
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);
|
||||
if (chdir(g_olddir)) {
|
||||
perror(g_olddir);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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 (rmrf(g_tmpdir)) {
|
||||
perror(g_tmpdir);
|
||||
exit(1);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
void testlib_runtestcases(testfn_t *start, testfn_t *end, testfn_t warmup) {
|
||||
/*
|
||||
* getpid() calls are inserted to help visually see tests in traces
|
||||
* which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
|
||||
*
|
||||
* strace -f o/default/test.com |& less
|
||||
* truss o/default/test.com |& less
|
||||
* ktrace -f trace o/default/test.com </dev/null; kdump -f trace | less
|
||||
* dtruss o/default/test.com |& less
|
||||
*
|
||||
* Test cases are iterable via a decentralized section. Your TEST()
|
||||
* macro inserts .testcase.SUITENAME sections into the binary which
|
||||
* the linker sorts into an array.
|
||||
*
|
||||
* @see ape/ape.lds
|
||||
*/
|
||||
// getpid() calls are inserted to help visually see tests in traces
|
||||
// which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
|
||||
//
|
||||
// strace -f o/default/test.com |& less
|
||||
// truss o/default/test.com |& less
|
||||
// ktrace -f trace o/default/test.com </dev/null; kdump -f trace | less
|
||||
// dtruss o/default/test.com |& less
|
||||
//
|
||||
// Test cases are iterable via a decentralized section. Your TEST()
|
||||
// macro inserts .testcase.SUITENAME sections into the binary which
|
||||
// the linker sorts into an array.
|
||||
//
|
||||
// @see ape/ape.lds
|
||||
const testfn_t *fn;
|
||||
CopySignalHandlers();
|
||||
if (_weaken(testlib_enable_tmp_setup_teardown) ||
|
||||
_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)) {
|
||||
SetupTmpDir();
|
||||
}
|
||||
if (_weaken(SetUpOnce)) _weaken(SetUpOnce)();
|
||||
for (x = 0, fn = start; fn != end; ++fn) {
|
||||
for (fn = start; fn != end; ++fn) {
|
||||
STRACE("");
|
||||
STRACE("# setting up %t", fn);
|
||||
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 (_weaken(TearDown)) _weaken(TearDown)();
|
||||
if (_weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir();
|
||||
CheckForFileDescriptors();
|
||||
CheckForSignalHandlers();
|
||||
CheckForZombies();
|
||||
}
|
||||
if (_weaken(TearDownOnce)) {
|
||||
_weaken(TearDownOnce)();
|
||||
|
|
|
@ -17,10 +17,19 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/ftw.h"
|
||||
#include "libc/sysv/errfuns.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, //
|
||||
const struct stat *st, //
|
||||
|
@ -48,6 +57,12 @@ static int rmrf_callback(const char *fpath, //
|
|||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -65,8 +65,25 @@ TEST(fcntl_getfl, testRemembersAccessMode) {
|
|||
EXPECT_NE(-1, close(fd));
|
||||
}
|
||||
|
||||
TEST(fcntl_setfl, testChangeAppendStatus) {
|
||||
if (IsWindows()) return; // Can't ReOpenFile() w/ O_APPEND
|
||||
TEST(fcntl_setfl, testChangeAppendStatus_proper) {
|
||||
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};
|
||||
ASSERT_SYS(0, 3, open("foo", O_CREAT | O_WRONLY, 0644));
|
||||
EXPECT_SYS(0, 3, write(3, "foo", 3));
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/sock/struct/ifreq.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/fio.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
#include "libc/sysv/consts/sio.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
|
@ -82,3 +83,18 @@ TEST(siocgifconf, mkntenvblock_systemroot) {
|
|||
EXITS(0);
|
||||
}
|
||||
#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]));
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
#include "libc/limits.h"
|
||||
#include "libc/log/check.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/sock.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
@ -31,7 +35,7 @@
|
|||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
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) {
|
||||
|
@ -59,14 +63,26 @@ TEST(lseek, 64bit) {
|
|||
EXPECT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
TEST(lseek, nonSeekableFd_espipe) {
|
||||
TEST(lseek, isPipe_ESPIPE) {
|
||||
int fds[2];
|
||||
char buf[2];
|
||||
ASSERT_SYS(0, 0, pipe(fds));
|
||||
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(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) {
|
||||
ASSERT_SYS(0, 3, creat("wut", 0644));
|
||||
ASSERT_SYS(0, 4, dup(3));
|
||||
|
|
|
@ -18,14 +18,20 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.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/sysv/consts/o.h"
|
||||
#include "libc/testlib/testlib.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;
|
||||
|
||||
|
@ -113,6 +119,7 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) {
|
|||
char buf[16] = {0};
|
||||
ASSERT_SYS(0, 0, xbarf("hello.txt", "hell", -1));
|
||||
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, 0, lseek(3, 0, SEEK_SET));
|
||||
EXPECT_SYS(0, 1, write(3, "!", 1));
|
||||
|
@ -123,6 +130,50 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) {
|
|||
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) {
|
||||
ASSERT_SYS(0, 0, mkdir("foo", 0755));
|
||||
ASSERT_SYS(0, 3, open("foo", O_RDONLY | O_DIRECTORY));
|
||||
|
@ -132,6 +183,104 @@ TEST(open, testRelativePath_opensRelativeToDirFd) {
|
|||
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 i, count;
|
||||
for (count = i = 0; i < g_fds.n; ++i) {
|
||||
|
@ -154,3 +303,18 @@ TEST(open, lotsOfFds) {
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -447,7 +447,7 @@ TEST(pledge, open_rpath) {
|
|||
if (!pid) {
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath", 0));
|
||||
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_RDWR | O_TRUNC | O_CREAT, 0644));
|
||||
ASSERT_SYS(EPERM, -1, open("foo", O_WRONLY | O_TRUNC | O_CREAT, 0644));
|
||||
|
|
|
@ -62,9 +62,6 @@ TEST(readlinkat, test) {
|
|||
EXPECT_EQ(255, buf[8] & 255);
|
||||
buf[8] = 0;
|
||||
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) {
|
||||
|
@ -89,10 +86,7 @@ TEST(readlinkat, frootloop) {
|
|||
ASSERT_SYS(0, 0, symlink("froot", "froot"));
|
||||
ASSERT_SYS(ELOOP, -1, readlink("froot/loop", buf, sizeof(buf)));
|
||||
if (O_NOFOLLOW) {
|
||||
ASSERT_SYS(IsFreebsd() ? EMLINK
|
||||
: IsNetbsd() ? EFTYPE
|
||||
: ELOOP,
|
||||
-1, open("froot", O_RDONLY | O_NOFOLLOW));
|
||||
ASSERT_SYS(ELOOP, -1, open("froot", O_RDONLY | O_NOFOLLOW));
|
||||
if (0 && O_PATH) { /* need rhel5 test */
|
||||
ASSERT_NE(-1, (fd = open("froot", O_RDONLY | O_NOFOLLOW | O_PATH)));
|
||||
ASSERT_NE(-1, close(fd));
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
@ -42,18 +44,76 @@ TEST(renameat, enotdir) {
|
|||
// 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) {
|
||||
ASSERT_SYS(0, 0, close(creat("hello", 0644)));
|
||||
EXPECT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, 0));
|
||||
EXPECT_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, 0, AT_FDCWD, 0));
|
||||
ASSERT_SYS(EFAULT, -1, renameat(AT_FDCWD, 0, AT_FDCWD, "hello"));
|
||||
ASSERT_SYS(EFAULT, -1, renameat(AT_FDCWD, "hello", AT_FDCWD, 0));
|
||||
}
|
||||
|
||||
TEST(renameat, test) {
|
||||
ASSERT_SYS(0, 0, close(creat("first", 0644)));
|
||||
EXPECT_TRUE(fileexists("first"));
|
||||
EXPECT_TRUE(!fileexists("second"));
|
||||
EXPECT_SYS(0, 0, renameat(AT_FDCWD, "first", AT_FDCWD, "second"));
|
||||
EXPECT_TRUE(!fileexists("first"));
|
||||
EXPECT_TRUE(fileexists("second"));
|
||||
ASSERT_TRUE(fileexists("first"));
|
||||
ASSERT_TRUE(!fileexists("second"));
|
||||
ASSERT_SYS(0, 0, renameat(AT_FDCWD, "first", AT_FDCWD, "second"));
|
||||
ASSERT_TRUE(!fileexists("first"));
|
||||
ASSERT_TRUE(fileexists("second"));
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -66,13 +67,17 @@ TEST(reservefd, testGrowthOfFdsDataStructure) {
|
|||
int i, n;
|
||||
struct rlimit rlim;
|
||||
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) {
|
||||
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);
|
||||
for (i = 0; i < n; ++i) {
|
||||
EXPECT_SYS(0, 0, close(i + 3));
|
||||
ASSERT_SYS(0, 0, close(i + 3));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ o/$(MODE)/test/libc/calls/zipread.com.zip.o: private \
|
|||
o/$(MODE)/test/libc/calls/ioctl_test.com.runs: \
|
||||
private .PLEDGE =
|
||||
|
||||
o/$(MODE)/test/libc/calls/lseek_test.com.runs \
|
||||
o/$(MODE)/test/libc/calls/poll_test.com.runs: \
|
||||
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
@ -45,6 +46,16 @@ TEST(unlink, enotdir) {
|
|||
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) {
|
||||
int i, fd;
|
||||
EXPECT_EQ(0, touch("mytmp", 0644));
|
||||
|
|
|
@ -94,6 +94,7 @@ TEST(__zipos_normpath, vectors) {
|
|||
{"./", ""},
|
||||
{"..", ""},
|
||||
{"../", ""},
|
||||
{"./foo/", "foo/"},
|
||||
{"foo/", "foo/"},
|
||||
{"../abc/def", "abc/def"},
|
||||
{"../abc/def/..", "abc/"},
|
||||
|
|
|
@ -28,25 +28,24 @@
|
|||
|
||||
void SetUpOnce(void) {
|
||||
GetSymbolTable();
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath", 0));
|
||||
}
|
||||
|
||||
TEST(isutf8, good) {
|
||||
ASSERT_TRUE(_isutf8("\0\1\2\3", 4));
|
||||
EXPECT_TRUE(_isutf8(kHyperion, kHyperionSize));
|
||||
EXPECT_TRUE(_isutf8("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒", -1));
|
||||
EXPECT_TRUE(_isutf8("天地玄黄 宇宙洪荒 日月盈昃 辰宿列张 寒来暑往 秋收冬藏"
|
||||
"闰馀成岁 律吕调阳 云腾致雨 露结为霜 金生丽水 玉出昆冈"
|
||||
"剑号巨阙 珠称夜光 果珍李柰 菜重芥姜 海咸河淡 鳞潜羽翔"
|
||||
"龙师火帝 鸟官人皇 始制文字 乃服衣裳 推位让国 有虞陶唐",
|
||||
-1));
|
||||
ASSERT_TRUE(isutf8("\0\1\2\3", 4));
|
||||
EXPECT_TRUE(isutf8(kHyperion, kHyperionSize));
|
||||
EXPECT_TRUE(isutf8("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒", -1));
|
||||
EXPECT_TRUE(isutf8("天地玄黄 宇宙洪荒 日月盈昃 辰宿列张 寒来暑往 秋收冬藏"
|
||||
"闰馀成岁 律吕调阳 云腾致雨 露结为霜 金生丽水 玉出昆冈"
|
||||
"剑号巨阙 珠称夜光 果珍李柰 菜重芥姜 海咸河淡 鳞潜羽翔"
|
||||
"龙师火帝 鸟官人皇 始制文字 乃服衣裳 推位让国 有虞陶唐",
|
||||
-1));
|
||||
}
|
||||
|
||||
TEST(isutf8, bad) {
|
||||
ASSERT_FALSE(_isutf8("\300\200", -1)); // overlong nul
|
||||
ASSERT_FALSE(_isutf8("\200\300", -1)); // latin1 c1 control code
|
||||
ASSERT_FALSE(_isutf8("\300\300", -1)); // missing continuation
|
||||
ASSERT_FALSE(_isutf8("\377\200\200\200\200", -1)); // thompson-pike varint
|
||||
ASSERT_FALSE(isutf8("\300\200", -1)); // overlong nul
|
||||
ASSERT_FALSE(isutf8("\200\300", -1)); // latin1 c1 control code
|
||||
ASSERT_FALSE(isutf8("\300\300", -1)); // missing continuation
|
||||
ASSERT_FALSE(isutf8("\377\200\200\200\200", -1)); // thompson-pike varint
|
||||
}
|
||||
|
||||
TEST(isutf8, oob) {
|
||||
|
@ -54,15 +53,15 @@ TEST(isutf8, oob) {
|
|||
char *p;
|
||||
for (n = 0; n < 32; ++n) {
|
||||
p = memset(malloc(n), 'a', n);
|
||||
ASSERT_TRUE(_isutf8(p, n));
|
||||
ASSERT_TRUE(isutf8(p, n));
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(isutf8, bench) {
|
||||
EZBENCH_N("_isutf8", 0, _isutf8(0, 0));
|
||||
EZBENCH_N("_isutf8", 5, _isutf8("hello", 5));
|
||||
EZBENCH_N("_isutf8 ascii", kHyperionSize, _isutf8(kHyperion, kHyperionSize));
|
||||
EZBENCH_N("_isutf8 unicode", kBlocktronicsSize,
|
||||
_isutf8(kBlocktronics, kBlocktronicsSize));
|
||||
EZBENCH_N("isutf8", 0, isutf8(0, 0));
|
||||
EZBENCH_N("isutf8", 5, isutf8("hello", 5));
|
||||
EZBENCH_N("isutf8 ascii", kHyperionSize, isutf8(kHyperion, kHyperionSize));
|
||||
EZBENCH_N("isutf8 unicode", kBlocktronicsSize,
|
||||
isutf8(kBlocktronics, kBlocktronicsSize));
|
||||
}
|
||||
|
|
2
third_party/lua/luaencodeluadata.c
vendored
2
third_party/lua/luaencodeluadata.c
vendored
|
@ -192,7 +192,7 @@ static int SerializeString(lua_State *L, char **buf, int idx) {
|
|||
size_t i, n;
|
||||
const char *s;
|
||||
s = lua_tolstring(L, idx, &n);
|
||||
utf8 = _isutf8(s, n);
|
||||
utf8 = isutf8(s, n);
|
||||
RETURN_ON_ERROR(appendw(buf, '"'));
|
||||
for (i = 0; i < n; i++) {
|
||||
switch ((x = kLuaStrXlat[(c = s[i] & 255)])) {
|
||||
|
|
2
third_party/sqlite3/shell.c
vendored
2
third_party/sqlite3/shell.c
vendored
|
@ -209,7 +209,7 @@ typedef unsigned char u8;
|
|||
# define access(f,m) _access((f),(m))
|
||||
# endif
|
||||
# ifndef unlink
|
||||
# define unlink _unlink
|
||||
# define unlink_ _unlink
|
||||
# endif
|
||||
# ifndef strdup
|
||||
# define strdup _strdup
|
||||
|
|
|
@ -163,8 +163,8 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *cname,
|
|||
lfilehdrsize = kZipLfileHdrMinSize + namesize;
|
||||
crc = crc32_z(0, data, uncompsize);
|
||||
GetDosLocalTime(mtim.tv_sec, &mtime, &mdate);
|
||||
if (_isutf8(name, namesize)) gflags |= kZipGflagUtf8;
|
||||
if (S_ISREG(mode) && _istext(data, size)) {
|
||||
if (isutf8(name, namesize)) gflags |= kZipGflagUtf8;
|
||||
if (S_ISREG(mode) && istext(data, size)) {
|
||||
iattrs |= kZipIattrText;
|
||||
}
|
||||
dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0;
|
||||
|
|
|
@ -1084,7 +1084,7 @@ static bool HasString(struct Strings *l, const char *s, size_t n) {
|
|||
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) {
|
||||
#ifndef STATIC
|
||||
|
@ -1100,11 +1100,10 @@ static void UpdateLuaPath(const char *s) {
|
|||
if (t = strstr(curpath, DEFAULTLUAPATH)) {
|
||||
// if the DEFAULT path is found, prepend the path in front of it
|
||||
respath = xasprintf("%.*s%s/.lua/?.lua;%s/.lua/?/init.lua;%s",
|
||||
t-curpath, curpath, s, s, t);
|
||||
t - curpath, curpath, s, s, t);
|
||||
} else {
|
||||
// if the DEFAULT path is not found, append to the end
|
||||
respath = xasprintf("%s;%s/.lua/?.lua;%s/.lua/?/init.lua",
|
||||
curpath, s, s);
|
||||
respath = xasprintf("%s;%s/.lua/?.lua;%s/.lua/?/init.lua", curpath, s, s);
|
||||
}
|
||||
lua_pushstring(L, _gc(respath));
|
||||
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);
|
||||
disk = gflags = iattrs = 0;
|
||||
if (_isutf8(path, pathlen)) gflags |= kZipGflagUtf8;
|
||||
if (_istext(data, datalen)) iattrs |= kZipIattrText;
|
||||
if (isutf8(path, pathlen)) gflags |= kZipGflagUtf8;
|
||||
if (istext(data, datalen)) iattrs |= kZipIattrText;
|
||||
crc = crc32_z(0, data, datalen);
|
||||
if (datalen < 100) {
|
||||
method = kZipCompressionNone;
|
||||
|
|
Loading…
Reference in a new issue