Remove old stack code and improve dirstream

This commit is contained in:
Justine Tunney 2023-08-16 07:54:40 -07:00
parent 74caabb823
commit dc6c67256f
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
61 changed files with 463 additions and 595 deletions

View file

@ -25,13 +25,19 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nopl.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/mem/critbit0.h"
#include "libc/mem/mem.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/byhandlefileinformation.h"
#include "libc/nt/struct/win32finddata.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
@ -60,18 +66,20 @@ int sys_getdents(unsigned, void *, unsigned, long *);
struct dirstream {
int fd;
bool iszip;
long tell;
int64_t hand;
int64_t tell;
char16_t *name;
pthread_mutex_t lock;
struct {
uint64_t offset;
uint64_t records;
size_t prefixlen;
uint8_t prefix[ZIPOS_PATH_MAX];
} zip;
struct dirent ent;
union {
struct {
struct Zipos *zipos;
uint64_t inode;
uint64_t offset;
uint64_t records;
size_t prefixlen;
uint8_t prefix[ZIPOS_PATH_MAX];
struct critbit0 found;
} zip;
struct {
unsigned buf_pos;
unsigned buf_end;
@ -80,6 +88,8 @@ struct dirstream {
struct {
bool isdone;
struct NtWin32FindData windata;
char16_t name16[PATH_MAX];
uint32_t name16len;
};
};
};
@ -119,69 +129,40 @@ struct dirent_netbsd {
char d_name[512];
};
// TODO(jart): wipe these locks when forking
void _lockdir(DIR *dir) {
pthread_mutex_lock(&dir->lock);
}
void _unlockdir(DIR *dir) {
pthread_mutex_unlock(&dir->lock);
}
#ifdef _NOPL1
#define _lockdir(d) _NOPL1("__threadcalls", _lockdir, d)
#define _unlockdir(d) _NOPL1("__threadcalls", _unlockdir, d)
#else
#define _lockdir(d) (__threaded ? _lockdir(d) : 0)
#define _unlockdir(d) (__threaded ? _unlockdir(d) : 0)
#endif
static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) {
DIR *res;
if (len + 2 + 1 <= PATH_MAX) {
if (len == 1 && name[0] == '.') {
name[0] = '*';
} else {
if (len > 1 && name[len - 1] != u'\\') {
name[len++] = u'\\';
}
name[len++] = u'*';
}
name[len] = u'\0';
if ((res = calloc(1, sizeof(DIR)))) {
if ((res->hand = FindFirstFile(name, &res->windata)) != -1) {
return res;
}
__fix_enotdir(-1, name);
free(res);
}
} else {
enametoolong();
static void _lockdir(DIR *dir) {
if (__threaded) {
pthread_mutex_lock(&dir->lock);
}
return NULL;
}
static textwindows dontinline DIR *fdopendir_nt(int fd) {
DIR *res;
char16_t *name;
if (__isfdkind(fd, kFdFile)) {
if ((name = calloc(1, PATH_MAX * 2))) {
if ((res = opendir_nt_impl(
name, GetFinalPathNameByHandle(
g_fds.p[fd].handle, name, PATH_MAX,
kNtFileNameNormalized | kNtVolumeNameDos)))) {
res->name = name;
res->fd = -1;
close(fd);
return res;
}
free(name);
}
} else {
ebadf();
static void _unlockdir(DIR *dir) {
if (__threaded) {
pthread_mutex_unlock(&dir->lock);
}
return NULL;
}
static textwindows dontinline int fdopendir_nt(DIR *res, int fd) {
if (!__isfdkind(fd, kFdFile)) {
return ebadf();
}
res->name16len = GetFinalPathNameByHandle(
g_fds.p[fd].handle, res->name16, ARRAYLEN(res->name16),
kNtFileNameNormalized | kNtVolumeNameDos);
if (!res->name16len) {
return __winerr();
}
if (res->name16len + 2 + 1 > ARRAYLEN(res->name16)) {
return enametoolong();
}
if (res->name16len > 1 && res->name16[res->name16len - 1] != u'\\') {
res->name16[res->name16len++] = u'\\';
}
res->name16[res->name16len++] = u'*';
res->name16[res->name16len] = u'\0';
if ((res->hand = FindFirstFile(res->name16, &res->windata)) == -1) {
return __fix_enotdir(-1, res->name16);
}
return 0;
}
static textwindows uint8_t GetNtDirentType(struct NtWin32FindData *w) {
@ -204,26 +185,57 @@ static textwindows uint8_t GetNtDirentType(struct NtWin32FindData *w) {
}
static textwindows dontinline struct dirent *readdir_nt(DIR *dir) {
size_t i;
if (!dir->isdone) {
bzero(&dir->ent, sizeof(dir->ent));
dir->ent.d_ino++;
dir->ent.d_off = dir->tell++;
dir->ent.d_reclen =
tprecode16to8(dir->ent.d_name, sizeof(dir->ent.d_name) - 2,
dir->windata.cFileName)
.ax;
for (i = 0; i < dir->ent.d_reclen; ++i) {
if (dir->ent.d_name[i] == '\\') {
dir->ent.d_name[i] = '/';
}
}
dir->ent.d_type = GetNtDirentType(&dir->windata);
dir->isdone = !FindNextFile(dir->hand, &dir->windata);
return &dir->ent;
} else {
if (dir->isdone) {
return NULL;
}
// join absolute path
uint64_t ino = 0;
size_t i = dir->name16len - 1;
char16_t *p = dir->windata.cFileName;
while (*p) {
if (i + 1 < ARRAYLEN(dir->name16)) {
dir->name16[i++] = *p++;
} else {
// ignore errors and set inode to zero
goto GiveUpOnGettingInode;
}
}
dir->name16[i] = u'\0';
// get inode that's consistent with stat()
int e = errno;
int64_t fh =
CreateFile(dir->name16, kNtFileReadAttributes, 0, 0, kNtOpenExisting,
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
kNtFileFlagOpenReparsePoint,
0);
if (fh != -1) {
struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(fh, &wst)) {
ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
}
CloseHandle(fh);
} else {
// ignore errors and set inode to zero
// TODO(jart): How do we handle "." and ".."?
errno = e;
}
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;
dir->ent.d_off = dir->tell++;
tprecode16to8(dir->ent.d_name, sizeof(dir->ent.d_name),
dir->windata.cFileName);
dir->ent.d_type = GetNtDirentType(&dir->windata);
dir->isdone = !FindNextFile(dir->hand, &dir->windata);
return &dir->ent;
}
/**
@ -246,8 +258,12 @@ DIR *fdopendir(int fd) {
dir->fd = fd;
if (!__isfdkind(fd, kFdZip)) {
if (IsWindows()) {
free(dir);
return fdopendir_nt(fd);
if (!fdopendir_nt(dir, fd)) {
return dir;
} else {
free(dir);
return 0;
}
}
return dir;
}
@ -285,6 +301,8 @@ DIR *fdopendir(int fd) {
dir->zip.prefixlen = 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);
@ -325,124 +343,123 @@ DIR *opendir(const char *name) {
return res;
}
static struct dirent *readdir_impl(DIR *dir) {
size_t n;
int rc, mode;
uint8_t *s, *p;
struct Zipos *zip;
struct dirent *ent;
struct dirent *lastent;
struct dirent_bsd *bsd;
struct dirent_netbsd *nbsd;
struct dirent_openbsd *obsd;
if (dir->iszip) {
ent = 0;
zip = _weaken(__zipos_get)();
while (!ent && dir->tell < dir->zip.records + 2) {
if (!dir->tell) {
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = 1;
ent->d_type = DT_DIR;
strcpy(ent->d_name, ".");
} else if (dir->tell == 1) {
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = 2;
ent->d_type = DT_DIR;
strcpy(ent->d_name, "..");
} else {
s = ZIP_CFILE_NAME(zip->map + dir->zip.offset);
n = ZIP_CFILE_NAMESIZE(zip->map + dir->zip.offset);
if (dir->zip.prefixlen < n &&
!memcmp(dir->zip.prefix, s, dir->zip.prefixlen)) {
s += dir->zip.prefixlen;
n -= dir->zip.prefixlen;
p = memchr(s, '/', n);
if (!p || p + 1 - s == n) {
if (p + 1 - s == n) --n;
mode = GetZipCfileMode(zip->map + dir->zip.offset);
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = MIN(n, 255);
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
if (ent->d_reclen) {
memcpy(ent->d_name, s, ent->d_reclen);
}
ent->d_name[ent->d_reclen] = 0;
} else {
lastent = (struct dirent *)dir->buf;
n = p - s;
n = MIN(n, 255);
if (!lastent->d_ino || (n != lastent->d_reclen) ||
memcmp(lastent->d_name, s, n)) {
ent = lastent;
mode = GetZipCfileMode(zip->map + dir->zip.offset);
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = n;
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
if (ent->d_reclen) {
memcpy(ent->d_name, s, ent->d_reclen);
}
ent->d_name[ent->d_reclen] = 0;
}
}
}
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
}
dir->tell++;
}
return ent;
} else if (!IsWindows()) {
if (dir->buf_pos >= dir->buf_end) {
long basep = dir->tell;
rc = sys_getdents(dir->fd, dir->buf, sizeof(dir->buf) - 256, &basep);
STRACE("sys_getdents(%d) → %d% m", dir->fd, rc);
if (!rc || rc == -1) return NULL;
dir->buf_pos = 0;
dir->buf_end = rc;
}
if (IsLinux()) {
ent = (struct dirent *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += ent->d_reclen;
dir->tell = ent->d_off;
} else if (IsOpenbsd()) {
obsd = (struct dirent_openbsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += obsd->d_reclen;
dir->tell = obsd->d_off;
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_ino = obsd->d_fileno;
ent->d_off = obsd->d_off;
ent->d_reclen = obsd->d_reclen;
ent->d_type = obsd->d_type;
memcpy(ent->d_name, obsd->d_name, obsd->d_namlen + 1);
} else if (IsNetbsd()) {
nbsd = (struct dirent_netbsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += nbsd->d_reclen;
ent->d_off = dir->tell;
ent->d_ino = dir->zip.inode;
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_ino = nbsd->d_fileno;
ent->d_off = (dir->tell += nbsd->d_reclen);
ent->d_reclen = nbsd->d_reclen;
ent->d_type = nbsd->d_type;
memcpy(ent->d_name, nbsd->d_name, MAX(256, nbsd->d_namlen + 1));
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;
} else {
bsd = (struct dirent_bsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += bsd->d_reclen;
ent = &dir->ent;
ent->d_ino = bsd->d_fileno;
ent->d_off = dir->tell++;
ent->d_reclen = bsd->d_reclen;
ent->d_type = bsd->d_type;
memcpy(ent->d_name, bsd->d_name, bsd->d_namlen + 1);
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;
uint8_t *p = memchr(s, '/', n);
if (p) n = p - s;
if ((n = MIN(n, sizeof(ent->d_name) - 1)) &&
critbit0_emplace(&dir->zip.found, s, n) == 1) {
ent = &dir->ent;
ent->d_ino = dir->zip.offset;
ent->d_off = dir->tell;
ent->d_type =
S_ISDIR(GetZipCfileMode(dir->zip.zipos->map + dir->zip.offset))
? DT_DIR
: DT_REG;
memcpy(ent->d_name, s, n);
ent->d_name[n] = 0;
}
}
dir->zip.offset +=
ZIP_CFILE_HDRSIZE(dir->zip.zipos->map + dir->zip.offset);
}
return ent;
dir->tell++;
}
return ent;
}
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);
STRACE("sys_getdents(%d) → %d% m", dir->fd, rc);
if (!rc || rc == -1) {
return NULL;
}
dir->buf_pos = 0;
dir->buf_end = rc;
}
struct dirent *ent;
if (IsLinux()) {
ent = (struct dirent *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += ent->d_reclen;
dir->tell = ent->d_off;
} else if (IsOpenbsd()) {
struct dirent_openbsd *obsd =
(struct dirent_openbsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += obsd->d_reclen;
dir->tell = obsd->d_off;
ent = &dir->ent;
ent->d_ino = obsd->d_fileno;
ent->d_off = obsd->d_off;
ent->d_reclen = obsd->d_reclen;
ent->d_type = obsd->d_type;
memcpy(ent->d_name, obsd->d_name, obsd->d_namlen + 1);
} else if (IsNetbsd()) {
struct dirent_netbsd *nbsd =
(struct dirent_netbsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += nbsd->d_reclen;
ent = &dir->ent;
ent->d_ino = nbsd->d_fileno;
ent->d_off = (dir->tell += nbsd->d_reclen);
ent->d_reclen = nbsd->d_reclen;
ent->d_type = nbsd->d_type;
size_t n =
MIN(nbsd->d_namlen, MIN(sizeof(ent->d_name) - 1, sizeof(nbsd->d_name)));
memcpy(ent->d_name, nbsd->d_name, n);
ent->d_name[n] = 0;
} else {
struct dirent_bsd *bsd =
(struct dirent_bsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += bsd->d_reclen;
ent = &dir->ent;
ent->d_ino = bsd->d_fileno;
ent->d_off = dir->tell++;
ent->d_reclen = bsd->d_reclen;
ent->d_type = bsd->d_type;
memcpy(ent->d_name, bsd->d_name, bsd->d_namlen + 1);
}
if (ent) {
ent->d_reclen =
ROUNDUP(offsetof(struct dirent, d_name) + strlen(ent->d_name) + 1, 8);
}
return ent;
}
static struct dirent *readdir_impl(DIR *dir) {
if (dir->iszip) {
return readdir_zipos(dir);
}
if (IsWindows()) {
return readdir_nt(dir);
}
return readdir_unix(dir);
}
/**
@ -509,10 +526,12 @@ errno_t readdir_r(DIR *dir, struct dirent *output, struct dirent **result) {
int closedir(DIR *dir) {
int rc = 0;
if (dir) {
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
}
if (dir->fd != -1) {
rc |= close(dir->fd);
}
free(dir->name);
if (IsWindows() && !dir->iszip) {
if (!FindClose(dir->hand)) {
rc = __winerr();
@ -540,11 +559,7 @@ long telldir(DIR *dir) {
* @threadsafe
*/
int dirfd(DIR *dir) {
int rc;
_lockdir(dir);
rc = dir->fd;
_unlockdir(dir);
return rc;
return dir->fd;
}
/**
@ -554,8 +569,9 @@ int dirfd(DIR *dir) {
void rewinddir(DIR *dir) {
_lockdir(dir);
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
dir->tell = 0;
dir->zip.offset = GetZipCdirOffset(_weaken(__zipos_get)()->cdir);
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
} else if (!IsWindows()) {
if (!lseek(dir->fd, 0, SEEK_SET)) {
dir->buf_pos = dir->buf_end = 0;
@ -563,7 +579,7 @@ void rewinddir(DIR *dir) {
}
} else {
FindClose(dir->hand);
if ((dir->hand = FindFirstFile(dir->name, &dir->windata)) != -1) {
if ((dir->hand = FindFirstFile(dir->name16, &dir->windata)) != -1) {
dir->isdone = false;
dir->tell = 0;
} else {
@ -577,27 +593,26 @@ void rewinddir(DIR *dir) {
* Seeks in directory stream.
* @threadsafe
*/
void seekdir(DIR *dir, long off) {
long i;
struct Zipos *zip;
void seekdir(DIR *dir, long tell) {
_lockdir(dir);
zip = _weaken(__zipos_get)();
if (dir->iszip) {
dir->zip.offset = GetZipCdirOffset(_weaken(__zipos_get)()->cdir);
for (i = 0; i < off && i < dir->zip.records; ++i) {
if (i >= 2) {
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
critbit0_clear(&dir->zip.found);
dir->tell = 0;
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
while (dir->tell < tell) {
if (!readdir_zipos(dir)) {
break;
}
}
} else if (!IsWindows()) {
i = lseek(dir->fd, off, SEEK_SET);
dir->tell = lseek(dir->fd, tell, SEEK_SET);
dir->buf_pos = dir->buf_end = 0;
} else {
i = 0;
dir->tell = 0;
dir->isdone = false;
FindClose(dir->hand);
if ((dir->hand = FindFirstFile(dir->name, &dir->windata)) != -1) {
for (; i < off; ++i) {
if ((dir->hand = FindFirstFile(dir->name16, &dir->windata)) != -1) {
for (; dir->tell < tell; ++dir->tell) {
if (!FindNextFile(dir->hand, &dir->windata)) {
dir->isdone = true;
break;
@ -607,7 +622,6 @@ void seekdir(DIR *dir, long off) {
dir->isdone = true;
}
}
dir->tell = i;
_unlockdir(dir);
}

View file

@ -56,6 +56,7 @@ o//libc/stdio/appendw.o: private \
CFLAGS += \
-Os
o/$(MODE)/libc/stdio/dirstream.o \
o/$(MODE)/libc/stdio/posix_spawnattr.o \
o/$(MODE)/libc/stdio/posix_spawn_file_actions.o \
o/$(MODE)/libc/stdio/mt19937.o: private \