mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Make improvements
- Introduce path module to redbean - Fix glitch with linenoise printing extra line on eof - Introduce closefrom() and close_range() system calls - Make file descriptor closing more secure in pledge.com
This commit is contained in:
parent
439ad21b12
commit
1837dc2e85
31 changed files with 806 additions and 75 deletions
23
examples/tls.c
Normal file
23
examples/tls.c
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#if 0
|
||||||
|
/*─────────────────────────────────────────────────────────────────╗
|
||||||
|
│ To the extent possible under law, Justine Tunney has waived │
|
||||||
|
│ all copyright and related or neighboring rights to this file, │
|
||||||
|
│ as it is written in the following disclaimers: │
|
||||||
|
│ • http://unlicense.org/ │
|
||||||
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview thread local storage
|
||||||
|
*
|
||||||
|
* Cosmopolitan guarantees `_Thread_local` variables are always
|
||||||
|
* accessible, even if you're not using threads.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_Thread_local int x;
|
||||||
|
_Thread_local int y = 42;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return x + y;
|
||||||
|
}
|
|
@ -69,6 +69,8 @@ int chown(const char *, uint32_t, uint32_t);
|
||||||
int chroot(const char *);
|
int chroot(const char *);
|
||||||
int clone(void *, void *, size_t, int, void *, int *, void *, int *);
|
int clone(void *, void *, size_t, int, void *, int *, void *, int *);
|
||||||
int close(int);
|
int close(int);
|
||||||
|
int close_range(unsigned, unsigned, unsigned);
|
||||||
|
int closefrom(int);
|
||||||
int creat(const char *, uint32_t);
|
int creat(const char *, uint32_t);
|
||||||
int dup(int);
|
int dup(int);
|
||||||
int dup2(int, int);
|
int dup2(int, int);
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
/**
|
/**
|
||||||
* Closes file descriptor.
|
* Closes file descriptor.
|
||||||
*
|
*
|
||||||
* This function may be used for file descriptors returned by socket,
|
* This function may be used for file descriptors returned by functions
|
||||||
* accept, epoll_create, and zipos file descriptors too.
|
* like open, socket, accept, epoll_create, and landlock_create_ruleset.
|
||||||
*
|
*
|
||||||
* This function should never be called twice for the same file
|
* This function should never be called twice for the same file
|
||||||
* descriptor, regardless of whether or not an error happened. However
|
* descriptor, regardless of whether or not an error happened. However
|
||||||
|
|
65
libc/calls/close_range.c
Normal file
65
libc/calls/close_range.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes inclusive range of file descriptors, e.g.
|
||||||
|
*
|
||||||
|
* // close all non-stdio file descriptors
|
||||||
|
* if (close_range(3, -1, 0) == -1) {
|
||||||
|
* for (int i = 3; i < 256; ++i) {
|
||||||
|
* close(i);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* This is supported on Linux 5.9+, FreeBSD, and OpenBSD. On FreeBSD,
|
||||||
|
* `flags` must be zero. On OpenBSD, we call closefrom(int) so `last`
|
||||||
|
* should be `-1` in order to get OpenBSD support, otherwise `ENOSYS`
|
||||||
|
* will be returned. We also polyfill closefrom on FreeBSD since it's
|
||||||
|
* available on older kernels.
|
||||||
|
*
|
||||||
|
* On Linux, the following flags are supported:
|
||||||
|
*
|
||||||
|
* - CLOSE_RANGE_UNSHARE
|
||||||
|
* - CLOSE_RANGE_CLOEXEC
|
||||||
|
*
|
||||||
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
* @error ENOSYS if not Linux 5.9+ / FreeBSD / OpenBSD
|
||||||
|
* @error EBADF on OpenBSD if `first` is greater than highest fd
|
||||||
|
* @error EINVAL if flags are bad or first is greater than last
|
||||||
|
* @error EMFILE if a weird race condition happens on Linux
|
||||||
|
* @error EINTR possibly on OpenBSD
|
||||||
|
* @error ENOMEM on Linux maybe
|
||||||
|
*/
|
||||||
|
int close_range(unsigned int first, unsigned int last, unsigned int flags) {
|
||||||
|
int rc, err;
|
||||||
|
err = errno;
|
||||||
|
if ((rc = sys_close_range(first, last, flags)) == -1) {
|
||||||
|
if (errno == ENOSYS && first <= INT_MAX && last == UINT_MAX && !flags) {
|
||||||
|
errno = err;
|
||||||
|
rc = sys_closefrom(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
STRACE("close_range(%d, %d, %#x) → %d% m", first, last, flags, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
60
libc/calls/closefrom.c
Normal file
60
libc/calls/closefrom.c
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes extra file descriptors, e.g.
|
||||||
|
*
|
||||||
|
* // close all non-stdio file descriptors
|
||||||
|
* if (closefrom(3) == -1) {
|
||||||
|
* for (int i = 3; i < 256; ++i) {
|
||||||
|
* close(i);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
* @error ENOSYS if not Linux 5.9+ / FreeBSD / OpenBSD
|
||||||
|
* @error EBADF on OpenBSD if `first` is greater than highest fd
|
||||||
|
* @error EINVAL if flags are bad or first is greater than last
|
||||||
|
* @error EMFILE if a weird race condition happens on Linux
|
||||||
|
* @error EINTR possibly on OpenBSD
|
||||||
|
* @error ENOMEM on Linux maybe
|
||||||
|
* @note supported on Linux 5.9+, FreeBSD 8+, and OpenBSD
|
||||||
|
*/
|
||||||
|
int closefrom(int first) {
|
||||||
|
int rc, err;
|
||||||
|
if (first >= 0) {
|
||||||
|
err = errno;
|
||||||
|
if ((rc = sys_close_range(first, -1, 0)) == -1) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
errno = err;
|
||||||
|
rc = sys_closefrom(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc = ebadf();
|
||||||
|
}
|
||||||
|
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -28,6 +28,8 @@ i32 sys_arch_prctl(i32, i64) hidden;
|
||||||
i32 sys_chdir(const char *) hidden;
|
i32 sys_chdir(const char *) hidden;
|
||||||
i32 sys_chroot(const char *) hidden;
|
i32 sys_chroot(const char *) hidden;
|
||||||
i32 sys_close(i32) hidden;
|
i32 sys_close(i32) hidden;
|
||||||
|
i32 sys_close_range(u32, u32, u32) hidden;
|
||||||
|
i32 sys_closefrom(i32) hidden;
|
||||||
i32 sys_dup(i32) hidden;
|
i32 sys_dup(i32) hidden;
|
||||||
i32 sys_dup2(i32, i32) hidden;
|
i32 sys_dup2(i32, i32) hidden;
|
||||||
i32 sys_dup3(i32, i32, i32) hidden;
|
i32 sys_dup3(i32, i32, i32) hidden;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
*
|
*
|
||||||
* path │ dirname() │ basename()
|
* path │ dirname() │ basename()
|
||||||
* ─────────────────────────────────
|
* ─────────────────────────────────
|
||||||
|
* 0 │ . │ .
|
||||||
* . │ . │ .
|
* . │ . │ .
|
||||||
* .. │ . │ ..
|
* .. │ . │ ..
|
||||||
* / │ / │ /
|
* / │ / │ /
|
||||||
|
@ -48,7 +49,7 @@ char *basename(char *path) {
|
||||||
for (; i && _isdirsep(path[i]); i--) {
|
for (; i && _isdirsep(path[i]); i--) {
|
||||||
path[i] = 0;
|
path[i] = 0;
|
||||||
}
|
}
|
||||||
for (; i && !_isdirsep(path[i - 1]);) {
|
while (i && !_isdirsep(path[i - 1])) {
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
return path + i;
|
return path + i;
|
||||||
|
|
|
@ -24,12 +24,18 @@
|
||||||
/**
|
/**
|
||||||
* Joins paths, e.g.
|
* Joins paths, e.g.
|
||||||
*
|
*
|
||||||
|
* 0 + 0 → 0
|
||||||
|
* "" + "" → ""
|
||||||
|
* "a" + 0 → "a"
|
||||||
|
* "a" + "" → "a/"
|
||||||
|
* 0 + "b" → "b"
|
||||||
|
* "" + "b" → "b"
|
||||||
|
* "." + "b" → "./b"
|
||||||
|
* "b" + "." → "b/."
|
||||||
* "a" + "b" → "a/b"
|
* "a" + "b" → "a/b"
|
||||||
* "a/" + "b" → "a/b"
|
* "a/" + "b" → "a/b"
|
||||||
* "a" + "b/" → "a/b/"
|
* "a" + "b/" → "a/b/"
|
||||||
* "a" + "/b" → "/b"
|
* "a" + "/b" → "/b"
|
||||||
* "." + "b" → "b"
|
|
||||||
* "" + "b" → "b"
|
|
||||||
*
|
*
|
||||||
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
||||||
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
||||||
|
@ -39,14 +45,11 @@ char *_joinpaths(char *buf, size_t size, const char *path, const char *other) {
|
||||||
size_t pathlen, otherlen;
|
size_t pathlen, otherlen;
|
||||||
if (!other) return path;
|
if (!other) return path;
|
||||||
if (!path) return other;
|
if (!path) return other;
|
||||||
otherlen = strlen(other);
|
|
||||||
if (!otherlen) {
|
|
||||||
return (/*unconst*/ char *)path;
|
|
||||||
}
|
|
||||||
pathlen = strlen(path);
|
pathlen = strlen(path);
|
||||||
if (!pathlen || (READ16LE(path) == READ16LE(".")) || *other == '/') {
|
if (!pathlen || *other == '/') {
|
||||||
return (/*unconst*/ char *)other;
|
return (/*unconst*/ char *)other;
|
||||||
}
|
}
|
||||||
|
otherlen = strlen(other);
|
||||||
if (path[pathlen - 1] == '/') {
|
if (path[pathlen - 1] == '/') {
|
||||||
if (pathlen + otherlen + 1 <= size) {
|
if (pathlen + otherlen + 1 <= size) {
|
||||||
memmove(buf, path, pathlen);
|
memmove(buf, path, pathlen);
|
||||||
|
|
|
@ -84,6 +84,7 @@ static const uint16_t kPledgeLinuxStdio[] = {
|
||||||
__NR_linux_clock_getres, //
|
__NR_linux_clock_getres, //
|
||||||
__NR_linux_clock_gettime, //
|
__NR_linux_clock_gettime, //
|
||||||
__NR_linux_clock_nanosleep, //
|
__NR_linux_clock_nanosleep, //
|
||||||
|
__NR_linux_close_range, //
|
||||||
__NR_linux_close, //
|
__NR_linux_close, //
|
||||||
__NR_linux_write, //
|
__NR_linux_write, //
|
||||||
__NR_linux_writev, //
|
__NR_linux_writev, //
|
||||||
|
|
|
@ -296,6 +296,9 @@ static int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
* possible to use opendir() and go fishing for paths which weren't
|
* possible to use opendir() and go fishing for paths which weren't
|
||||||
* previously known.
|
* previously known.
|
||||||
*
|
*
|
||||||
|
* 5. Always specify at least one path. OpenBSD has unclear semantics
|
||||||
|
* when `pledge(0,0)` is used without any previous calls.
|
||||||
|
*
|
||||||
* This system call is supported natively on OpenBSD and polyfilled on
|
* This system call is supported natively on OpenBSD and polyfilled on
|
||||||
* Linux using the Landlock LSM[1].
|
* Linux using the Landlock LSM[1].
|
||||||
*
|
*
|
||||||
|
@ -321,6 +324,7 @@ static int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
* @raise EINVAL if one argument is set and the other is not
|
* @raise EINVAL if one argument is set and the other is not
|
||||||
* @raise EINVAL if an invalid character in `permissions` was found
|
* @raise EINVAL if an invalid character in `permissions` was found
|
||||||
* @raise EPERM if unveil() is called after locking
|
* @raise EPERM if unveil() is called after locking
|
||||||
|
* @note on Linux this function requires Linux Kernel 5.13+
|
||||||
* @see [1] https://docs.kernel.org/userspace-api/landlock.html
|
* @see [1] https://docs.kernel.org/userspace-api/landlock.html
|
||||||
*/
|
*/
|
||||||
int unveil(const char *path, const char *permissions) {
|
int unveil(const char *path, const char *permissions) {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall close_range,0xfffffffffffff1b4,globl
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall closefrom,0xfff11f1fdfffffff,globl
|
|
2
libc/sysv/calls/sys_close_range.s
Normal file
2
libc/sysv/calls/sys_close_range.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_close_range,0xffffff23fffff1b4,globl,hidden
|
2
libc/sysv/calls/sys_closefrom.s
Normal file
2
libc/sysv/calls/sys_closefrom.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_closefrom,0xfff11f1fdfffffff,globl,hidden
|
|
@ -586,6 +586,12 @@ syscon ss MINSIGSTKSZ 2048 32768 2048 12288 8192 2048 # overlayed
|
||||||
syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus
|
syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus
|
||||||
syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus
|
syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus
|
||||||
|
|
||||||
|
# close_range() values
|
||||||
|
#
|
||||||
|
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||||
|
syscon close CLOSE_RANGE_UNSHARE 2 -1 -1 -1 -1 -1 #
|
||||||
|
syscon close CLOSE_RANGE_CLOEXEC 4 -1 -1 -1 -1 -1 #
|
||||||
|
|
||||||
# clock_{gettime,settime} timers
|
# clock_{gettime,settime} timers
|
||||||
#
|
#
|
||||||
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||||
|
|
16
libc/sysv/consts/close.h
Normal file
16
libc/sysv/consts/close.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CLOSE_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_CLOSE_H_
|
||||||
|
#include "libc/runtime/symbolic.h"
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
extern const long CLOSE_RANGE_UNSHARE;
|
||||||
|
extern const long CLOSE_RANGE_CLOEXEC;
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
||||||
|
#define CLOSE_RANGE_UNSHARE SYMBOLIC(CLOSE_RANGE_UNSHARE)
|
||||||
|
#define CLOSE_RANGE_CLOEXEC SYMBOLIC(CLOSE_RANGE_CLOEXEC)
|
||||||
|
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CLOSE_H_ */
|
|
@ -383,7 +383,7 @@ scall fsmount 0xfffffffffffff1b0 globl
|
||||||
scall fspick 0xfffffffffffff1b1 globl
|
scall fspick 0xfffffffffffff1b1 globl
|
||||||
scall pidfd_open 0xfffffffffffff1b2 globl
|
scall pidfd_open 0xfffffffffffff1b2 globl
|
||||||
scall clone3 0xfffffffffffff1b3 globl
|
scall clone3 0xfffffffffffff1b3 globl
|
||||||
scall close_range 0xfffffffffffff1b4 globl
|
scall sys_close_range 0xffffff23fffff1b4 globl hidden # linux 5.9
|
||||||
scall sys_openat2 0xfffffffffffff1b5 globl hidden # Linux 5.6
|
scall sys_openat2 0xfffffffffffff1b5 globl hidden # Linux 5.6
|
||||||
scall pidfd_getfd 0xfffffffffffff1b6 globl
|
scall pidfd_getfd 0xfffffffffffff1b6 globl
|
||||||
scall sys_faccessat2 0xfffffffffffff1b7 globl hidden
|
scall sys_faccessat2 0xfffffffffffff1b7 globl hidden
|
||||||
|
@ -463,7 +463,7 @@ scall chflagsat 0xfff06b21cfffffff globl
|
||||||
scall profil 0x02c02c02cfffffff globl
|
scall profil 0x02c02c02cfffffff globl
|
||||||
scall fhstatfs 0xfff04122efffffff globl
|
scall fhstatfs 0xfff04122efffffff globl
|
||||||
scall utrace 0x1320d114ffffffff globl
|
scall utrace 0x1320d114ffffffff globl
|
||||||
scall closefrom 0xfff11f1fdfffffff globl
|
scall sys_closefrom 0xfff11f1fdfffffff globl hidden
|
||||||
#───────────────────────────XNU──────────────────────────────
|
#───────────────────────────XNU──────────────────────────────
|
||||||
scall __pthread_markcancel 0xfffffffff214cfff globl
|
scall __pthread_markcancel 0xfffffffff214cfff globl
|
||||||
scall __pthread_kill 0xfffffffff2148fff globl
|
scall __pthread_kill 0xfffffffff2148fff globl
|
||||||
|
|
61
test/libc/calls/closefrom_test.c
Normal file
61
test/libc/calls/closefrom_test.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/sysv/consts/f.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
|
void SetUp(void) {
|
||||||
|
if (closefrom(3) == -1) {
|
||||||
|
if (IsOpenbsd()) {
|
||||||
|
ASSERT_EQ(EBADF, errno);
|
||||||
|
} else {
|
||||||
|
ASSERT_EQ(ENOSYS, errno);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(closefrom, test) {
|
||||||
|
ASSERT_SYS(0, 3, dup(2));
|
||||||
|
ASSERT_SYS(0, 4, dup(2));
|
||||||
|
ASSERT_SYS(0, 5, dup(2));
|
||||||
|
ASSERT_SYS(0, 6, dup(2));
|
||||||
|
EXPECT_SYS(0, 0, closefrom(3));
|
||||||
|
ASSERT_SYS(0, 0, fcntl(2, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(3, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(4, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(5, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(6, F_GETFD));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(close_range, test) {
|
||||||
|
ASSERT_SYS(0, 3, dup(2));
|
||||||
|
ASSERT_SYS(0, 4, dup(2));
|
||||||
|
ASSERT_SYS(0, 5, dup(2));
|
||||||
|
ASSERT_SYS(0, 6, dup(2));
|
||||||
|
EXPECT_SYS(0, 0, close_range(3, -1, 0));
|
||||||
|
ASSERT_SYS(0, 0, fcntl(2, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(3, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(4, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(5, F_GETFD));
|
||||||
|
ASSERT_SYS(EBADF, -1, fcntl(6, F_GETFD));
|
||||||
|
}
|
|
@ -16,23 +16,37 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/str/path.h"
|
#include "libc/str/path.h"
|
||||||
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/x/x.h"
|
||||||
|
|
||||||
|
char b[PATH_MAX];
|
||||||
|
|
||||||
TEST(xjoinpaths, test) {
|
TEST(xjoinpaths, test) {
|
||||||
char b[PATH_MAX];
|
|
||||||
EXPECT_EQ(NULL, _joinpaths(b, sizeof(b), 0, 0));
|
EXPECT_EQ(NULL, _joinpaths(b, sizeof(b), 0, 0));
|
||||||
EXPECT_STREQ("x", _joinpaths(b, sizeof(b), "x", 0));
|
EXPECT_STREQ("x", _joinpaths(b, sizeof(b), "x", 0));
|
||||||
EXPECT_STREQ("x", _joinpaths(b, sizeof(b), 0, "x"));
|
EXPECT_STREQ("x", _joinpaths(b, sizeof(b), 0, "x"));
|
||||||
EXPECT_STREQ("", _joinpaths(b, sizeof(b), "", ""));
|
EXPECT_STREQ("", _joinpaths(b, sizeof(b), "", ""));
|
||||||
|
EXPECT_STREQ("", _joinpaths(b, sizeof(b), "", 0));
|
||||||
|
EXPECT_STREQ("", _joinpaths(b, sizeof(b), 0, ""));
|
||||||
|
EXPECT_STREQ("", _joinpaths(b, sizeof(b), "", ""));
|
||||||
EXPECT_STREQ("b", _joinpaths(b, sizeof(b), "", "b"));
|
EXPECT_STREQ("b", _joinpaths(b, sizeof(b), "", "b"));
|
||||||
|
EXPECT_STREQ("a/", _joinpaths(b, sizeof(b), "a", ""));
|
||||||
EXPECT_STREQ("a/b", _joinpaths(b, sizeof(b), "a", "b"));
|
EXPECT_STREQ("a/b", _joinpaths(b, sizeof(b), "a", "b"));
|
||||||
EXPECT_STREQ("a/b", _joinpaths(b, sizeof(b), "a/", "b"));
|
EXPECT_STREQ("a/b", _joinpaths(b, sizeof(b), "a/", "b"));
|
||||||
EXPECT_STREQ("a/b/", _joinpaths(b, sizeof(b), "a", "b/"));
|
EXPECT_STREQ("a/b/", _joinpaths(b, sizeof(b), "a", "b/"));
|
||||||
EXPECT_STREQ("/b", _joinpaths(b, sizeof(b), "a", "/b"));
|
EXPECT_STREQ("/b", _joinpaths(b, sizeof(b), "a", "/b"));
|
||||||
EXPECT_STREQ("b", _joinpaths(b, sizeof(b), ".", "b"));
|
EXPECT_STREQ("./b", _joinpaths(b, sizeof(b), ".", "b"));
|
||||||
|
EXPECT_STREQ("b/.", _joinpaths(b, sizeof(b), "b", "."));
|
||||||
EXPECT_EQ(NULL, _joinpaths(b, 3, "a", "b/"));
|
EXPECT_EQ(NULL, _joinpaths(b, 3, "a", "b/"));
|
||||||
EXPECT_EQ(NULL, _joinpaths(b, 4, "a", "b/"));
|
EXPECT_EQ(NULL, _joinpaths(b, 4, "a", "b/"));
|
||||||
EXPECT_STREQ("a/b", _joinpaths(b, 4, "a/", "b"));
|
EXPECT_STREQ("a/b", _joinpaths(b, 4, "a/", "b"));
|
||||||
EXPECT_STREQ("a/b/", _joinpaths(b, 5, "a", "b/"));
|
EXPECT_STREQ("a/b/", _joinpaths(b, 5, "a", "b/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BENCH(joinpaths, bench) {
|
||||||
|
EZBENCH2("_joinpaths", donothing, _joinpaths(b, sizeof(b), "care", "bear"));
|
||||||
|
EZBENCH2("xjoinpaths", donothing, free(xjoinpaths("care", "bear")));
|
||||||
|
}
|
||||||
|
|
|
@ -182,6 +182,18 @@ TEST(unveil, dirfdHacking_doesntWork) {
|
||||||
EXITS(0);
|
EXITS(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unveil, mostRestrictivePolicy) {
|
||||||
|
if (IsOpenbsd()) return; // openbsd behaves oddly; see docs
|
||||||
|
SPAWN();
|
||||||
|
ASSERT_SYS(0, 0, mkdir("jail", 0755));
|
||||||
|
ASSERT_SYS(0, 0, mkdir("garden", 0755));
|
||||||
|
ASSERT_SYS(0, 0, touch("garden/secret.txt", 0644));
|
||||||
|
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||||
|
ASSERT_SYS(EACCES_OR_ENOENT, -1, open("jail", O_RDONLY | O_DIRECTORY));
|
||||||
|
ASSERT_SYS(EACCES_OR_ENOENT, -1, open("garden/secret.txt", O_RDONLY));
|
||||||
|
EXITS(0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unveil, overlappingDirectories_inconsistentBehavior) {
|
TEST(unveil, overlappingDirectories_inconsistentBehavior) {
|
||||||
SPAWN();
|
SPAWN();
|
||||||
ASSERT_SYS(0, 0, makedirs("f1/f2", 0755));
|
ASSERT_SYS(0, 0, makedirs("f1/f2", 0755));
|
||||||
|
|
28
test/libc/runtime/tls_test.c
Normal file
28
test/libc/runtime/tls_test.c
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*-*- 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 2022 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/runtime/runtime.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
|
_Thread_local int x;
|
||||||
|
_Thread_local int y = 40;
|
||||||
|
int z = 2;
|
||||||
|
|
||||||
|
TEST(tls, test) {
|
||||||
|
EXPECT_EQ(42, x + y + z);
|
||||||
|
}
|
77
test/tool/net/path_test.lua
Normal file
77
test/tool/net/path_test.lua
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
-- Copyright 2022 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.
|
||||||
|
|
||||||
|
assert("/usr/lib" == path.dirname("/usr/lib/foo.bar"))
|
||||||
|
assert("/usr" == path.dirname("/usr/lib"))
|
||||||
|
assert("usr" == path.dirname("usr/lib"))
|
||||||
|
assert("/" == path.dirname("/usr//"))
|
||||||
|
assert("/" == path.dirname("/usr/"))
|
||||||
|
assert("." == path.dirname("usr"))
|
||||||
|
assert("/" == path.dirname("/"))
|
||||||
|
assert("." == path.dirname("."))
|
||||||
|
assert("." == path.dirname(".."))
|
||||||
|
assert("." == path.dirname(""))
|
||||||
|
assert("." == path.dirname(nil))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
assert("lib" == path.basename("/usr/lib"))
|
||||||
|
assert("lib" == path.basename("usr/lib"))
|
||||||
|
assert("usr" == path.basename("/usr/"))
|
||||||
|
assert("usr" == path.basename("/usr"))
|
||||||
|
assert("/" == path.basename("/"))
|
||||||
|
assert("." == path.basename("."))
|
||||||
|
assert(".." == path.basename(".."))
|
||||||
|
assert("." == path.basename(""))
|
||||||
|
assert("foo" == path.basename("foo/"))
|
||||||
|
assert("foo" == path.basename("foo//"))
|
||||||
|
assert("/" == path.basename("///"))
|
||||||
|
assert("0" == path.basename(0))
|
||||||
|
assert("." == path.basename(nil))
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
assert("a" == path.join("a"))
|
||||||
|
assert("a/b/c" == path.join("a", "b", "c"))
|
||||||
|
assert("/c" == path.join("a", "b", "/c"))
|
||||||
|
assert("/b/c" == path.join("a", "/b", "c"))
|
||||||
|
assert("/c" == path.join("a", "/b", "/c"))
|
||||||
|
assert("./." == path.join(".", "."))
|
||||||
|
assert("./.." == path.join(".", ".."))
|
||||||
|
assert("../." == path.join("..", "."))
|
||||||
|
assert("./a" == path.join(".", "a"))
|
||||||
|
assert("c//c" == path.join("c//", "c"))
|
||||||
|
assert("a/." == path.join("a", "."))
|
||||||
|
assert("a/b" == path.join("a", "b"))
|
||||||
|
assert("a/b" == path.join("a/", "b"))
|
||||||
|
assert("a/b/" == path.join("a", "b/"))
|
||||||
|
assert("/b" == path.join("a", "/b"))
|
||||||
|
assert("./b" == path.join(".", "b"))
|
||||||
|
assert("a" * 3000 .."/123/".. "b" * 3000 .. "/123" == path.join("a" * 3000, 123, "b" * 3000, 123))
|
||||||
|
assert("1/2/3" == path.join(1, 2, 3))
|
||||||
|
assert(nil == path.join(nil))
|
||||||
|
assert(nil == path.join(nil, nil))
|
||||||
|
assert("a" == path.join("a", nil))
|
||||||
|
assert("a" == path.join(nil, "a"))
|
||||||
|
assert("a/a" == path.join("a", nil, "a"))
|
||||||
|
assert("a/a" == path.join("a/", nil, "a"))
|
||||||
|
assert("" == path.join(""))
|
||||||
|
assert("" == path.join("", ""))
|
||||||
|
assert("b" == path.join("", "b"))
|
||||||
|
assert("a" == path.join("", "a"))
|
||||||
|
assert("a/" == path.join("a", ""))
|
||||||
|
assert("a/b" == path.join("a", "", "b"))
|
||||||
|
assert("a/b" == path.join("a", "", "", "b"))
|
||||||
|
assert("a/b/" == path.join("a", "", "", "b", ""))
|
2
third_party/linenoise/linenoise.c
vendored
2
third_party/linenoise/linenoise.c
vendored
|
@ -2428,7 +2428,9 @@ char *linenoiseRaw(const char *prompt, int infd, int outfd) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
if (rc != -1) {
|
if (rc != -1) {
|
||||||
|
if (buf) {
|
||||||
linenoiseWriteStr(outfd, "\n");
|
linenoiseWriteStr(outfd, "\n");
|
||||||
|
}
|
||||||
return buf;
|
return buf;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
93
third_party/lua/lunix.c
vendored
93
third_party/lua/lunix.c
vendored
|
@ -35,6 +35,7 @@
|
||||||
#include "libc/calls/ucontext.h"
|
#include "libc/calls/ucontext.h"
|
||||||
#include "libc/dns/dns.h"
|
#include "libc/dns/dns.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
#include "libc/fmt/magnumstrs.internal.h"
|
#include "libc/fmt/magnumstrs.internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
#include "libc/sock/syslog.h"
|
#include "libc/sock/syslog.h"
|
||||||
#include "libc/stdio/append.internal.h"
|
#include "libc/stdio/append.internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/str/path.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/af.h"
|
#include "libc/sysv/consts/af.h"
|
||||||
#include "libc/sysv/consts/at.h"
|
#include "libc/sysv/consts/at.h"
|
||||||
|
@ -89,6 +91,7 @@
|
||||||
#include "third_party/lua/lgc.h"
|
#include "third_party/lua/lgc.h"
|
||||||
#include "third_party/lua/lua.h"
|
#include "third_party/lua/lua.h"
|
||||||
#include "third_party/lua/luaconf.h"
|
#include "third_party/lua/luaconf.h"
|
||||||
|
#include "third_party/lua/lunix.h"
|
||||||
#include "tool/net/luacheck.h"
|
#include "tool/net/luacheck.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,12 +99,6 @@
|
||||||
* @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD
|
* @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct UnixErrno {
|
|
||||||
int errno_;
|
|
||||||
int winerr;
|
|
||||||
const char *call;
|
|
||||||
};
|
|
||||||
|
|
||||||
static lua_State *GL;
|
static lua_State *GL;
|
||||||
|
|
||||||
static void *LuaRealloc(lua_State *L, void *p, size_t n) {
|
static void *LuaRealloc(lua_State *L, void *p, size_t n) {
|
||||||
|
@ -177,7 +174,7 @@ static dontinline int ReturnString(lua_State *L, const char *x) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SysretErrno(lua_State *L, const char *call, int olderr) {
|
int LuaUnixSysretErrno(lua_State *L, const char *call, int olderr) {
|
||||||
struct UnixErrno *ep;
|
struct UnixErrno *ep;
|
||||||
int i, unixerr, winerr;
|
int i, unixerr, winerr;
|
||||||
unixerr = errno;
|
unixerr = errno;
|
||||||
|
@ -195,7 +192,7 @@ static int SysretErrno(lua_State *L, const char *call, int olderr) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SysretBool(lua_State *L, const char *call, int olderr, int rc) {
|
static int SysretBool(lua_State *L, const char *call, int olderr, int rc) {
|
||||||
if (!IsTiny() && (rc != 0 && rc != -1)) {
|
if (!IsTiny() && (rc != 0 && rc != -1)) {
|
||||||
WARNF("syscall supposed to return 0 / -1 but got %d", rc);
|
WARNF("syscall supposed to return 0 / -1 but got %d", rc);
|
||||||
}
|
}
|
||||||
|
@ -203,7 +200,7 @@ int SysretBool(lua_State *L, const char *call, int olderr, int rc) {
|
||||||
lua_pushboolean(L, true);
|
lua_pushboolean(L, true);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, call, olderr);
|
return LuaUnixSysretErrno(L, call, olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +213,7 @@ static int SysretInteger(lua_State *L, const char *call, int olderr,
|
||||||
lua_pushinteger(L, rc);
|
lua_pushinteger(L, rc);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, call, olderr);
|
return LuaUnixSysretErrno(L, call, olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +499,7 @@ static int LuaUnixReadlink(lua_State *L) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
return SysretErrno(L, "readlink", olderr);
|
return LuaUnixSysretErrno(L, "readlink", olderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.getcwd()
|
// unix.getcwd()
|
||||||
|
@ -516,7 +513,7 @@ static int LuaUnixGetcwd(lua_State *L) {
|
||||||
free(path);
|
free(path);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "getcwd", olderr);
|
return LuaUnixSysretErrno(L, "getcwd", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,14 +555,14 @@ static int LuaUnixExecve(lua_State *L) {
|
||||||
freeme2 = envp;
|
freeme2 = envp;
|
||||||
} else {
|
} else {
|
||||||
FreeStringList(argv);
|
FreeStringList(argv);
|
||||||
return SysretErrno(L, "execve", olderr);
|
return LuaUnixSysretErrno(L, "execve", olderr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
envp = environ;
|
envp = environ;
|
||||||
freeme2 = 0;
|
freeme2 = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "execve", olderr);
|
return LuaUnixSysretErrno(L, "execve", olderr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ezargs[0] = prog;
|
ezargs[0] = prog;
|
||||||
|
@ -578,7 +575,7 @@ static int LuaUnixExecve(lua_State *L) {
|
||||||
execve(prog, argv, envp);
|
execve(prog, argv, envp);
|
||||||
FreeStringList(freeme1);
|
FreeStringList(freeme1);
|
||||||
FreeStringList(freeme2);
|
FreeStringList(freeme2);
|
||||||
return SysretErrno(L, "execve", olderr);
|
return LuaUnixSysretErrno(L, "execve", olderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.commandv(prog:str)
|
// unix.commandv(prog:str)
|
||||||
|
@ -597,7 +594,7 @@ static int LuaUnixCommandv(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
free(pathbuf);
|
free(pathbuf);
|
||||||
return SysretErrno(L, "commandv", olderr);
|
return LuaUnixSysretErrno(L, "commandv", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +612,7 @@ static int LuaUnixRealpath(lua_State *L) {
|
||||||
free(resolved);
|
free(resolved);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "realpath", olderr);
|
return LuaUnixSysretErrno(L, "realpath", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,7 +653,7 @@ static int LuaUnixGetrlimit(lua_State *L) {
|
||||||
lua_pushinteger(L, FixLimit(rlim.rlim_max));
|
lua_pushinteger(L, FixLimit(rlim.rlim_max));
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "getrlimit", olderr);
|
return LuaUnixSysretErrno(L, "getrlimit", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,7 +667,7 @@ static int LuaUnixGetrusage(lua_State *L) {
|
||||||
LuaPushRusage(L, &ru);
|
LuaPushRusage(L, &ru);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "getrusage", olderr);
|
return LuaUnixSysretErrno(L, "getrusage", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,7 +701,7 @@ static int LuaUnixWait(lua_State *L) {
|
||||||
LuaPushRusage(L, &ru);
|
LuaPushRusage(L, &ru);
|
||||||
return 3;
|
return 3;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "wait", olderr);
|
return LuaUnixSysretErrno(L, "wait", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,7 +742,7 @@ static int LuaUnixPipe(lua_State *L) {
|
||||||
lua_pushinteger(L, pipefd[1]);
|
lua_pushinteger(L, pipefd[1]);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "pipe", olderr);
|
return LuaUnixSysretErrno(L, "pipe", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,7 +895,7 @@ static int LuaUnixGettime(lua_State *L) {
|
||||||
lua_pushinteger(L, ts.tv_nsec);
|
lua_pushinteger(L, ts.tv_nsec);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "clock_gettime", olderr);
|
return LuaUnixSysretErrno(L, "clock_gettime", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,7 +912,7 @@ static int LuaUnixNanosleep(lua_State *L) {
|
||||||
lua_pushinteger(L, rem.tv_nsec);
|
lua_pushinteger(L, rem.tv_nsec);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "nanosleep", olderr);
|
return LuaUnixSysretErrno(L, "nanosleep", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,7 +1013,7 @@ static int LuaUnixRead(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
free(buf);
|
free(buf);
|
||||||
return SysretErrno(L, "read", olderr);
|
return LuaUnixSysretErrno(L, "read", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1052,7 +1049,7 @@ static int LuaUnixStat(lua_State *L) {
|
||||||
LuaPushStat(L, &st);
|
LuaPushStat(L, &st);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "stat", olderr);
|
return LuaUnixSysretErrno(L, "stat", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1066,7 +1063,7 @@ static int LuaUnixFstat(lua_State *L) {
|
||||||
LuaPushStat(L, &st);
|
LuaPushStat(L, &st);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "fstat", olderr);
|
return LuaUnixSysretErrno(L, "fstat", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,7 +1137,7 @@ static int LuaUnixSetsockopt(lua_State *L) {
|
||||||
if (level == -1 || optname == 0) {
|
if (level == -1 || optname == 0) {
|
||||||
NoProtocolOption:
|
NoProtocolOption:
|
||||||
enoprotoopt();
|
enoprotoopt();
|
||||||
return SysretErrno(L, "setsockopt", olderr);
|
return LuaUnixSysretErrno(L, "setsockopt", olderr);
|
||||||
}
|
}
|
||||||
if (IsSockoptBool(level, optname)) {
|
if (IsSockoptBool(level, optname)) {
|
||||||
// unix.setsockopt(fd:int, level:int, optname:int, value:bool)
|
// unix.setsockopt(fd:int, level:int, optname:int, value:bool)
|
||||||
|
@ -1191,7 +1188,7 @@ static int LuaUnixGetsockopt(lua_State *L) {
|
||||||
if (level == -1 || optname == 0) {
|
if (level == -1 || optname == 0) {
|
||||||
NoProtocolOption:
|
NoProtocolOption:
|
||||||
enoprotoopt();
|
enoprotoopt();
|
||||||
return SysretErrno(L, "setsockopt", olderr);
|
return LuaUnixSysretErrno(L, "setsockopt", olderr);
|
||||||
}
|
}
|
||||||
if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) {
|
if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) {
|
||||||
// unix.getsockopt(fd:int, level:int, optname:int)
|
// unix.getsockopt(fd:int, level:int, optname:int)
|
||||||
|
@ -1240,7 +1237,7 @@ static int LuaUnixGetsockopt(lua_State *L) {
|
||||||
} else {
|
} else {
|
||||||
goto NoProtocolOption;
|
goto NoProtocolOption;
|
||||||
}
|
}
|
||||||
return SysretErrno(L, "getsockopt", olderr);
|
return LuaUnixSysretErrno(L, "getsockopt", olderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.socket([family:int[, type:int[, protocol:int]]])
|
// unix.socket([family:int[, type:int[, protocol:int]]])
|
||||||
|
@ -1266,7 +1263,7 @@ static int LuaUnixSocketpair(lua_State *L) {
|
||||||
lua_pushinteger(L, sv[1]);
|
lua_pushinteger(L, sv[1]);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "socketpair", olderr);
|
return LuaUnixSysretErrno(L, "socketpair", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1315,7 +1312,7 @@ static int LuaUnixGetname(lua_State *L, const char *name,
|
||||||
if (!func(luaL_checkinteger(L, 1), &ss, &addrsize)) {
|
if (!func(luaL_checkinteger(L, 1), &ss, &addrsize)) {
|
||||||
return PushSockaddr(L, &ss);
|
return PushSockaddr(L, &ss);
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, name, olderr);
|
return LuaUnixSysretErrno(L, name, olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1348,14 +1345,14 @@ static int LuaUnixSiocgifconf(lua_State *L) {
|
||||||
data = LuaAllocOrDie(L, (n = 4096));
|
data = LuaAllocOrDie(L, (n = 4096));
|
||||||
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
|
||||||
free(data);
|
free(data);
|
||||||
return SysretErrno(L, "siocgifconf", olderr);
|
return LuaUnixSysretErrno(L, "siocgifconf", olderr);
|
||||||
}
|
}
|
||||||
conf.ifc_buf = data;
|
conf.ifc_buf = data;
|
||||||
conf.ifc_len = n;
|
conf.ifc_len = n;
|
||||||
if (ioctl(fd, SIOCGIFCONF, &conf) == -1) {
|
if (ioctl(fd, SIOCGIFCONF, &conf) == -1) {
|
||||||
close(fd);
|
close(fd);
|
||||||
free(data);
|
free(data);
|
||||||
return SysretErrno(L, "siocgifconf", olderr);
|
return LuaUnixSysretErrno(L, "siocgifconf", olderr);
|
||||||
}
|
}
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -1415,7 +1412,7 @@ static int LuaUnixGethostname(lua_State *L) {
|
||||||
enomem();
|
enomem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SysretErrno(L, "gethostname", olderr);
|
return LuaUnixSysretErrno(L, "gethostname", olderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.accept(serverfd:int[, flags:int])
|
// unix.accept(serverfd:int[, flags:int])
|
||||||
|
@ -1435,7 +1432,7 @@ static int LuaUnixAccept(lua_State *L) {
|
||||||
lua_pushinteger(L, clientfd);
|
lua_pushinteger(L, clientfd);
|
||||||
return 1 + PushSockaddr(L, &ss);
|
return 1 + PushSockaddr(L, &ss);
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "accept", olderr);
|
return LuaUnixSysretErrno(L, "accept", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1458,7 +1455,7 @@ static int LuaUnixPoll(lua_State *L) {
|
||||||
++nfds;
|
++nfds;
|
||||||
} else {
|
} else {
|
||||||
free(fds);
|
free(fds);
|
||||||
return SysretErrno(L, "poll", olderr);
|
return LuaUnixSysretErrno(L, "poll", olderr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ignore non-integer key
|
// ignore non-integer key
|
||||||
|
@ -1478,7 +1475,7 @@ static int LuaUnixPoll(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
free(fds);
|
free(fds);
|
||||||
return SysretErrno(L, "poll", olderr);
|
return LuaUnixSysretErrno(L, "poll", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1508,7 +1505,7 @@ static int LuaUnixRecvfrom(lua_State *L) {
|
||||||
return pushed;
|
return pushed;
|
||||||
} else {
|
} else {
|
||||||
free(buf);
|
free(buf);
|
||||||
return SysretErrno(L, "recvfrom", olderr);
|
return LuaUnixSysretErrno(L, "recvfrom", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1534,7 +1531,7 @@ static int LuaUnixRecv(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
free(buf);
|
free(buf);
|
||||||
return SysretErrno(L, "recv", olderr);
|
return LuaUnixSysretErrno(L, "recv", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1591,7 +1588,7 @@ static int LuaUnixSigprocmask(lua_State *L) {
|
||||||
LuaPushSigset(L, oldmask);
|
LuaPushSigset(L, oldmask);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "sigprocmask", olderr);
|
return LuaUnixSysretErrno(L, "sigprocmask", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1692,7 +1689,7 @@ static int LuaUnixSigaction(lua_State *L) {
|
||||||
LuaPushSigset(L, oldsa.sa_mask);
|
LuaPushSigset(L, oldsa.sa_mask);
|
||||||
return 3;
|
return 3;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "sigaction", olderr);
|
return LuaUnixSysretErrno(L, "sigaction", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1701,7 +1698,7 @@ static int LuaUnixSigaction(lua_State *L) {
|
||||||
static int LuaUnixSigsuspend(lua_State *L) {
|
static int LuaUnixSigsuspend(lua_State *L) {
|
||||||
int olderr = errno;
|
int olderr = errno;
|
||||||
sigsuspend(!lua_isnoneornil(L, 1) ? luaL_checkudata(L, 1, "unix.Sigset") : 0);
|
sigsuspend(!lua_isnoneornil(L, 1) ? luaL_checkudata(L, 1, "unix.Sigset") : 0);
|
||||||
return SysretErrno(L, "sigsuspend", olderr);
|
return LuaUnixSysretErrno(L, "sigsuspend", olderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.setitimer(which[, intervalsec, intns, valuesec, valuens])
|
// unix.setitimer(which[, intervalsec, intns, valuesec, valuens])
|
||||||
|
@ -1727,7 +1724,7 @@ static int LuaUnixSetitimer(lua_State *L) {
|
||||||
lua_pushinteger(L, oldit.it_value.tv_usec * 1000);
|
lua_pushinteger(L, oldit.it_value.tv_usec * 1000);
|
||||||
return 4;
|
return 4;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "setitimer", olderr);
|
return LuaUnixSysretErrno(L, "setitimer", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1785,7 +1782,7 @@ static dontinline int LuaUnixTime(lua_State *L, const char *call,
|
||||||
lua_pushstring(L, tm.tm_zone);
|
lua_pushstring(L, tm.tm_zone);
|
||||||
return 11;
|
return 11;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, call, olderr);
|
return LuaUnixSysretErrno(L, call, olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1883,7 +1880,7 @@ static int LuaUnixTiocgwinsz(lua_State *L) {
|
||||||
lua_pushinteger(L, ws.ws_col);
|
lua_pushinteger(L, ws.ws_col);
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "tiocgwinsz", olderr);
|
return LuaUnixSysretErrno(L, "tiocgwinsz", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2459,7 +2456,7 @@ static int LuaUnixDirFd(lua_State *L) {
|
||||||
lua_pushinteger(L, fd);
|
lua_pushinteger(L, fd);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "dirfd", olderr);
|
return LuaUnixSysretErrno(L, "dirfd", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2494,7 +2491,7 @@ static int LuaUnixOpendir(lua_State *L) {
|
||||||
if ((dir = opendir(luaL_checkstring(L, 1)))) {
|
if ((dir = opendir(luaL_checkstring(L, 1)))) {
|
||||||
return ReturnDir(L, dir);
|
return ReturnDir(L, dir);
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "opendir", olderr);
|
return LuaUnixSysretErrno(L, "opendir", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2507,7 +2504,7 @@ static int LuaUnixFdopendir(lua_State *L) {
|
||||||
if ((dir = fdopendir(luaL_checkinteger(L, 1)))) {
|
if ((dir = fdopendir(luaL_checkinteger(L, 1)))) {
|
||||||
return ReturnDir(L, dir);
|
return ReturnDir(L, dir);
|
||||||
} else {
|
} else {
|
||||||
return SysretErrno(L, "fdopendir", olderr);
|
return LuaUnixSysretErrno(L, "fdopendir", olderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
third_party/lua/lunix.h
vendored
7
third_party/lua/lunix.h
vendored
|
@ -4,7 +4,14 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
struct UnixErrno {
|
||||||
|
int errno_;
|
||||||
|
int winerr;
|
||||||
|
const char *call;
|
||||||
|
};
|
||||||
|
|
||||||
int LuaUnix(lua_State *);
|
int LuaUnix(lua_State *);
|
||||||
|
int LuaUnixSysretErrno(lua_State *, const char *, int);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -214,8 +214,11 @@ int GetPollMaxFds(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizeFileDescriptors(void) {
|
void NormalizeFileDescriptors(void) {
|
||||||
int i, n, fd;
|
int e, i, n, fd;
|
||||||
n = GetPollMaxFds();
|
n = GetPollMaxFds();
|
||||||
|
e = errno;
|
||||||
|
closefrom(3); // more secure if linux 5.9+
|
||||||
|
errno = e;
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
pfds[i].fd = i;
|
pfds[i].fd = i;
|
||||||
pfds[i].events = POLLIN;
|
pfds[i].events = POLLIN;
|
||||||
|
|
|
@ -1959,6 +1959,96 @@ RE MODULE
|
||||||
and re.Regex:search.
|
and re.Regex:search.
|
||||||
|
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
|
PATH MODULE
|
||||||
|
|
||||||
|
The path module may be used to manipulate unix paths.
|
||||||
|
|
||||||
|
Note that we use unix paths on Windows. For example, if you have a
|
||||||
|
path like C:\foo\bar then it should be /c/foo/bar with redbean. It
|
||||||
|
should also be noted the unix module is more permissive when using
|
||||||
|
Windows paths, where translation to win32 is very light.
|
||||||
|
|
||||||
|
path.dirname(str)
|
||||||
|
└─→ str
|
||||||
|
|
||||||
|
Strips final component of path, e.g.
|
||||||
|
|
||||||
|
path │ dirname
|
||||||
|
───────────────────
|
||||||
|
. │ .
|
||||||
|
.. │ .
|
||||||
|
/ │ /
|
||||||
|
usr │ .
|
||||||
|
/usr/ │ /
|
||||||
|
/usr/lib │ /usr
|
||||||
|
/usr/lib/ │ /usr
|
||||||
|
|
||||||
|
path.basename(path:str)
|
||||||
|
└─→ str
|
||||||
|
|
||||||
|
Returns final component of path, e.g.
|
||||||
|
|
||||||
|
path │ basename
|
||||||
|
─────────────────────
|
||||||
|
. │ .
|
||||||
|
.. │ ..
|
||||||
|
/ │ /
|
||||||
|
usr │ usr
|
||||||
|
/usr/ │ usr
|
||||||
|
/usr/lib │ lib
|
||||||
|
/usr/lib/ │ lib
|
||||||
|
|
||||||
|
path.join(str, ...)
|
||||||
|
└─→ str
|
||||||
|
|
||||||
|
Concatenates path components, e.g.
|
||||||
|
|
||||||
|
x │ y │ joined
|
||||||
|
─────────────────────────────────
|
||||||
|
/ │ / │ /
|
||||||
|
/usr │ lib │ /usr/lib
|
||||||
|
/usr/ │ lib │ /usr/lib
|
||||||
|
/usr/lib │ /lib │ /lib
|
||||||
|
|
||||||
|
You may specify 1+ arguments.
|
||||||
|
|
||||||
|
Specifying no arguments will raise an error. If nil arguments are
|
||||||
|
specified, then they're skipped over. If exclusively nil arguments
|
||||||
|
are passed, then nil is returned. Empty strings behave similarly to
|
||||||
|
nil, but unlike nil may coerce a trailing slash.
|
||||||
|
|
||||||
|
path.exists(path:str)
|
||||||
|
└─→ bool
|
||||||
|
|
||||||
|
Returns true if path exists.
|
||||||
|
|
||||||
|
This function is inclusive of regular files, directories, and
|
||||||
|
special files. Symbolic links are followed are resolved. On error,
|
||||||
|
false is returned.
|
||||||
|
|
||||||
|
path.isfile(path:str)
|
||||||
|
└─→ bool
|
||||||
|
|
||||||
|
Returns true if path exists and is regular file.
|
||||||
|
|
||||||
|
Symbolic links are not followed. On error, false is returned.
|
||||||
|
|
||||||
|
path.isdir(path:str)
|
||||||
|
└─→ bool
|
||||||
|
|
||||||
|
Returns true if path exists and is directory.
|
||||||
|
|
||||||
|
Symbolic links are not followed. On error, false is returned.
|
||||||
|
|
||||||
|
path.islink(path:str)
|
||||||
|
└─→ bool
|
||||||
|
|
||||||
|
Returns true if path exists and is symbolic link.
|
||||||
|
|
||||||
|
Symbolic links are not followed. On error, false is returned.
|
||||||
|
|
||||||
|
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
MAXMIND MODULE
|
MAXMIND MODULE
|
||||||
|
|
||||||
|
@ -2220,12 +2310,36 @@ UNIX MODULE
|
||||||
|
|
||||||
Closes file descriptor.
|
Closes file descriptor.
|
||||||
|
|
||||||
|
This function should never be called twice for the same file
|
||||||
|
descriptor, regardless of whether or not an error happened. The file
|
||||||
|
descriptor is always gone after close is called. So it technically
|
||||||
|
always succeeds, but that doesn't mean an error should be ignored.
|
||||||
|
For example, on NFS a close failure could indicate data loss.
|
||||||
|
|
||||||
|
Closing does not mean that scheduled i/o operations have been
|
||||||
|
completed. You'd need to use fsync() or fdatasync() beforehand to
|
||||||
|
ensure that. You shouldn't need to do that normally, because our
|
||||||
|
close implementation guarantees a consistent view, since on systems
|
||||||
|
where it isn't guaranteed (like Windows) close will implicitly sync.
|
||||||
|
|
||||||
|
File descriptors are automatically closed on exit().
|
||||||
|
|
||||||
|
Returns `EBADF` if `fd` wasn't valid.
|
||||||
|
|
||||||
|
Returns `EINTR` possibly maybe.
|
||||||
|
|
||||||
|
Returns `EIO` if an i/o error occurred.
|
||||||
|
|
||||||
unix.read(fd:int[, bufsiz:str[, offset:int]])
|
unix.read(fd:int[, bufsiz:str[, offset:int]])
|
||||||
├─→ data:str
|
├─→ data:str
|
||||||
└─→ nil, unix.Errno
|
└─→ nil, unix.Errno
|
||||||
|
|
||||||
Reads from file descriptor.
|
Reads from file descriptor.
|
||||||
|
|
||||||
|
This function returns empty string on end of file. The exception is
|
||||||
|
if `bufsiz` is zero, in which case an empty returned string means
|
||||||
|
the file descriptor works.
|
||||||
|
|
||||||
unix.write(fd:int, data:str[, offset:int])
|
unix.write(fd:int, data:str[, offset:int])
|
||||||
├─→ wrotebytes:int
|
├─→ wrotebytes:int
|
||||||
└─→ nil, unix.Errno
|
└─→ nil, unix.Errno
|
||||||
|
@ -3778,14 +3892,66 @@ UNIX MODULE
|
||||||
├─→ true
|
├─→ true
|
||||||
└─→ nil, unix.Errno
|
└─→ nil, unix.Errno
|
||||||
|
|
||||||
Unveil parts of a restricted filesystem view, e.g.
|
Restricts filesystem operations, e.g.
|
||||||
|
|
||||||
unix.unveil(".", "r")
|
unix.unveil(".", "r"); -- current dir + children visible
|
||||||
unix.unveil(nil, nil)
|
unix.unveil("/etc", "r"); -- make /etc readable too
|
||||||
|
unix.unveil(0, 0); -- commit and lock policy
|
||||||
|
|
||||||
This can be used for sandboxing file system access.
|
Unveiling restricts a thread's view of the filesystem to a set of
|
||||||
|
allowed paths with specific privileges.
|
||||||
|
|
||||||
Unveil support is a work in progress.
|
Once you start using unveil(), the entire file system is considered
|
||||||
|
hidden. You then specify, by repeatedly calling unveil(), which paths
|
||||||
|
should become unhidden. When you're finished, you call `unveil(0,0)`
|
||||||
|
which commits your policy, after which further use is forbidden, in
|
||||||
|
the current thread, as well as any threads or processes it spawns.
|
||||||
|
|
||||||
|
There are some differences between unveil() on Linux versus OpenBSD.
|
||||||
|
|
||||||
|
1. Build your policy and lock it in one go. On OpenBSD, policies take
|
||||||
|
effect immediately and may evolve as you continue to call unveil()
|
||||||
|
but only in a more restrictive direction. On Linux, nothing will
|
||||||
|
happen until you call `unveil(0,0)` which commits and locks.
|
||||||
|
|
||||||
|
2. Try not to overlap directory trees. On OpenBSD, if directory trees
|
||||||
|
overlap, then the most restrictive policy will be used for a given
|
||||||
|
file. On Linux overlapping may result in a less restrictive policy
|
||||||
|
and possibly even undefined behavior.
|
||||||
|
|
||||||
|
3. OpenBSD and Linux disagree on error codes. On OpenBSD, accessing
|
||||||
|
paths outside of the allowed set raises ENOENT, and accessing ones
|
||||||
|
with incorrect permissions raises EACCES. On Linux, both these
|
||||||
|
cases raise EACCES.
|
||||||
|
|
||||||
|
4. Unlike OpenBSD, Linux does nothing to conceal the existence of
|
||||||
|
paths. Even with an unveil() policy in place, it's still possible
|
||||||
|
to access the metadata of all files using functions like stat()
|
||||||
|
and open(O_PATH), provided you know the path. A sandboxed process
|
||||||
|
can always, for example, determine how many bytes of data are in
|
||||||
|
/etc/passwd, even if the file isn't readable. But it's still not
|
||||||
|
possible to use opendir() and go fishing for paths which weren't
|
||||||
|
previously known.
|
||||||
|
|
||||||
|
This system call is supported natively on OpenBSD and polyfilled on
|
||||||
|
Linux using the Landlock LSM[1].
|
||||||
|
|
||||||
|
`path` is the file or directory to unveil
|
||||||
|
|
||||||
|
`permissions` is a string consisting of zero or more of the
|
||||||
|
following characters:
|
||||||
|
|
||||||
|
- 'r' makes `path` available for read-only path operations,
|
||||||
|
corresponding to the pledge promise "rpath".
|
||||||
|
|
||||||
|
- `w` makes `path` available for write operations, corresponding
|
||||||
|
to the pledge promise "wpath".
|
||||||
|
|
||||||
|
- `x` makes `path` available for execute operations,
|
||||||
|
corresponding to the pledge promises "exec" and "execnative".
|
||||||
|
|
||||||
|
- `c` allows `path` to be created and removed, corresponding to
|
||||||
|
the pledge promise "cpath".
|
||||||
|
|
||||||
unix.gmtime(unixts:int)
|
unix.gmtime(unixts:int)
|
||||||
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
||||||
|
|
166
tool/net/lpath.c
Normal file
166
tool/net/lpath.c
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/struct/stat.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/sysv/consts/at.h"
|
||||||
|
#include "libc/sysv/consts/s.h"
|
||||||
|
#include "third_party/lua/lauxlib.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview redbean unix path manipulation module
|
||||||
|
*/
|
||||||
|
|
||||||
|
// path.basename(str)
|
||||||
|
// └─→ str
|
||||||
|
static int LuaPathBasename(lua_State *L) {
|
||||||
|
size_t i, n;
|
||||||
|
const char *p;
|
||||||
|
if ((p = luaL_optlstring(L, 1, 0, &n)) && n) {
|
||||||
|
while (n > 1 && p[n - 1] == '/') --n;
|
||||||
|
i = n - 1;
|
||||||
|
while (i && p[i - 1] != '/') --i;
|
||||||
|
lua_pushlstring(L, p + i, n - i);
|
||||||
|
} else {
|
||||||
|
lua_pushlstring(L, ".", 1);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.dirname(str)
|
||||||
|
// └─→ str
|
||||||
|
static int LuaPathDirname(lua_State *L) {
|
||||||
|
size_t n;
|
||||||
|
const char *p;
|
||||||
|
if ((p = luaL_optlstring(L, 1, 0, &n)) && n--) {
|
||||||
|
for (; p[n] == '/'; n--) {
|
||||||
|
if (!n) goto ReturnSlash;
|
||||||
|
}
|
||||||
|
for (; p[n] != '/'; n--) {
|
||||||
|
if (!n) goto ReturnDot;
|
||||||
|
}
|
||||||
|
for (; p[n] == '/'; n--) {
|
||||||
|
if (!n) goto ReturnSlash;
|
||||||
|
}
|
||||||
|
lua_pushlstring(L, p, n + 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ReturnDot:
|
||||||
|
lua_pushlstring(L, ".", 1);
|
||||||
|
return 1;
|
||||||
|
ReturnSlash:
|
||||||
|
lua_pushlstring(L, "/", 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.join(str, ...)
|
||||||
|
// └─→ str
|
||||||
|
static int LuaPathJoin(lua_State *L) {
|
||||||
|
int i, n;
|
||||||
|
size_t z;
|
||||||
|
bool gotstr;
|
||||||
|
const char *c;
|
||||||
|
luaL_Buffer b;
|
||||||
|
bool needslash;
|
||||||
|
if ((n = lua_gettop(L))) {
|
||||||
|
luaL_buffinit(L, &b);
|
||||||
|
gotstr = false;
|
||||||
|
needslash = false;
|
||||||
|
for (i = 1; i <= n; ++i) {
|
||||||
|
if (lua_isnoneornil(L, i)) continue;
|
||||||
|
gotstr = true;
|
||||||
|
c = luaL_checklstring(L, i, &z);
|
||||||
|
if (z) {
|
||||||
|
if (c[0] == '/') {
|
||||||
|
luaL_buffsub(&b, luaL_bufflen(&b));
|
||||||
|
} else if (needslash) {
|
||||||
|
luaL_addchar(&b, '/');
|
||||||
|
}
|
||||||
|
luaL_addlstring(&b, c, z);
|
||||||
|
needslash = c[z - 1] != '/';
|
||||||
|
} else if (needslash) {
|
||||||
|
luaL_addchar(&b, '/');
|
||||||
|
needslash = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gotstr) {
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
luaL_error(L, "missing argument");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CheckPath(lua_State *L, int type, int flags) {
|
||||||
|
int olderr;
|
||||||
|
struct stat st;
|
||||||
|
const char *path;
|
||||||
|
path = luaL_checkstring(L, 1);
|
||||||
|
olderr = errno;
|
||||||
|
if (fstatat(AT_FDCWD, path, &st, flags) != -1) {
|
||||||
|
lua_pushboolean(L, !type || (st.st_mode & S_IFMT) == type);
|
||||||
|
} else {
|
||||||
|
errno = olderr;
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.exists(str)
|
||||||
|
// └─→ bool
|
||||||
|
static int LuaPathExists(lua_State *L) {
|
||||||
|
return CheckPath(L, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.isfile(str)
|
||||||
|
// └─→ bool
|
||||||
|
static int LuaPathIsfile(lua_State *L) {
|
||||||
|
return CheckPath(L, S_IFREG, AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.islink(str)
|
||||||
|
// └─→ bool
|
||||||
|
static int LuaPathIslink(lua_State *L) {
|
||||||
|
return CheckPath(L, S_IFLNK, AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.isdir(str)
|
||||||
|
// └─→ bool
|
||||||
|
static int LuaPathIsdir(lua_State *L) {
|
||||||
|
return CheckPath(L, S_IFDIR, AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg kLuaPath[] = {
|
||||||
|
{"basename", LuaPathBasename}, //
|
||||||
|
{"dirname", LuaPathDirname}, //
|
||||||
|
{"exists", LuaPathExists}, //
|
||||||
|
{"isdir", LuaPathIsdir}, //
|
||||||
|
{"isfile", LuaPathIsfile}, //
|
||||||
|
{"islink", LuaPathIslink}, //
|
||||||
|
{"join", LuaPathJoin}, //
|
||||||
|
{0}, //
|
||||||
|
};
|
||||||
|
|
||||||
|
int LuaPath(lua_State *L) {
|
||||||
|
luaL_newlib(L, kLuaPath);
|
||||||
|
return 1;
|
||||||
|
}
|
11
tool/net/lpath.h
Normal file
11
tool/net/lpath.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef COSMOPOLITAN_TOOL_NET_LPATH_H_
|
||||||
|
#define COSMOPOLITAN_TOOL_NET_LPATH_H_
|
||||||
|
#include "third_party/lua/lauxlib.h"
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
int LuaPath(lua_State *);
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_TOOL_NET_LPATH_H_ */
|
|
@ -96,6 +96,7 @@ o/$(MODE)/tool/net/redbean.com.dbg: \
|
||||||
$(TOOL_NET_DEPS) \
|
$(TOOL_NET_DEPS) \
|
||||||
o/$(MODE)/tool/net/redbean.o \
|
o/$(MODE)/tool/net/redbean.o \
|
||||||
o/$(MODE)/tool/net/lfuncs.o \
|
o/$(MODE)/tool/net/lfuncs.o \
|
||||||
|
o/$(MODE)/tool/net/lpath.o \
|
||||||
o/$(MODE)/tool/net/lfinger.o \
|
o/$(MODE)/tool/net/lfinger.o \
|
||||||
o/$(MODE)/tool/net/lre.o \
|
o/$(MODE)/tool/net/lre.o \
|
||||||
o/$(MODE)/tool/net/ljson.o \
|
o/$(MODE)/tool/net/ljson.o \
|
||||||
|
@ -217,6 +218,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
||||||
$(TOOL_NET_DEPS) \
|
$(TOOL_NET_DEPS) \
|
||||||
o/$(MODE)/tool/net/redbean.o \
|
o/$(MODE)/tool/net/redbean.o \
|
||||||
o/$(MODE)/tool/net/lfuncs.o \
|
o/$(MODE)/tool/net/lfuncs.o \
|
||||||
|
o/$(MODE)/tool/net/lpath.o \
|
||||||
o/$(MODE)/tool/net/lfinger.o \
|
o/$(MODE)/tool/net/lfinger.o \
|
||||||
o/$(MODE)/tool/net/lre.o \
|
o/$(MODE)/tool/net/lre.o \
|
||||||
o/$(MODE)/tool/net/ljson.o \
|
o/$(MODE)/tool/net/ljson.o \
|
||||||
|
@ -336,6 +338,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \
|
||||||
$(TOOL_NET_DEPS) \
|
$(TOOL_NET_DEPS) \
|
||||||
o/$(MODE)/tool/net/redbean-unsecure.o \
|
o/$(MODE)/tool/net/redbean-unsecure.o \
|
||||||
o/$(MODE)/tool/net/lfuncs.o \
|
o/$(MODE)/tool/net/lfuncs.o \
|
||||||
|
o/$(MODE)/tool/net/lpath.o \
|
||||||
o/$(MODE)/tool/net/lfinger.o \
|
o/$(MODE)/tool/net/lfinger.o \
|
||||||
o/$(MODE)/tool/net/lre.o \
|
o/$(MODE)/tool/net/lre.o \
|
||||||
o/$(MODE)/tool/net/ljson.o \
|
o/$(MODE)/tool/net/ljson.o \
|
||||||
|
|
|
@ -115,6 +115,7 @@
|
||||||
#include "tool/net/lfinger.h"
|
#include "tool/net/lfinger.h"
|
||||||
#include "tool/net/lfuncs.h"
|
#include "tool/net/lfuncs.h"
|
||||||
#include "tool/net/ljson.h"
|
#include "tool/net/ljson.h"
|
||||||
|
#include "tool/net/lpath.h"
|
||||||
#include "tool/net/luacheck.h"
|
#include "tool/net/luacheck.h"
|
||||||
#include "tool/net/sandbox.h"
|
#include "tool/net/sandbox.h"
|
||||||
|
|
||||||
|
@ -5272,6 +5273,7 @@ static const luaL_Reg kLuaLibs[] = {
|
||||||
{"lsqlite3", luaopen_lsqlite3}, //
|
{"lsqlite3", luaopen_lsqlite3}, //
|
||||||
{"maxmind", LuaMaxmind}, //
|
{"maxmind", LuaMaxmind}, //
|
||||||
{"finger", LuaFinger}, //
|
{"finger", LuaFinger}, //
|
||||||
|
{"path", LuaPath}, //
|
||||||
{"re", LuaRe}, //
|
{"re", LuaRe}, //
|
||||||
{"unix", LuaUnix}, //
|
{"unix", LuaUnix}, //
|
||||||
};
|
};
|
||||||
|
@ -6323,8 +6325,7 @@ static bool HandleMessageActual(void) {
|
||||||
if (hasonloglatency) LuaOnLogLatency(reqtime, contime);
|
if (hasonloglatency) LuaOnLogLatency(reqtime, contime);
|
||||||
if (loglatency || LOGGABLE(kLogDebug))
|
if (loglatency || LOGGABLE(kLogDebug))
|
||||||
LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs",
|
LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs",
|
||||||
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a,
|
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, reqtime, contime);
|
||||||
reqtime, contime);
|
|
||||||
}
|
}
|
||||||
if (!generator) {
|
if (!generator) {
|
||||||
return TransmitResponse(p);
|
return TransmitResponse(p);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue