Skip to content

Commit

Permalink
Rewrite special file handling on Windows
Browse files Browse the repository at this point in the history
This change gets GNU grep working. What caused it to not work, is it
wouldn't write to an output file descriptor when its dev/ino equaled
/dev/null's. So now we invent special dev/ino values for these files
  • Loading branch information
jart committed Oct 14, 2023
1 parent aca2261 commit 2db2f40
Show file tree
Hide file tree
Showing 53 changed files with 484 additions and 298 deletions.
4 changes: 1 addition & 3 deletions libc/calls/abort.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@
* @noreturn
*/
wontreturn void abort(void) {
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGABRT);
sigset_t m = 1ull << (SIGABRT - 1);
sigprocmask(SIG_UNBLOCK, &m, 0);
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);
Expand Down
55 changes: 5 additions & 50 deletions libc/calls/fcntl-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,63 +321,18 @@ static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start);
}

static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags,
unsigned mode, unsigned arg,
intptr_t *handle) {

// you may change the following:
//
// - O_NONBLOCK make read() raise EAGAIN
// - O_APPEND for toggling append mode
// - O_RANDOM alt. for posix_fadvise()
// - O_SEQUENTIAL alt. for posix_fadvise()
// - O_DIRECT works but haven't tested
//
// the other bits are ignored.
unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK;
unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT;
unsigned newflag = (*flags & ~allowed) | (arg & allowed);

if ((*flags & needreo) ^ (arg & needreo)) {
unsigned perm, share, attr;
if (GetNtOpenFlags(newflag, mode, &perm, &share, 0, &attr) == -1) {
return -1;
}
// MSDN says only these are allowed, otherwise it returns EINVAL.
attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |
kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped |
kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess |
kNtFileFlagSequentialScan | kNtFileFlagWriteThrough;
intptr_t hand;
if ((hand = ReOpenFile(*handle, perm, share, attr)) != -1) {
if (hand != *handle) {
CloseHandle(*handle);
*handle = hand;
}
} else {
return __winerr();
}
}

// 1. ignore flags that aren't access mode flags
// 2. return zero if nothing's changed
*flags = newflag;
return 0;
}

textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
int rc;
BLOCK_SIGNALS;
if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole)) {
if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole) || //
__isfdkind(fd, kFdDevNull)) {
if (cmd == F_GETFL) {
rc = g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_DIRECT | O_NONBLOCK |
O_RANDOM | O_SEQUENTIAL);
} else if (cmd == F_SETFL) {
rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, g_fds.p[fd].mode, arg,
&g_fds.p[fd].handle);
rc = sys_fcntl_nt_setfl(fd, arg);
} else if (cmd == F_GETFD) {
if (g_fds.p[fd].flags & O_CLOEXEC) {
rc = FD_CLOEXEC;
Expand Down
43 changes: 41 additions & 2 deletions libc/calls/fstat-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/wintime.internal.h"
#include "libc/intrin/atomic.h"
Expand Down Expand Up @@ -76,13 +78,47 @@ static textwindows long GetSizeOfReparsePoint(int64_t fh) {
return z;
}

textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
static textwindows int sys_fstat_nt_socket(int kind, struct stat *st) {
bzero(st, sizeof(*st));
st->st_blksize = 512;
st->st_mode = S_IFSOCK | 0666;
st->st_dev = 0x44444444;
st->st_ino = kind;
return 0;
}

textwindows int sys_fstat_nt_special(int kind, struct stat *st) {
bzero(st, sizeof(*st));
st->st_blksize = 512;
st->st_mode = S_IFCHR | 0666;
st->st_dev = 0x77777777;
st->st_ino = kind;
return 0;
}

textwindows int sys_fstat_nt(int fd, struct stat *st) {
if (fd + 0u >= g_fds.n) return ebadf();
switch (g_fds.p[fd].kind) {
case kFdEmpty:
return ebadf();
case kFdConsole:
case kFdDevNull:
return sys_fstat_nt_special(g_fds.p[fd].kind, st);
case kFdSocket:
return sys_fstat_nt_socket(g_fds.p[fd].kind, st);
default:
return sys_fstat_nt_handle(g_fds.p[fd].handle, st);
}
}

textwindows int sys_fstat_nt_handle(int64_t handle, struct stat *out_st) {
struct stat st = {0};

// Always set st_blksize to avoid divide by zero issues.
// The Linux kernel sets this for /dev/tty and similar too.
// TODO(jart): GetVolumeInformationByHandle?
st.st_blksize = 4096;
st.st_gid = st.st_uid = sys_getuid_nt();

// We'll use the "umask" to fake out the mode bits.
uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire);
Expand All @@ -92,9 +128,13 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
break;
case kNtFileTypeChar:
st.st_mode = S_IFCHR | (0666 & ~umask);
st.st_dev = 0x66666666;
st.st_ino = handle;
break;
case kNtFileTypePipe:
st.st_mode = S_IFIFO | (0666 & ~umask);
st.st_dev = 0x55555555;
st.st_ino = handle;
break;
case kNtFileTypeDisk: {
struct NtByHandleFileInformation wst;
Expand Down Expand Up @@ -126,7 +166,6 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
} else {
st.st_ctim = st.st_mtim;
}
st.st_gid = st.st_uid = sys_getuid_nt();
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
st.st_dev = wst.dwVolumeSerialNumber;
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
Expand Down
2 changes: 1 addition & 1 deletion libc/calls/fstat.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int fstat(int fd, struct stat *st) {
} else if (IsMetal()) {
rc = sys_fstat_metal(fd, st);
} else if (IsWindows()) {
rc = sys_fstat_nt(__getfdhandleactual(fd), st);
rc = sys_fstat_nt(fd, st);
} else {
rc = enosys();
}
Expand Down
44 changes: 35 additions & 9 deletions libc/calls/fstatat-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
Expand All @@ -29,18 +28,43 @@
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"

textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
int flags) {
int rc, e;
int64_t fh;
uint32_t dwDesiredAccess;

// handle special files
if (startswith(path, "/dev/")) {
if (!strcmp(path + 5, "tty")) {
return sys_fstat_nt_special(kFdConsole, st);
} else if (!strcmp(path + 5, "null")) {
return sys_fstat_nt_special(kFdDevNull, st);
} else if (!strcmp(path + 5, "stdin")) {
return sys_fstat_nt(STDIN_FILENO, st);
} else if (!strcmp(path + 5, "stdout")) {
return sys_fstat_nt(STDOUT_FILENO, st);
} else if (!strcmp(path + 5, "stderr")) {
return sys_fstat_nt(STDERR_FILENO, st);
} else {
return enoent();
}
}

// convert path from utf-8 to utf-16
uint16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if (__mkntpathat(dirfd, path, 0, path16) == -1) {
return -1;
}

// open an actual file
int rc;
int64_t fh;
int e = errno;
uint32_t dwDesiredAccess = kNtFileGenericRead;
BLOCK_SIGNALS;
e = errno;
dwDesiredAccess = kNtFileGenericRead;
TryAgain:
if ((fh = CreateFile(
path16, dwDesiredAccess,
Expand All @@ -50,7 +74,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
: 0),
0)) != -1) {
rc = st ? sys_fstat_nt(fh, st) : 0;
rc = st ? sys_fstat_nt_handle(fh, st) : 0;
CloseHandle(fh);
} else if (dwDesiredAccess == kNtFileGenericRead &&
GetLastError() == kNtErrorSharingViolation) {
Expand All @@ -61,5 +85,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
rc = __winerr();
}
ALLOW_SIGNALS;

// mop up errors
return __fix_enotdir(rc, path16);
}
3 changes: 3 additions & 0 deletions libc/calls/getloadavg-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
Expand All @@ -36,6 +37,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
int i, rc;
uint64_t elapsed, used;
struct NtFileTime idle, kern, user;
BLOCK_SIGNALS;
pthread_spin_lock(&lock);
if (GetSystemTimes(&idle, &kern, &user)) {
elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1));
Expand All @@ -53,6 +55,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
rc = __winerr();
}
pthread_spin_unlock(&lock);
ALLOW_SIGNALS;
return rc;
}

Expand Down
12 changes: 2 additions & 10 deletions libc/calls/getuid-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,15 @@
#include "libc/intrin/atomic.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"

static textwindows uint32_t __kmp32(const void *buf, size_t size) {
size_t i;
uint32_t h;
const uint32_t kPhiPrime = 0x9e3779b1;
const unsigned char *p = (const unsigned char *)buf;
for (h = i = 0; i < size; i++) h = (p[i] + h) * kPhiPrime;
return h;
}
#include "libc/str/str.h"

textwindows uint32_t sys_getuid_nt(void) {
char16_t buf[257];
static atomic_uint uid;
uint32_t tmp, size = ARRAYLEN(buf);
if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) {
GetUserName(&buf, &size);
tmp = __kmp32(buf, size >> 1) & 32767;
tmp = __fnv(buf, size >> 1) & 32767;
if (!tmp) ++tmp;
atomic_store_explicit(&uid, tmp, memory_order_release);
}
Expand Down
3 changes: 3 additions & 0 deletions libc/calls/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
int bytes = CountConsoleInputBytes();
*arg = MAX(0, bytes);
return 0;
} else if (g_fds.p[fd].kind == kFdDevNull) {
*arg = 1;
return 0;
} else if (GetFileType(handle) == kNtFileTypePipe) {
uint32_t avail;
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {
Expand Down
2 changes: 1 addition & 1 deletion libc/calls/ischardev.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bool32 ischardev(int fd) {
return false;
}
} else {
return __isfdkind(fd, kFdConsole) ||
return __isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdDevNull) ||
(__isfdkind(fd, kFdFile) &&
GetFileType(__getfdhandleactual(fd)) == kNtFileTypeChar);
}
Expand Down
4 changes: 3 additions & 1 deletion libc/calls/lseek-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) {
}

textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
if (__isfdkind(fd, kFdFile)) {
if (__isfdkind(fd, kFdDevNull)) {
return offset;
} else if (__isfdkind(fd, kFdFile)) {
struct Fd *f = g_fds.p + fd;
int filetype = GetFileType(f->handle);
if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) {
Expand Down
20 changes: 0 additions & 20 deletions libc/calls/mkntpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/ntmagicpaths.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
Expand All @@ -36,24 +35,6 @@ static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}

textwindows static const char *FixNtMagicPath(const char *path,
unsigned flags) {
const struct NtMagicPaths *mp = &kNtMagicPaths;
asm("" : "+r"(mp));
if (!IsSlash(path[0])) return path;
if (strcmp(path, mp->devtty) == 0) {
if ((flags & O_ACCMODE) == O_RDONLY) {
return mp->conin;
} else if ((flags & O_ACCMODE) == O_WRONLY) {
return mp->conout;
}
}
if (strcmp(path, mp->devnull) == 0) return mp->nul;
if (strcmp(path, mp->devstdin) == 0) return mp->conin;
if (strcmp(path, mp->devstdout) == 0) return mp->conout;
return path;
}

textwindows size_t __normntpath(char16_t *p, size_t n) {
size_t i, j;
for (j = i = 0; i < n; ++i) {
Expand Down Expand Up @@ -116,7 +97,6 @@ textwindows int __mkntpath2(const char *path,
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
return efault();
}
path = FixNtMagicPath(path, flags);

size_t x, z;
char16_t *p = path16;
Expand Down
7 changes: 0 additions & 7 deletions libc/calls/ntmagicpaths.inc

This file was deleted.

16 changes: 0 additions & 16 deletions libc/calls/ntmagicpaths.internal.h

This file was deleted.

Loading

0 comments on commit 2db2f40

Please sign in to comment.