Add /dev/fd support to Windows

GNU bash needs this functionality, otherwise it can't do <(cmd...).
This commit is contained in:
Justine Tunney 2024-01-06 18:05:14 -08:00
parent 2e5f662dfe
commit b3fb6cff43
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
6 changed files with 113 additions and 6 deletions

View file

@ -33,11 +33,27 @@
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
static int Atoi(const char *str) {
int c;
unsigned x = 0;
if (!*str) return -1;
while ((c = *str++)) {
if ('0' <= c && c <= '9') {
x *= 10;
x += c - '0';
} else {
return -1;
}
}
return x;
}
textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
int flags) {
// handle special files
if (startswith(path, "/dev/")) {
int fd;
if (!strcmp(path + 5, "tty")) {
return sys_fstat_nt_special(kFdConsole, st);
} else if (!strcmp(path + 5, "null")) {
@ -48,6 +64,8 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
return sys_fstat_nt(STDOUT_FILENO, st);
} else if (!strcmp(path + 5, "stderr")) {
return sys_fstat_nt(STDERR_FILENO, st);
} else if (startswith(path + 5, "fd/") && (fd = Atoi(path + 8)) != -1) {
return sys_fstat_nt(fd, st);
} else {
return enoent();
}

View file

@ -181,10 +181,25 @@ static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) {
}
}
static int Atoi(const char *str) {
int c;
unsigned x = 0;
if (!*str) return -1;
while ((c = *str++)) {
if ('0' <= c && c <= '9') {
x *= 10;
x += c - '0';
} else {
return -1;
}
}
return x;
}
textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
int32_t mode) {
int fd;
ssize_t rc;
int fd, oldfd;
BLOCK_SIGNALS;
__fds_lock();
if (!(flags & _O_CREAT)) mode = 0;
@ -200,6 +215,9 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
rc = sys_open_nt_dup(fd, flags, mode, STDOUT_FILENO);
} else if (!strcmp(file + 5, "stderr")) {
rc = sys_open_nt_dup(fd, flags, mode, STDERR_FILENO);
} else if (startswith(file + 5, "fd/") &&
(oldfd = Atoi(file + 8)) != -1) {
rc = sys_open_nt_dup(fd, flags, mode, oldfd);
} else {
rc = enoent();
}

View file

@ -0,0 +1,66 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
}
TEST(devfd, test) {
// TODO: What is up with this mysterious ENOENT error?
// The code appears like it should support this.
if (IsFreebsd()) return;
char buf[8] = {0};
struct stat st[2] = {0};
ASSERT_SYS(0, 0, xbarf("hello.txt", "bone", -1));
ASSERT_SYS(0, 3, open("hello.txt", O_RDONLY));
ASSERT_SYS(0, 4, open("/dev/fd/3", O_RDONLY));
ASSERT_SYS(0, 4, read(4, buf, 7));
ASSERT_STREQ("bone", buf);
ASSERT_SYS(0, 0, fstat(3, st));
ASSERT_SYS(0, 0, fstat(4, st + 1));
ASSERT_EQ(0, memcmp(st, st + 1, sizeof(struct stat)));
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
}
TEST(devfd, not_DEV_FD_STAT_BROKEN) {
// fstat() and stat() are inconsistent on bsd systems
// with xnu it only appears to be st_dev that differs
if (IsBsd()) return;
char buf[8] = {0};
struct stat st[2] = {0};
ASSERT_SYS(0, 0, xbarf("hello.txt", "bone", -1));
ASSERT_SYS(0, 3, open("hello.txt", O_RDONLY));
ASSERT_SYS(0, 4, open("/dev/fd/3", O_RDONLY));
ASSERT_SYS(0, 4, read(4, buf, 7));
ASSERT_STREQ("bone", buf);
ASSERT_SYS(0, 0, fstat(3, st));
ASSERT_SYS(0, 0, stat("/dev/fd/3", st + 1));
ASSERT_EQ(0, memcmp(st, st + 1, sizeof(struct stat)));
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
}

View file

@ -13,3 +13,4 @@ ORIGIN
LOCAL CHANGES
- Force disable mkfifo() code
- Added runtime check for broken BSD /dev/fd stuff

View file

@ -542,7 +542,7 @@
#define HAVE_HASH_BANG_EXEC 1
/* Define if you have the /dev/fd devices to map open files into the file system. */
/* #define HAVE_DEV_FD 1 */
#define HAVE_DEV_FD 1
/* Defined to /dev/fd or /proc/self/fd (linux). */
#define DEV_FD_PREFIX "/dev/fd/"
@ -1160,7 +1160,7 @@
/* #undef GETCWD_BROKEN */
/* #undef DEV_FD_STAT_BROKEN */
#define DEV_FD_STAT_BROKEN
/* An array implementation that prioritizes speed (O(1) access) over space,
in array2.c */

View file

@ -1,3 +1,4 @@
#include "libc/dce.h"
/* eaccess.c - eaccess replacement for the shell, plus other access functions. */
/* Copyright (C) 2006-2020 Free Software Foundation, Inc.
@ -93,7 +94,8 @@ sh_stat (path, finfo)
{
/* If stating /dev/fd/n doesn't produce the same results as fstat of
FD N, then define DEV_FD_STAT_BROKEN */
#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
//#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
if (IsBsd()) {//[jart]
intmax_t fd;
int r;
@ -105,7 +107,8 @@ sh_stat (path, finfo)
}
errno = ENOENT;
return (-1);
#else
//#else
} else {//[jart]
/* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
On most systems, with the notable exception of linux, this is
@ -114,7 +117,8 @@ sh_stat (path, finfo)
strcpy (pbuf, DEV_FD_PREFIX);
strcat (pbuf, path + 8);
return (stat (pbuf, finfo));
#endif /* !HAVE_DEV_FD */
//#endif /* !HAVE_DEV_FD */
}//[jart]
}
#if !defined (HAVE_DEV_STDIN)
else if (STREQN (path, "/dev/std", 8))