mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-12 05:59:10 +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
|
@ -214,8 +214,11 @@ int GetPollMaxFds(void) {
|
|||
}
|
||||
|
||||
void NormalizeFileDescriptors(void) {
|
||||
int i, n, fd;
|
||||
int e, i, n, fd;
|
||||
n = GetPollMaxFds();
|
||||
e = errno;
|
||||
closefrom(3); // more secure if linux 5.9+
|
||||
errno = e;
|
||||
for (i = 0; i < n; ++i) {
|
||||
pfds[i].fd = i;
|
||||
pfds[i].events = POLLIN;
|
||||
|
|
|
@ -1959,6 +1959,96 @@ RE MODULE
|
|||
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
|
||||
|
||||
|
@ -2220,12 +2310,36 @@ UNIX MODULE
|
|||
|
||||
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]])
|
||||
├─→ data:str
|
||||
└─→ nil, unix.Errno
|
||||
|
||||
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])
|
||||
├─→ wrotebytes:int
|
||||
└─→ nil, unix.Errno
|
||||
|
@ -3778,14 +3892,66 @@ UNIX MODULE
|
|||
├─→ true
|
||||
└─→ nil, unix.Errno
|
||||
|
||||
Unveil parts of a restricted filesystem view, e.g.
|
||||
Restricts filesystem operations, e.g.
|
||||
|
||||
unix.unveil(".", "r")
|
||||
unix.unveil(nil, nil)
|
||||
unix.unveil(".", "r"); -- current dir + children visible
|
||||
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)
|
||||
├─→ 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) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lpath.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
|
@ -217,6 +218,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
|||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lpath.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
|
@ -336,6 +338,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \
|
|||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean-unsecure.o \
|
||||
o/$(MODE)/tool/net/lfuncs.o \
|
||||
o/$(MODE)/tool/net/lpath.o \
|
||||
o/$(MODE)/tool/net/lfinger.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/ljson.o \
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
#include "tool/net/lfinger.h"
|
||||
#include "tool/net/lfuncs.h"
|
||||
#include "tool/net/ljson.h"
|
||||
#include "tool/net/lpath.h"
|
||||
#include "tool/net/luacheck.h"
|
||||
#include "tool/net/sandbox.h"
|
||||
|
||||
|
@ -5272,6 +5273,7 @@ static const luaL_Reg kLuaLibs[] = {
|
|||
{"lsqlite3", luaopen_lsqlite3}, //
|
||||
{"maxmind", LuaMaxmind}, //
|
||||
{"finger", LuaFinger}, //
|
||||
{"path", LuaPath}, //
|
||||
{"re", LuaRe}, //
|
||||
{"unix", LuaUnix}, //
|
||||
};
|
||||
|
@ -6323,8 +6325,7 @@ static bool HandleMessageActual(void) {
|
|||
if (hasonloglatency) LuaOnLogLatency(reqtime, contime);
|
||||
if (loglatency || LOGGABLE(kLogDebug))
|
||||
LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs",
|
||||
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a,
|
||||
reqtime, contime);
|
||||
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, reqtime, contime);
|
||||
}
|
||||
if (!generator) {
|
||||
return TransmitResponse(p);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue