Improve zip read-only filesystem

readdir() will now always yield an inode that's consistent with stat()
on ZipOS and Windows in general. More APIs have been updated to return
the appropriate error code when inappropriately trying to do ops, like
sockets, with a zip file descriptor. The path normalization algorithms
are now fully fleshed out. Some socket APIs have been fixed so they'll
raise EBADF vs. ENOTSOCK appropriately. Lastly seekdir() will now work
properly on NetBSD and FreeBSD (not sure why anyone would even use it)
This commit is contained in:
Justine Tunney 2023-08-16 15:53:06 -07:00
parent dc6c67256f
commit b76b2be2d0
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
47 changed files with 644 additions and 269 deletions

View file

@ -84,7 +84,6 @@ static void copy_file_range_init(void) {
* @raise EXDEV if source and destination are on different filesystems
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ENOTSUP if `infd` or `outfd` is a zip file descriptor
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
@ -114,8 +113,10 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
(opt_in_out_outoffset &&
!__asan_is_valid(opt_in_out_outoffset, 8)))) {
rc = efault();
} else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) {
rc = enotsup();
} else if (__isfdkind(outfd, kFdZip)) {
rc = ebadf();
} else if (__isfdkind(infd, kFdZip)) {
rc = exdev();
} else {
rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd,
opt_in_out_outoffset, uptobytes, flags);

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
#include "libc/dce.h"
@ -28,7 +29,9 @@
/**
* Returns information about filesystem.
*
* @return 0 on success, or -1 w/ errno
* @raise ENOTSUP if /zip path
* @cancellationpoint
*/
int fstatfs(int fd, struct statfs *sf) {
@ -37,7 +40,9 @@ int fstatfs(int fd, struct statfs *sf) {
BEGIN_CANCELLATION_POINT;
CheckLargeStackAllocation(&m, sizeof(m));
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsup();
} else if (!IsWindows()) {
if ((rc = sys_fstatfs(fd, &m)) != -1) {
statfs2cosmo(sf, &m);
}

View file

@ -29,8 +29,8 @@
#include "libc/intrin/likely.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
int e, i;

View file

@ -20,6 +20,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -59,6 +60,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
rc = ebadf();
} else if (IsAsan() && !__asan_is_valid(buf, size)) {
rc = efault();
} else if (__isfdkind(fd, kFdZip)) {
rc = ebadf();
} else if (!IsWindows()) {
rc = sys_pwrite(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdFile)) {

View file

@ -30,7 +30,6 @@
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
int64_t off) {
@ -51,8 +50,7 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
}
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return _weaken(__zipos_write)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off);
return ebadf();
}
if (IsWindows()) {

View file

@ -18,29 +18,39 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Returns information about filesystem.
*
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @raise ENOTSUP if /zip path
* @cancellationpoint
*/
int statfs(const char *path, struct statfs *sf) {
int rc;
union statfs_meta m;
struct ZiposUri zipname;
BEGIN_CANCELLATION_POINT;
CheckLargeStackAllocation(&m, sizeof(m));
if (!IsWindows()) {
if (_weaken(__zipos_parseuri) &&
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
rc = enotsup();
} else if (!IsWindows()) {
if ((rc = sys_statfs(path, &m)) != -1) {
statfs2cosmo(sf, &m);
}

View file

@ -17,7 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Syncs filesystem associated with file descriptor.
@ -30,8 +32,13 @@
* @raise ENOSPC if disk space was exhausted during sync
* @raise EDQUOT (or ENOSPC) on some kinds of NFS errors
* @raise EBADF if `fd` isn't a valid file descriptor
* @raise EROFS if `fd` is a zip file
* @raise ENOSYS on non-Linux
*/
int syncfs(int fd) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return erofs();
} else {
return sys_syncfs(fd);
}
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/termios.h"
@ -54,7 +55,9 @@ static dontinline textwindows int sys_tcdrain_nt(int fd) {
int tcdrain(int fd) {
int rc;
BEGIN_CANCELLATION_POINT;
if (IsLinux()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsLinux()) {
rc = sys_ioctl_cp(fd, TCSBRK, (uintptr_t)1);
} else if (IsBsd()) {
rc = sys_ioctl_cp(fd, TIOCDRAIN, 0);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/metatermios.internal.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -127,7 +128,9 @@ static dontinline textwindows int sys_tcflow_nt(int fd, int action) {
*/
int tcflow(int fd, int action) {
int rc;
if (IsLinux()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsLinux()) {
rc = sys_ioctl(fd, TCXONC, action);
} else if (IsBsd()) {
rc = sys_tcflow_bsd(fd, action);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#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/calls/syscall_support-nt.internal.h"
@ -76,7 +77,9 @@ static dontinline textwindows int sys_tcflush_nt(int fd, int queue) {
*/
int tcflush(int fd, int queue) {
int rc;
if (IsLinux()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsLinux()) {
rc = sys_ioctl(fd, TCFLSH, queue);
} else if (IsBsd()) {
rc = sys_ioctl(fd, TIOCFLUSH, &queue);

View file

@ -16,10 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/termios.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
#define TIOCGSID (IsLinux() ? 0x5429 : 0x40047463)
@ -28,7 +30,11 @@
*/
int tcgetsid(int fd) {
int rc, sid;
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else {
rc = sys_ioctl(fd, TIOCGSID, &sid);
}
STRACE("tcgetsid(%d) → %d% m", fd, rc);
return rc != -1 ? sid : -1;
}

View file

@ -57,7 +57,9 @@ static textwindows int sys_tcsendbreak_nt(int fd) {
*/
int tcsendbreak(int fd, int duration) {
int rc;
if (IsLinux()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsLinux()) {
rc = sys_ioctl(fd, TCSBRK, 0);
} else if (IsBsd()) {
rc = sys_tcsendbreak_bsd(fd);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/termios.h"
#include "libc/dce.h"
@ -39,7 +40,9 @@
*/
int tcsetpgrp(int fd, int pgrp) {
int rc;
if (IsWindows() || IsMetal()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsWindows() || IsMetal()) {
rc = enosys();
} else {
rc = sys_ioctl(fd, TIOCSPGRP, &pgrp);

View file

@ -25,9 +25,9 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/sock.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Writes data to file descriptor.
@ -71,8 +71,8 @@ ssize_t write(int fd, const void *buf, size_t size) {
if (fd >= 0) {
if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsup();
} else if (__isfdkind(fd, kFdZip)) {
rc = ebadf();
} else if (!IsWindows() && !IsMetal()) {
rc = sys_write(fd, buf, size);
} else if (fd >= g_fds.n) {

View file

@ -30,7 +30,6 @@
#include "libc/intrin/weaken.h"
#include "libc/sock/internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Writes data from multiple buffers.
@ -59,8 +58,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_write)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1);
rc = ebadf();
} else if (!IsWindows() && !IsMetal()) {
if (iovlen == 1) {
rc = sys_write(fd, iov[0].iov_base, iov[0].iov_len);

View file

@ -72,7 +72,6 @@ _start:
// make process stack (8mb) follow thread stack (256kb) alignment
and $-(256*1024),%rsp
// bofram 9f
#if SupportsWindows()
// make win32 imps noop

View file

@ -104,7 +104,6 @@ __msabi static inline char16_t *MyCommandLine(void) {
// this ensures close(1) won't accidentally close(2) for example
__msabi static textwindows void DeduplicateStdioHandles(void) {
int64_t h1, h2, h3, proc;
for (long i = 0; i < 3; ++i) {
int64_t h1 = __imp_GetStdHandle(kNtConsoleHandles[i]);
for (long j = i + 1; j < 3; ++j) {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/stat.h"
#include "libc/runtime/zipos.internal.h"
/**
@ -25,5 +26,7 @@
* @asyncsignalsafe
*/
int __zipos_fstat(struct ZiposHandle *h, struct stat *st) {
return __zipos_stat_impl(h->zipos, h->cfile, st);
if (__zipos_stat_impl(h->zipos, h->cfile, st)) return -1;
st->st_ino = __zipos_inode(h->zipos, h->cfile, h->data, h->size);
return 0;
}

View file

@ -0,0 +1,42 @@
/*-*- 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 2023 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/limits.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/zip.internal.h"
static uint64_t __zipos_fnv(const char *s, int len) {
uint64_t hash = 0xcbf29ce484222325;
for (int i = 0; i < len; i++) {
hash *= 0x100000001b3;
hash ^= (unsigned char)s[i];
}
return hash;
}
uint64_t __zipos_inode(struct Zipos *zipos, int64_t cfile, //
const void *name, size_t namelen) {
unassert(cfile >= 0);
if (cfile == ZIPOS_SYNTHETIC_DIRECTORY) {
if (namelen && ((char *)name)[namelen - 1] == '/') --namelen;
cfile = INT64_MIN | __zipos_fnv(name, namelen);
}
return cfile;
}

View file

@ -18,63 +18,45 @@
*/
#include "libc/runtime/zipos.internal.h"
static size_t __zipos_trimpath(char *s, int *isabs) {
char *p = s, *q = s;
for (; *q; ++q) {
if (*q == '/') {
while (q[1] == '/') ++q;
if (q[1] == '.' && (q[2] == '/' || q[2] == '\0')) {
++q;
// normalizes zip filesystem path w/ overlapping strlcpy() style api
// zip paths look like relative paths, but they're actually absolute
// with respect to the archive; so similar to how /../etc would mean
// /etc, we'd translate that here to "etc". when storing assets in a
// zip archive, callers should append trailing slash for directories
// returns strlen of 𝑑; returns 𝑛 when insufficient buffer available
// nul terminator is guaranteed if n>0. it's fine if 𝑑 and 𝑠 overlap
// test vectors for this algorithm in: test/libc/stdio/zipdir_test.c
size_t __zipos_normpath(char *d, const char *s, size_t n) {
char *p, *e;
for (p = d, e = d + n; p < e && *s; ++s) {
if ((p == d || p[-1] == '/') && *s == '/') {
// matched "^/" or "//"
} else if ((p == d || p[-1] == '/') && //
s[0] == '.' && //
(!s[1] || s[1] == '/')) {
// matched "/./" or "^.$" or "^./" or "/.$"
s += !!s[1];
} else if ((p == d || p[-1] == '/') && //
s[0] == '.' && //
s[1] == '.' && //
(!s[2] || s[2] == '/')) {
// matched "/../" or "^..$" or "^../" or "/..$"
while (p > d && p[-1] == '/') --p;
while (p > d && p[-1] != '/') --p;
} else {
*p++ = '/';
*p++ = *s;
}
}
// if we didn't overflow
if (p < e) {
// trim trailing slashes and add nul terminator
while (p > d && p[-1] == '/') --p;
*p = '\0';
} else {
*p++ = *q;
// force nul-terminator to exist if possible
if (p > d) {
p[-1] = '\0';
}
}
if (s < p && p[-1] == '.' && p[-2] == '.' && (p - 2 == s || p[-3] == '/')) {
*p++ = '/';
}
*p = '\0';
if (isabs) {
*isabs = *s == '/';
}
return p - s;
}
size_t __zipos_normpath(char *s) {
int isabs;
char *p = s, *q = s;
__zipos_trimpath(s, &isabs);
if (!*s) return 0;
for (; *q != '\0'; ++q) {
if (q[0] == '/' && q[1] == '.' && q[2] == '.' &&
(q[3] == '/' || q[3] == '\0')) {
char *ep = p;
while (s < ep && *--ep != '/') donothing;
if (ep != p &&
(p[-1] != '.' || p[-2] != '.' || (s < p - 3 && p[-3] != '/'))) {
p = ep;
q += 2;
continue;
} else if (ep == s && isabs) {
q += 2;
continue;
}
}
if (q[0] != '/' || p != s || isabs) {
*p++ = *q;
}
}
if (p == s) {
*p++ = isabs ? '/' : '.';
}
if (p == s + 1 && s[0] == '.') {
*p++ = '/';
}
while (p - s > 1 && p[-1] == '/') {
--p;
}
*p = '\0';
return p - s;
return p - d;
}

View file

@ -199,6 +199,11 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
static int __zipos_open_impl(struct ZiposUri *name, int flags) {
struct Zipos *zipos;
if ((flags & O_CREAT) || //
(flags & O_TRUNC) || //
(flags & O_ACCMODE) != O_RDONLY) {
return erofs();
}
if (!(zipos = __zipos_get())) {
return enoexec();
}
@ -206,19 +211,14 @@ static int __zipos_open_impl(struct ZiposUri *name, int flags) {
if ((cf = __zipos_find(zipos, name)) == -1) {
return enoent();
}
if ((flags & O_ACCMODE) != O_RDONLY || (flags & O_TRUNC)) {
return erofs();
}
if (flags & O_EXCL) {
return eexist();
}
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
int mode = GetZipCfileMode(zipos->map + cf);
#if 0
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
return enotdir();
}
#endif
if (!(mode & 0444)) {
return eacces();
}

View file

@ -29,8 +29,9 @@ ssize_t __zipos_parseuri(const char *uri, struct ZiposUri *out) {
uri[2] == 'i' && //
uri[3] == 'p' && //
(!uri[4] || uri[4] == '/')) &&
strlcpy(out->path, uri + 4 + !!uri[4], ZIPOS_PATH_MAX) < ZIPOS_PATH_MAX) {
return (out->len = __zipos_normpath(out->path));
(len = __zipos_normpath(out->path, uri + 4 + !!uri[4],
sizeof(out->path))) < sizeof(out->path)) {
return (out->len = len);
} else {
return -1;
}

View file

@ -26,7 +26,6 @@
int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
size_t lf;
bzero(st, sizeof(*st));
st->st_ino = cf;
st->st_nlink = 1;
st->st_dev = zipos->dev;
st->st_blksize = FRAMESIZE;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/stat.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
@ -30,5 +31,7 @@ int __zipos_stat(struct ZiposUri *name, struct stat *st) {
struct Zipos *zipos;
if (!(zipos = __zipos_get())) return enoexec();
if ((cf = __zipos_find(zipos, name)) == -1) return enoent();
return __zipos_stat_impl(zipos, cf, st);
if (__zipos_stat_impl(zipos, cf, st)) return -1;
st->st_ino = __zipos_inode(zipos, cf, name->path, name->len);
return 0;
}

View file

@ -41,11 +41,12 @@ struct Zipos {
int __zipos_close(int);
void __zipos_lock(void);
void __zipos_unlock(void);
size_t __zipos_normpath(char *);
struct Zipos *__zipos_get(void) pureconst;
void __zipos_free(struct ZiposHandle *);
struct Zipos *__zipos_get(void) pureconst;
size_t __zipos_normpath(char *, const char *, size_t);
ssize_t __zipos_parseuri(const char *, struct ZiposUri *);
ssize_t __zipos_find(struct Zipos *, struct ZiposUri *);
uint64_t __zipos_inode(struct Zipos *, int64_t, const void *, size_t);
int __zipos_open(struct ZiposUri *, int);
int __zipos_access(struct ZiposUri *, int);
int __zipos_stat(struct ZiposUri *, struct stat *);
@ -53,8 +54,6 @@ int __zipos_fstat(struct ZiposHandle *, struct stat *);
int __zipos_stat_impl(struct Zipos *, size_t, struct stat *);
ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);
ssize_t __zipos_write(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);
int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned);
int __zipos_fcntl(int, int, uintptr_t);
int __zipos_notat(int, const char *);

View file

@ -47,14 +47,16 @@ int accept4(int fd, struct sockaddr *opt_out_addr, uint32_t *opt_inout_addrsize,
struct sockaddr_storage ss = {0};
BEGIN_CANCELLATION_POINT;
if (IsWindows()) {
if (__isfdkind(fd, kFdSocket)) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_accept4(fd, &ss, flags);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_accept_nt(g_fds.p + fd, &ss, flags);
} else {
rc = ebadf();
}
} else {
rc = sys_accept4(fd, &ss, flags);
rc = enotsock();
}
if (rc != -1) {

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
@ -50,12 +51,16 @@ int bind(int fd, const struct sockaddr *addr, uint32_t addrsize) {
if (!addr || (IsAsan() && !__asan_is_valid(addr, addrsize))) {
rc = efault();
} else if (addrsize >= sizeof(struct sockaddr_in)) {
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_bind(fd, addr, addrsize);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_bind_nt(&g_fds.p[fd], addr, addrsize);
} else {
rc = ebadf();
rc = enotsock();
}
} else {
rc = einval();

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
@ -45,12 +46,16 @@ int connect(int fd, const struct sockaddr *addr, uint32_t addrsize) {
BEGIN_CANCELLATION_POINT;
if (addr && !(IsAsan() && !__asan_is_valid(addr, addrsize))) {
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_connect(fd, addr, addrsize);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_connect_nt(&g_fds.p[fd], addr, addrsize);
} else {
rc = ebadf();
rc = enotsock();
}
} else {
rc = efault();

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
@ -45,18 +46,22 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval,
int rc;
if (level == -1 || !optname) {
rc = enoprotoopt(); /* see libc/sysv/consts.sh */
rc = enoprotoopt(); // see libc/sysv/consts.sh
} else if (IsAsan() && (out_opt_optval && out_optlen &&
(!__asan_is_valid(out_optlen, sizeof(uint32_t)) ||
!__asan_is_valid(out_opt_optval, *out_optlen)))) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_getsockopt(fd, level, optname, out_opt_optval, out_optlen);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_getsockopt_nt(&g_fds.p[fd], level, optname, out_opt_optval,
out_optlen);
} else {
rc = ebadf();
rc = enotsock();
}
#ifdef SYSDEBUG

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
@ -37,12 +38,16 @@
*/
int listen(int fd, int backlog) {
int rc;
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_listen(fd, backlog);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_listen_nt(&g_fds.p[fd], backlog);
} else {
rc = ebadf();
rc = enotsock();
}
STRACE("listen(%d, %d) → %d% lm", fd, backlog, rc);
return rc;

View file

@ -47,6 +47,8 @@ ssize_t recv(int fd, void *buf, size_t size, int flags) {
if (IsAsan() && !__asan_is_valid(buf, size)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_recvfrom(fd, buf, size, flags, 0, 0);
} else if (__isfdopen(fd)) {

View file

@ -62,6 +62,8 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
if (IsAsan() && !__asan_is_valid(buf, size)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_recvfrom(fd, buf, size, flags, &addr, &addrsize);
} else if (__isfdopen(fd)) {

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/dce.h"
@ -54,6 +55,8 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
BEGIN_CANCELLATION_POINT;
if (IsAsan() && !__asan_is_valid_msghdr(msg)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
if (IsBsd() && msg->msg_name) {
memcpy(&msg2, msg, sizeof(msg2));

View file

@ -48,6 +48,8 @@ ssize_t send(int fd, const void *buf, size_t size, int flags) {
if (IsAsan() && !__asan_is_valid(buf, size)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_sendto(fd, buf, size, flags, 0, 0);
} else if (__isfdopen(fd)) {

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/dce.h"
@ -56,6 +57,8 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
BEGIN_CANCELLATION_POINT;
if (IsAsan() && !__asan_is_valid_msghdr(msg)) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
if (IsBsd() && msg->msg_name) {
memcpy(&msg2, msg, sizeof(msg2));

View file

@ -64,8 +64,9 @@ ssize_t sendto(int fd, const void *buf, size_t size, int flags,
if (IsAsan() && (!__asan_is_valid(buf, size) ||
(opt_addr && !__asan_is_valid(opt_addr, addrsize)))) {
rc = efault();
} else {
if (!IsWindows()) {
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
if (!IsBsd() || !opt_addr) {
rc = sys_sendto(fd, buf, size, flags, opt_addr, addrsize);
} else if (!(rc = sockaddr2bsd(opt_addr, addrsize, &bsd, &bsdaddrsize))) {
@ -73,8 +74,8 @@ ssize_t sendto(int fd, const void *buf, size_t size, int flags,
}
} else if (__isfdopen(fd)) {
if (__isfdkind(fd, kFdSocket)) {
rc = sys_sendto_nt(fd, (struct iovec[]){{buf, size}}, 1, flags,
opt_addr, addrsize);
rc = sys_sendto_nt(fd, (struct iovec[]){{buf, size}}, 1, flags, opt_addr,
addrsize);
} else if (__isfdkind(fd, kFdFile)) {
if (flags) {
rc = einval();
@ -89,7 +90,6 @@ ssize_t sendto(int fd, const void *buf, size_t size, int flags,
} else {
rc = ebadf();
}
}
END_CANCELLATION_POINT;
DATATRACE("sendto(%d, %#.*hhs%s, %'zu, %#x, %p, %u) → %'ld% lm", fd,

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
@ -61,10 +62,12 @@ int setsockopt(int fd, int level, int optname, const void *optval,
int e, rc;
if (level == -1 || !optname) {
rc = enoprotoopt(); /* see libc/sysv/consts.sh */
rc = enoprotoopt(); // see libc/sysv/consts.sh
} else if ((!optval && optlen) ||
(IsAsan() && !__asan_is_valid(optval, optlen))) {
rc = efault();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = -1;
e = errno;
@ -75,10 +78,12 @@ int setsockopt(int fd, int level, int optname, const void *optval,
break;
}
} while (setsockopt_polyfill(&optname));
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_setsockopt_nt(&g_fds.p[fd], level, optname, optval, optlen);
} else {
rc = ebadf();
rc = enotsock();
}
#ifdef SYSDEBUG

View file

@ -35,12 +35,16 @@
*/
int shutdown(int fd, int how) {
int rc;
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
rc = sys_shutdown(fd, how);
} else if (!__isfdopen(fd)) {
rc = ebadf();
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_shutdown_nt(&g_fds.p[fd], how);
} else {
rc = ebadf();
rc = enotsock();
}
STRACE("shutdown(%d, %d) -> %d% lm", fd, how, rc);
return rc;

View file

@ -54,7 +54,9 @@ int sockatmark(int fd) {
} else { //
magnum = 0x40047307; // SIOCATMARK (BSD, Windows)
}
if (!IsWindows()) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotsock();
} else if (!IsWindows()) {
if (sys_ioctl(fd, magnum, &rc) == -1) {
rc = -1;
}

View file

@ -27,6 +27,7 @@
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/critbit0.h"
#include "libc/mem/mem.h"
@ -76,14 +77,13 @@ struct dirstream {
uint64_t inode;
uint64_t offset;
uint64_t records;
size_t prefixlen;
uint8_t prefix[ZIPOS_PATH_MAX];
struct ZiposUri prefix;
struct critbit0 found;
} zip;
struct {
unsigned buf_pos;
unsigned buf_end;
uint64_t buf[(BUFSIZ + 256) / 8];
uint64_t buf[4096];
};
struct {
bool isdone;
@ -129,13 +129,13 @@ struct dirent_netbsd {
char d_name[512];
};
static void _lockdir(DIR *dir) {
static void lockdir(DIR *dir) {
if (__threaded) {
pthread_mutex_lock(&dir->lock);
}
}
static void _unlockdir(DIR *dir) {
static void unlockdir(DIR *dir) {
if (__threaded) {
pthread_mutex_unlock(&dir->lock);
}
@ -189,28 +189,46 @@ static textwindows dontinline struct dirent *readdir_nt(DIR *dir) {
return NULL;
}
// join absolute path
// join absolute path that's already normalized
uint64_t ino = 0;
size_t i = dir->name16len - 1;
char16_t jp[PATH_MAX];
size_t i = dir->name16len - 1; // foo\* -> foo\ (strip star)
memcpy(jp, dir->name16, i * sizeof(char16_t));
char16_t *p = dir->windata.cFileName;
if (p[0] == u'.' && p[1] == u'\0') {
// join("foo\\", ".") -> "foo\\"
} else if (p[0] == u'.' && p[1] == u'.' && p[2] == u'\0') {
if (i == 7 && //
jp[0] == '\\' && //
jp[1] == '\\' && //
jp[2] == '?' && //
jp[3] == '\\' && //
jp[5] == ':' && //
jp[6] == '\\') {
// e.g. \\?\C:\ stays the same
} else {
--i; // foo\bar\ -> foo\ (parent)
while (i && jp[i - 1] != '\\') --i;
}
} else {
while (*p) {
if (i + 1 < ARRAYLEN(dir->name16)) {
dir->name16[i++] = *p++;
if (i + 1 < ARRAYLEN(jp)) {
jp[i++] = *p++;
} else {
// ignore errors and set inode to zero
goto GiveUpOnGettingInode;
}
}
dir->name16[i] = u'\0';
}
jp[i] = u'\0';
// get inode that's consistent with stat()
int e = errno;
int64_t fh =
CreateFile(dir->name16, kNtFileReadAttributes, 0, 0, kNtOpenExisting,
// get inode such that it's consistent with stat()
// it's important that we not follow symlinks here
int64_t fh = CreateFile(jp, kNtFileReadAttributes, 0, 0, kNtOpenExisting,
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
kNtFileFlagOpenReparsePoint,
0);
if (fh != -1) {
if (fh != kNtInvalidHandleValue) {
struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(fh, &wst)) {
ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
@ -218,15 +236,11 @@ static textwindows dontinline struct dirent *readdir_nt(DIR *dir) {
CloseHandle(fh);
} else {
// ignore errors and set inode to zero
// TODO(jart): How do we handle "." and ".."?
errno = e;
STRACE("failed to get inode of path join(%#hs, %#hs) -> %#hs %m",
dir->name16, dir->windata.cFileName, jp);
}
GiveUpOnGettingInode:
// restore original directory search path
dir->name16[dir->name16len - 1] = u'*';
dir->name16[dir->name16len] = u'\0';
// create result object
bzero(&dir->ent, sizeof(dir->ent));
dir->ent.d_ino = ino;
@ -293,18 +307,19 @@ DIR *fdopendir(int fd) {
enametoolong();
return 0;
}
if (len) memcpy(dir->zip.prefix, name, len);
if (len && dir->zip.prefix[len - 1] != '/') {
dir->zip.prefix[len++] = '/';
if (len) memcpy(dir->zip.prefix.path, name, len);
if (len && dir->zip.prefix.path[len - 1] != '/') {
dir->zip.prefix.path[len++] = '/';
}
dir->zip.prefix[len] = 0;
dir->zip.prefixlen = len;
dir->zip.prefix.path[len] = 0;
dir->zip.prefix.len = len;
// setup state values for directory iterator
dir->zip.zipos = h->zipos;
dir->zip.inode = h->cfile;
dir->zip.offset = GetZipCdirOffset(h->zipos->cdir);
dir->zip.records = GetZipCdirRecords(h->zipos->cdir);
dir->zip.inode = __zipos_inode(h->zipos, h->cfile, dir->zip.prefix.path,
dir->zip.prefix.len);
return dir;
}
@ -346,7 +361,6 @@ DIR *opendir(const char *name) {
static struct dirent *readdir_zipos(DIR *dir) {
struct dirent *ent = 0;
while (!ent && dir->tell < dir->zip.records + 2) {
size_t n;
if (!dir->tell) {
ent = &dir->ent;
ent->d_off = dir->tell;
@ -354,23 +368,29 @@ static struct dirent *readdir_zipos(DIR *dir) {
ent->d_type = DT_DIR;
ent->d_name[0] = '.';
ent->d_name[1] = 0;
n = 1;
} else if (dir->tell == 1) {
ent = &dir->ent;
ent->d_off = dir->tell;
ent->d_ino = 0; // TODO
ent->d_type = DT_DIR;
ent->d_name[0] = '.';
ent->d_name[1] = '.';
ent->d_name[2] = 0;
n = 2;
struct ZiposUri p;
p.len = dir->zip.prefix.len;
if (p.len) memcpy(p.path, dir->zip.prefix.path, p.len);
while (p.len && p.path[p.len - 1] == '/') --p.len;
while (p.len && p.path[p.len - 1] != '/') --p.len;
while (p.len && p.path[p.len - 1] == '/') --p.len;
p.path[p.len] = 0;
ent->d_ino = __zipos_inode(
dir->zip.zipos, __zipos_find(dir->zip.zipos, &p), p.path, p.len);
} else {
uint8_t *s = ZIP_CFILE_NAME(dir->zip.zipos->map + dir->zip.offset);
n = ZIP_CFILE_NAMESIZE(dir->zip.zipos->map + dir->zip.offset);
if (n > dir->zip.prefixlen &&
!memcmp(dir->zip.prefix, s, dir->zip.prefixlen)) {
s += dir->zip.prefixlen;
n -= dir->zip.prefixlen;
size_t n = ZIP_CFILE_NAMESIZE(dir->zip.zipos->map + dir->zip.offset);
if (n > dir->zip.prefix.len &&
!memcmp(dir->zip.prefix.path, s, dir->zip.prefix.len)) {
s += dir->zip.prefix.len;
n -= dir->zip.prefix.len;
uint8_t *p = memchr(s, '/', n);
if (p) n = p - s;
if ((n = MIN(n, sizeof(ent->d_name) - 1)) &&
@ -394,10 +414,33 @@ static struct dirent *readdir_zipos(DIR *dir) {
return ent;
}
static void *golden(void *a, const void *b, size_t n) {
size_t i;
char *volatile d = a;
const char *volatile s = b;
if (d > s) {
for (i = n; i--;) {
d[i] = s[i];
asm volatile("" ::: "memory");
}
} else {
for (i = 0; i < n; ++i) {
d[i] = s[i];
asm volatile("" ::: "memory");
}
}
return d;
}
static struct dirent *readdir_unix(DIR *dir) {
if (dir->buf_pos >= dir->buf_end) {
long basep = dir->tell;
int rc = sys_getdents(dir->fd, dir->buf, sizeof(dir->buf) - 256, &basep);
if (IsNetbsd() || IsFreebsd()) {
unsigned long seeky = lseek(dir->fd, 0, SEEK_CUR);
unassert(seeky <= INT_MAX);
dir->tell = seeky << 32;
}
int rc = sys_getdents(dir->fd, dir->buf, sizeof(dir->buf), &basep);
STRACE("sys_getdents(%d) → %d% m", dir->fd, rc);
if (!rc || rc == -1) {
return NULL;
@ -455,11 +498,11 @@ static struct dirent *readdir_unix(DIR *dir) {
static struct dirent *readdir_impl(DIR *dir) {
if (dir->iszip) {
return readdir_zipos(dir);
}
if (IsWindows()) {
} else if (IsWindows()) {
return readdir_nt(dir);
}
} else {
return readdir_unix(dir);
}
}
/**
@ -474,9 +517,9 @@ static struct dirent *readdir_impl(DIR *dir) {
struct dirent *readdir(DIR *dir) {
struct dirent *e;
if (dir) {
_lockdir(dir);
lockdir(dir);
e = readdir_impl(dir);
_unlockdir(dir);
unlockdir(dir);
} else {
efault();
e = 0;
@ -497,14 +540,14 @@ struct dirent *readdir(DIR *dir) {
errno_t readdir_r(DIR *dir, struct dirent *output, struct dirent **result) {
int err, olderr;
struct dirent *entry;
_lockdir(dir);
lockdir(dir);
olderr = errno;
errno = 0;
entry = readdir_impl(dir);
err = errno;
errno = olderr;
if (err) {
_unlockdir(dir);
unlockdir(dir);
return err;
}
if (entry) {
@ -514,7 +557,7 @@ errno_t readdir_r(DIR *dir, struct dirent *output, struct dirent **result) {
} else {
output = 0;
}
_unlockdir(dir);
unlockdir(dir);
*result = output;
return 0;
}
@ -548,9 +591,9 @@ int closedir(DIR *dir) {
*/
long telldir(DIR *dir) {
long rc;
_lockdir(dir);
lockdir(dir);
rc = dir->tell;
_unlockdir(dir);
unlockdir(dir);
return rc;
}
@ -567,7 +610,7 @@ int dirfd(DIR *dir) {
* @threadsafe
*/
void rewinddir(DIR *dir) {
_lockdir(dir);
lockdir(dir);
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
dir->tell = 0;
@ -586,7 +629,7 @@ void rewinddir(DIR *dir) {
dir->isdone = true;
}
}
_unlockdir(dir);
unlockdir(dir);
}
/**
@ -594,7 +637,7 @@ void rewinddir(DIR *dir) {
* @threadsafe
*/
void seekdir(DIR *dir, long tell) {
_lockdir(dir);
lockdir(dir);
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
dir->tell = 0;
@ -604,6 +647,14 @@ void seekdir(DIR *dir, long tell) {
break;
}
}
} else if (IsNetbsd() || IsFreebsd()) {
dir->buf_pos = dir->buf_end = 0;
dir->tell = lseek(dir->fd, tell >> 32, SEEK_SET) << 32;
while (dir->tell < tell) {
if (!readdir_unix(dir)) {
break;
}
}
} else if (!IsWindows()) {
dir->tell = lseek(dir->fd, tell, SEEK_SET);
dir->buf_pos = dir->buf_end = 0;
@ -622,7 +673,7 @@ void seekdir(DIR *dir, long tell) {
dir->isdone = true;
}
}
_unlockdir(dir);
unlockdir(dir);
}
__weak_reference(readdir, readdir64);

View file

@ -29,7 +29,7 @@
int fgetc_unlocked(FILE *f) {
unsigned char b[1];
if (f->beg < f->end) {
return f->buf[f->beg++] & 0xff;
return f->buf[f->beg++] & 255;
} else {
if (!fread_unlocked(b, 1, 1, f)) return -1;
return b[0];

View file

@ -29,11 +29,11 @@
#include "libc/calls/struct/dirent.h"
#include "libc/calls/weirdtypes.h"
#include "libc/errno.h"
#include "libc/stdio/ftw.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/thread/thread.h"
#include "libc/stdio/ftw.h"
#define PATH_MAXIMUS 4096

View file

@ -136,3 +136,10 @@ TEST(critbit0, duplicate) {
ASSERT_FALSE(critbit0_insert(&tree, "hi"));
critbit0_clear(&tree);
}
TEST(critbit0, manual_clear) {
struct critbit0 tree = {0};
ASSERT_TRUE(critbit0_insert(&tree, "hi"));
ASSERT_TRUE(critbit0_delete(&tree, "hi"));
ASSERT_EQ(NULL, tree.root);
}

View file

@ -39,7 +39,7 @@ int Worker(void *arg, int tid) {
int i, fd;
char *data;
for (i = 0; i < 20; ++i) {
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
ASSERT_NE(-1, (fd = open("/zip//./libc/testlib//hyperion.txt", O_RDONLY)));
data = malloc(kHyperionSize);
ASSERT_EQ(kHyperionSize, read(fd, data, kHyperionSize));
ASSERT_EQ(0, memcmp(data, kHyperion, kHyperionSize));
@ -57,30 +57,13 @@ TEST(zipos, test) {
__print_maps();
}
TEST(zipos, normpath) {
{
char s[] = "";
__zipos_normpath(s);
ASSERT_STREQ("", s);
}
{
char s[] = "usr/";
__zipos_normpath(s);
ASSERT_STREQ("usr", s);
}
{
char s[] = "usr/./";
__zipos_normpath(s);
ASSERT_STREQ("usr", s);
}
TEST(zipos, erofs) {
ASSERT_SYS(EROFS, -1, creat("/zip/foo.txt", 0644));
}
#if 0
TEST(zipos_O_DIRECTORY, blocksOpeningOfNormalFiles) {
ASSERT_SYS(ENOTDIR, -1,
open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY));
TEST(zipos, enoent) {
ASSERT_SYS(ENOENT, -1, open("/zip/foo.txt", O_RDONLY));
}
#endif
TEST(zipos, readPastEof) {
char buf[512];
@ -89,6 +72,13 @@ TEST(zipos, readPastEof) {
EXPECT_SYS(0, 0, pread(3, buf, 512, INT64_MAX));
EXPECT_SYS(EINVAL, -1, lseek(3, UINT64_MAX, SEEK_SET));
EXPECT_SYS(0, INT64_MAX, lseek(3, INT64_MAX, SEEK_SET));
EXPECT_SYS(EBADF, -1, write(3, buf, 512));
EXPECT_SYS(EBADF, -1, pwrite(3, buf, 512, 0));
EXPECT_SYS(0, 0, read(3, buf, 512));
EXPECT_SYS(0, 0, close(3));
}
TEST(zipos_O_DIRECTORY, blocksOpeningOfNormalFiles) {
ASSERT_SYS(ENOTDIR, -1,
open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY));
}

View file

@ -21,10 +21,17 @@
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/mem/critbit0.h"
#include "libc/mem/gc.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.h"
#include "libc/stdio/ftw.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
@ -37,6 +44,8 @@
__static_yoink("zipos");
__static_yoink("usr/share/zoneinfo/");
__static_yoink("usr/share/zoneinfo/New_York");
__static_yoink("libc/testlib/hyperion.txt");
__static_yoink("libc/testlib/moby.txt");
char testlib_enable_tmp_setup_teardown;
@ -64,55 +73,6 @@ TEST(opendir, enotdir) {
ASSERT_SYS(ENOTDIR, NULL, opendir("yo/there"));
}
TEST(opendir, zipTest_fake) {
ASSERT_NE(NULL, (dir = opendir("/zip")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("echo.com", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("usr", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".cosmo", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("echo.com", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("usr", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".cosmo", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/usr")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("share", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/usr/")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("share", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
EXPECT_EQ(NULL, (dir = opendir("/zip/us")));
EXPECT_EQ(NULL, (dir = opendir("/zip/us/")));
}
TEST(opendir, openSyntheticDirEntry) {
struct stat st;
ASSERT_SYS(0, 3, open("/zip", O_RDONLY | O_DIRECTORY));
@ -227,8 +187,7 @@ TEST(dirstream, zipTest_notDir) {
ASSERT_EQ(ENOTDIR, errno);
}
TEST(dirstream, seek) {
if (IsNetbsd()) return; // omg
TEST(dirstream, ino) {
ASSERT_SYS(0, 0, mkdir("boop", 0755));
EXPECT_SYS(0, 0, touch("boop/a", 0644));
EXPECT_SYS(0, 0, touch("boop/b", 0644));
@ -253,8 +212,7 @@ TEST(dirstream, seek) {
ASSERT_SYS(0, 0, closedir(dir));
}
TEST(dirstream, ino) {
if (IsNetbsd()) return; // omg
TEST(dirstream, seek) {
ASSERT_SYS(0, 0, mkdir("boop", 0755));
EXPECT_SYS(0, 0, touch("boop/a", 0644));
EXPECT_SYS(0, 0, touch("boop/b", 0644));
@ -279,6 +237,80 @@ TEST(dirstream, ino) {
ASSERT_SYS(0, 0, closedir(dir));
}
TEST(dirstream, seeky) {
char name[256];
char path[512];
struct stat golden;
int i, j, n = 1000;
int goodindex = 500;
struct critbit0 tree = {0};
ASSERT_SYS(0, 0, mkdir("boop", 0755));
ASSERT_EQ(1, critbit0_insert(&tree, "."));
ASSERT_EQ(1, critbit0_insert(&tree, ".."));
for (i = 0; i < n; ++i) {
for (j = 0; j < 255; ++j) {
name[j] = '0' + rand() % 10;
}
// TODO(jart): why does Windows croak with 255
name[100] = 0;
strcpy(path, "boop/");
strcat(path, name);
path[255] = 0;
*FormatInt32(path + 5, i) = '-';
ASSERT_EQ(1, critbit0_insert(&tree, path + 5));
ASSERT_SYS(0, 0, touch(path, 0644));
if (i == goodindex) {
ASSERT_SYS(0, 0, stat(path, &golden));
}
}
// do a full pass
{
ASSERT_NE(NULL, (dir = opendir("boop")));
long tell = -1;
long prev = telldir(dir);
while ((ent = readdir(dir))) {
if (atoi(ent->d_name) == goodindex) {
ASSERT_EQ(golden.st_ino, ent->d_ino);
tell = prev;
}
prev = telldir(dir);
ASSERT_EQ(1, critbit0_delete(&tree, ent->d_name));
}
ASSERT_EQ(NULL, tree.root); // all entries were found
ASSERT_NE(-1, tell);
seekdir(dir, tell);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(goodindex, atoi(ent->d_name));
ASSERT_EQ(golden.st_ino, ent->d_ino);
ASSERT_SYS(0, 0, closedir(dir));
}
// do a partial pass, and seek midway
{
ASSERT_NE(NULL, (dir = opendir("boop")));
int abort = 700;
long tell = -1;
bool foundit = false;
long prev = telldir(dir);
while ((ent = readdir(dir))) {
if (atoi(ent->d_name) == goodindex) {
ASSERT_EQ(golden.st_ino, ent->d_ino);
tell = prev;
foundit = true;
}
prev = telldir(dir);
if (--abort <= 0 && foundit) {
break;
}
}
ASSERT_NE(-1, tell);
seekdir(dir, tell);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(goodindex, atoi(ent->d_name));
ASSERT_EQ(golden.st_ino, ent->d_ino);
ASSERT_SYS(0, 0, closedir(dir));
}
}
TEST(dirstream, dots) {
bool got_dot = false;
bool got_dot_dot = false;
@ -321,3 +353,79 @@ TEST(dirstream_zipos, inoFile_isConsistentWithStat) {
}
ASSERT_SYS(0, 0, closedir(dir));
}
static const char *DescribeDt(int dt) {
static char buf[12];
switch (dt) {
case DT_UNKNOWN:
return "DT_UNKNOWN";
case DT_FIFO:
return "DT_FIFO";
case DT_CHR:
return "DT_CHR";
case DT_DIR:
return "DT_DIR";
case DT_BLK:
return "DT_BLK";
case DT_REG:
return "DT_REG";
case DT_LNK:
return "DT_LNK";
case DT_SOCK:
return "DT_SOCK";
default:
FormatInt32(buf, dt);
return buf;
}
}
static const char *DescribeFtw(int dt) {
static char buf[12];
switch (dt) {
case FTW_F:
return "FTW_F";
case FTW_D:
return "FTW_D";
case FTW_DNR:
return "FTW_DNR";
case FTW_NS:
return "FTW_NS";
case FTW_SL:
return "FTW_SL";
case FTW_DP:
return "FTW_DP";
case FTW_SLN:
return "FTW_SLN";
default:
FormatInt32(buf, dt);
return buf;
}
}
char *b;
static int walk(const char *fpath, //
const struct stat *st, //
int typeflag, //
struct FTW *ftwbuf) { //
appendf(&b, "%-6s %s\n", DescribeFtw(typeflag), fpath);
return 0;
}
TEST(dirstream, walk) {
ASSERT_SYS(0, 0, nftw("/zip", walk, 128, FTW_PHYS | FTW_DEPTH));
ASSERT_STREQ("FTW_F /zip/echo.com\n"
"FTW_F /zip/libc/testlib/hyperion.txt\n"
"FTW_F /zip/libc/testlib/moby.txt\n"
"FTW_DP /zip/libc/testlib\n"
"FTW_DP /zip/libc\n"
"FTW_F /zip/usr/share/zoneinfo/New_York\n"
"FTW_DP /zip/usr/share/zoneinfo\n"
"FTW_DP /zip/usr/share\n"
"FTW_DP /zip/usr\n"
"FTW_F /zip/.cosmo\n"
"FTW_DP /zip\n",
b);
free(b);
b = 0;
}

View file

@ -17,8 +17,13 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/dirent.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/testlib/testlib.h"
@ -68,3 +73,103 @@ TEST(dirstream, hasDirectoryEntry) {
ASSERT_SYS(0, 0, closedir(dir));
EXPECT_TRUE(gotsome);
}
TEST(__zipos_normpath, emptyBuf_wontNulTerminate) {
__zipos_normpath(0, "hello", 0);
}
TEST(__zipos_normpath, overflows_willNulTerminate) {
char buf[2];
ASSERT_EQ(2, __zipos_normpath(buf, "hello", 2));
ASSERT_STREQ("h", buf);
}
TEST(__zipos_normpath, vectors) {
static const char V[][2][128] = {
{"", ""},
{"/..", ""},
{"/../", ""},
{".", ""},
{"./", ""},
{"..", ""},
{"../", ""},
{"../abc/def", "abc/def"},
{"../abc/def/..", "abc"},
{"../abc/././././def/..", "abc"},
{"////../abc/def", "abc/def"},
{"/../def", "def"},
{"../def", "def"},
{"/abc////../def", "def"},
{"abc/../def/ghi", "def/ghi"},
{"/abc/def/../ghi", "abc/ghi"},
{"/abc/..abc////../def", "abc/def"},
{"/abc/..abc/../def", "abc/def"},
{"/abc/..abc/def", "abc/..abc/def"},
{"abc/../def", "def"},
{"abc/../../def", "def"},
{"abc/../../../def", "def"},
{"././", ""},
{"abc/..", ""},
{"abc/../", ""},
{"abc/../..", ""},
{"abc/../../", ""},
{"a/..", ""},
{"a/../", ""},
{"a/../..", ""},
{"a/../../", ""},
{"../../a", "a"},
{"../../../a", "a"},
{"../a../../a", "a"},
{"cccc/abc////..////.//../", ""},
{"aaaa/cccc/abc////..////.//../", "aaaa"},
{"..//////.///..////..////.//////abc////.////..////def//abc/..", "def"},
{"////////////..//////.///..////..////.//////abc////.////..////def//abc/"
"..",
"def"},
};
int fails = 0;
// test non-overlapping arguments
// duplicate input string for better asan checking
for (int i = 0; i < ARRAYLEN(V); ++i) {
char tmp[128];
__zipos_normpath(tmp, gc(strdup(V[i][0])), sizeof(tmp));
if (strcmp(tmp, V[i][1])) {
if (++fails < 8) {
fprintf(stderr,
"\n%s: zipos path normalization test failed\n"
"\tinput = %`'s\n"
"\t want = %`'s\n"
"\t got = %`'s\n"
"\t i = %d\n",
program_invocation_name, V[i][0], V[i][1], tmp, i);
}
}
}
// test overlapping arguments
if (!fails) {
for (int i = 0; i < ARRAYLEN(V); ++i) {
char tmp[128];
strcpy(tmp, V[i][0]);
__zipos_normpath(tmp, tmp, sizeof(tmp));
if (strcmp(tmp, V[i][1])) {
if (++fails < 8) {
fprintf(stderr,
"\n%s: zipos path normalization breaks w/ overlapping args\n"
"\tinput = %`'s\n"
"\t want = %`'s\n"
"\t got = %`'s\n"
"\t i = %d\n",
program_invocation_name, V[i][0], V[i][1], tmp, i);
}
}
}
}
if (fails) {
fprintf(stderr, "\n%d / %zd zipos path norm tests failed\n", fails,
ARRAYLEN(V));
exit(1);
}
}

View file

@ -148,7 +148,7 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *cname,
CHECK_NE(0, mtim.tv_sec);
char *name = gc(strndup(cname, namesize));
namesize = __zipos_normpath(name);
namesize = __zipos_normpath(name, name, strlen(name) + 1);
if (S_ISDIR(mode) && namesize && name[namesize - 1] != '/') {
name[namesize++] = '/';
name[namesize] = 0;