Skip to content

Commit b3fb6cf

Browse files
committed
Add /dev/fd support to Windows
GNU bash needs this functionality, otherwise it can't do <(cmd...).
1 parent 2e5f662 commit b3fb6cf

File tree

6 files changed

+113
-6
lines changed

6 files changed

+113
-6
lines changed

libc/calls/fstatat-nt.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,27 @@
3333
#include "libc/sysv/consts/fileno.h"
3434
#include "libc/sysv/errfuns.h"
3535

36+
static int Atoi(const char *str) {
37+
int c;
38+
unsigned x = 0;
39+
if (!*str) return -1;
40+
while ((c = *str++)) {
41+
if ('0' <= c && c <= '9') {
42+
x *= 10;
43+
x += c - '0';
44+
} else {
45+
return -1;
46+
}
47+
}
48+
return x;
49+
}
50+
3651
textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
3752
int flags) {
3853

3954
// handle special files
4055
if (startswith(path, "/dev/")) {
56+
int fd;
4157
if (!strcmp(path + 5, "tty")) {
4258
return sys_fstat_nt_special(kFdConsole, st);
4359
} else if (!strcmp(path + 5, "null")) {
@@ -48,6 +64,8 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
4864
return sys_fstat_nt(STDOUT_FILENO, st);
4965
} else if (!strcmp(path + 5, "stderr")) {
5066
return sys_fstat_nt(STDERR_FILENO, st);
67+
} else if (startswith(path + 5, "fd/") && (fd = Atoi(path + 8)) != -1) {
68+
return sys_fstat_nt(fd, st);
5169
} else {
5270
return enoent();
5371
}

libc/calls/open-nt.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,25 @@ static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) {
181181
}
182182
}
183183

184+
static int Atoi(const char *str) {
185+
int c;
186+
unsigned x = 0;
187+
if (!*str) return -1;
188+
while ((c = *str++)) {
189+
if ('0' <= c && c <= '9') {
190+
x *= 10;
191+
x += c - '0';
192+
} else {
193+
return -1;
194+
}
195+
}
196+
return x;
197+
}
198+
184199
textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
185200
int32_t mode) {
186-
int fd;
187201
ssize_t rc;
202+
int fd, oldfd;
188203
BLOCK_SIGNALS;
189204
__fds_lock();
190205
if (!(flags & _O_CREAT)) mode = 0;
@@ -200,6 +215,9 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
200215
rc = sys_open_nt_dup(fd, flags, mode, STDOUT_FILENO);
201216
} else if (!strcmp(file + 5, "stderr")) {
202217
rc = sys_open_nt_dup(fd, flags, mode, STDERR_FILENO);
218+
} else if (startswith(file + 5, "fd/") &&
219+
(oldfd = Atoi(file + 8)) != -1) {
220+
rc = sys_open_nt_dup(fd, flags, mode, oldfd);
203221
} else {
204222
rc = enoent();
205223
}

test/libc/calls/devfd_test.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2024 Justine Alexandra Roberts Tunney │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/calls/calls.h"
20+
#include "libc/calls/struct/stat.h"
21+
#include "libc/dce.h"
22+
#include "libc/intrin/strace.internal.h"
23+
#include "libc/str/str.h"
24+
#include "libc/sysv/consts/o.h"
25+
#include "libc/testlib/testlib.h"
26+
#include "libc/x/x.h"
27+
28+
void SetUpOnce(void) {
29+
testlib_enable_tmp_setup_teardown();
30+
}
31+
32+
TEST(devfd, test) {
33+
// TODO: What is up with this mysterious ENOENT error?
34+
// The code appears like it should support this.
35+
if (IsFreebsd()) return;
36+
char buf[8] = {0};
37+
struct stat st[2] = {0};
38+
ASSERT_SYS(0, 0, xbarf("hello.txt", "bone", -1));
39+
ASSERT_SYS(0, 3, open("hello.txt", O_RDONLY));
40+
ASSERT_SYS(0, 4, open("/dev/fd/3", O_RDONLY));
41+
ASSERT_SYS(0, 4, read(4, buf, 7));
42+
ASSERT_STREQ("bone", buf);
43+
ASSERT_SYS(0, 0, fstat(3, st));
44+
ASSERT_SYS(0, 0, fstat(4, st + 1));
45+
ASSERT_EQ(0, memcmp(st, st + 1, sizeof(struct stat)));
46+
ASSERT_SYS(0, 0, close(4));
47+
ASSERT_SYS(0, 0, close(3));
48+
}
49+
50+
TEST(devfd, not_DEV_FD_STAT_BROKEN) {
51+
// fstat() and stat() are inconsistent on bsd systems
52+
// with xnu it only appears to be st_dev that differs
53+
if (IsBsd()) return;
54+
char buf[8] = {0};
55+
struct stat st[2] = {0};
56+
ASSERT_SYS(0, 0, xbarf("hello.txt", "bone", -1));
57+
ASSERT_SYS(0, 3, open("hello.txt", O_RDONLY));
58+
ASSERT_SYS(0, 4, open("/dev/fd/3", O_RDONLY));
59+
ASSERT_SYS(0, 4, read(4, buf, 7));
60+
ASSERT_STREQ("bone", buf);
61+
ASSERT_SYS(0, 0, fstat(3, st));
62+
ASSERT_SYS(0, 0, stat("/dev/fd/3", st + 1));
63+
ASSERT_EQ(0, memcmp(st, st + 1, sizeof(struct stat)));
64+
ASSERT_SYS(0, 0, close(4));
65+
ASSERT_SYS(0, 0, close(3));
66+
}

third_party/bash/README.cosmo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ ORIGIN
1313
LOCAL CHANGES
1414

1515
- Force disable mkfifo() code
16+
- Added runtime check for broken BSD /dev/fd stuff

third_party/bash/config.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@
542542
#define HAVE_HASH_BANG_EXEC 1
543543

544544
/* Define if you have the /dev/fd devices to map open files into the file system. */
545-
/* #define HAVE_DEV_FD 1 */
545+
#define HAVE_DEV_FD 1
546546

547547
/* Defined to /dev/fd or /proc/self/fd (linux). */
548548
#define DEV_FD_PREFIX "/dev/fd/"
@@ -1160,7 +1160,7 @@
11601160

11611161
/* #undef GETCWD_BROKEN */
11621162

1163-
/* #undef DEV_FD_STAT_BROKEN */
1163+
#define DEV_FD_STAT_BROKEN
11641164

11651165
/* An array implementation that prioritizes speed (O(1) access) over space,
11661166
in array2.c */

third_party/bash/eaccess.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "libc/dce.h"
12
/* eaccess.c - eaccess replacement for the shell, plus other access functions. */
23

34
/* Copyright (C) 2006-2020 Free Software Foundation, Inc.
@@ -93,7 +94,8 @@ sh_stat (path, finfo)
9394
{
9495
/* If stating /dev/fd/n doesn't produce the same results as fstat of
9596
FD N, then define DEV_FD_STAT_BROKEN */
96-
#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
97+
//#if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
98+
if (IsBsd()) {//[jart]
9799
intmax_t fd;
98100
int r;
99101

@@ -105,7 +107,8 @@ sh_stat (path, finfo)
105107
}
106108
errno = ENOENT;
107109
return (-1);
108-
#else
110+
//#else
111+
} else {//[jart]
109112
/* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
110113
trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
111114
On most systems, with the notable exception of linux, this is
@@ -114,7 +117,8 @@ sh_stat (path, finfo)
114117
strcpy (pbuf, DEV_FD_PREFIX);
115118
strcat (pbuf, path + 8);
116119
return (stat (pbuf, finfo));
117-
#endif /* !HAVE_DEV_FD */
120+
//#endif /* !HAVE_DEV_FD */
121+
}//[jart]
118122
}
119123
#if !defined (HAVE_DEV_STDIN)
120124
else if (STREQN (path, "/dev/std", 8))

0 commit comments

Comments
 (0)