Skip to content

Commit c7a8cd2

Browse files
committed
Improve system call wrappers
This change improves copy_file_range(), sendfile(), splice(), openpty(), closefrom(), close_range(), fadvise() and posix_fadvise() in addition to writing tests that confirm things like errno and seeking behavior across platforms. We now less aggressively polyfill behavior with some of these functions when the platform support isn't available. Please see: https://justine.lol/cosmopolitan/functions.html
1 parent 224c12f commit c7a8cd2

File tree

89 files changed

+1151
-414
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+1151
-414
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ o/$(MODE): \
9494
libc/disclaimer.inc \
9595
rx:build/bootstrap \
9696
rx:o/third_party/gcc \
97-
/proc/self/status \
97+
/proc/stat \
9898
rw:/dev/null \
9999
w:o/stack.log \
100100
/etc/hosts \

examples/script.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
#include "libc/errno.h"
4141
#include "libc/fmt/conv.h"
4242
#include "libc/intrin/bswap.h"
43-
#include "libc/intrin/kprintf.h"
4443
#include "libc/log/bsd.h"
4544
#include "libc/macros.internal.h"
4645
#include "libc/mem/mem.h"

libc/calls/_ptsname.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2022 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/syscall-sysv.internal.h"
20+
#include "libc/calls/syscall_support-sysv.internal.h"
21+
#include "libc/dce.h"
22+
#include "libc/errno.h"
23+
#include "libc/fmt/itoa.h"
24+
#include "libc/str/str.h"
25+
#include "libc/sysv/consts/termios.h"
26+
#include "libc/sysv/errfuns.h"
27+
28+
extern const unsigned FIODGNAME;
29+
extern const unsigned TIOCPTSNAME;
30+
extern const unsigned TIOCPTYGNAME;
31+
32+
struct fiodgname_arg {
33+
int len;
34+
void *buf;
35+
};
36+
37+
struct ptmget {
38+
int cfd;
39+
int sfd;
40+
char cn[1024];
41+
char sn[1024];
42+
};
43+
44+
int _ptsname(int fd, char *buf, size_t size) {
45+
int pty;
46+
size_t n;
47+
struct ptmget t;
48+
49+
if (_isptmaster(fd)) {
50+
return -1;
51+
}
52+
53+
t.sn[0] = '/';
54+
t.sn[1] = 'd';
55+
t.sn[2] = 'e';
56+
t.sn[3] = 'v';
57+
t.sn[4] = '/';
58+
t.sn[5] = 0;
59+
60+
if (IsLinux()) {
61+
if (sys_ioctl(fd, TIOCGPTN, &pty)) return -1;
62+
t.sn[5] = 'p';
63+
t.sn[6] = 't';
64+
t.sn[7] = 's';
65+
t.sn[8] = '/';
66+
FormatInt32(t.sn + 9, pty);
67+
} else if (IsXnu()) {
68+
if (sys_ioctl(fd, TIOCPTYGNAME, t.sn)) {
69+
return -1;
70+
}
71+
} else if (IsFreebsd()) {
72+
struct fiodgname_arg fgn = {sizeof(t.sn) - 5, t.sn + 5};
73+
if (sys_ioctl(fd, FIODGNAME, &fgn) == -1) {
74+
if (errno == EINVAL) {
75+
errno = ERANGE;
76+
}
77+
return -1;
78+
}
79+
} else if (IsNetbsd()) {
80+
if (sys_ioctl(fd, TIOCPTSNAME, &t)) {
81+
return -1;
82+
}
83+
} else {
84+
return enosys();
85+
}
86+
87+
if ((n = strlen(t.sn)) < size) {
88+
memcpy(buf, t.sn, n + 1);
89+
return 0;
90+
} else {
91+
return erange();
92+
}
93+
}

libc/calls/calls.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: private \
9797

9898
# we always want -O3 because:
9999
# it makes the code size smaller too
100+
o/$(MODE)/libc/calls/termios2host.o \
100101
o/$(MODE)/libc/calls/sigenter-freebsd.o \
101102
o/$(MODE)/libc/calls/sigenter-netbsd.o \
102103
o/$(MODE)/libc/calls/sigenter-openbsd.o \

libc/calls/close_range.c

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
20-
#include "libc/intrin/strace.internal.h"
2120
#include "libc/calls/syscall-sysv.internal.h"
22-
#include "libc/errno.h"
23-
#include "libc/limits.h"
21+
#include "libc/dce.h"
22+
#include "libc/intrin/strace.internal.h"
23+
#include "libc/sysv/errfuns.h"
2424

2525
/**
2626
* Closes inclusive range of file descriptors, e.g.
@@ -32,33 +32,27 @@
3232
* }
3333
* }
3434
*
35-
* This is supported on Linux 5.9+, FreeBSD, and OpenBSD. On FreeBSD,
36-
* `flags` must be zero. On OpenBSD, we call closefrom(int) so `last`
37-
* should be `-1` in order to get OpenBSD support, otherwise `ENOSYS`
38-
* will be returned. We also polyfill closefrom on FreeBSD since it's
39-
* available on older kernels.
35+
* The following flags are available:
4036
*
41-
* On Linux, the following flags are supported:
37+
* - `CLOSE_RANGE_UNSHARE` (Linux-only)
38+
* - `CLOSE_RANGE_CLOEXEC` (Linux-only)
4239
*
43-
* - CLOSE_RANGE_UNSHARE
44-
* - CLOSE_RANGE_CLOEXEC
40+
* This is only supported on Linux 5.9+ and FreeBSD 13+. Consider using
41+
* closefrom() which will work on OpenBSD too.
4542
*
4643
* @return 0 on success, or -1 w/ errno
47-
* @error ENOSYS if not Linux 5.9+ / FreeBSD / OpenBSD
48-
* @error EBADF on OpenBSD if `first` is greater than highest fd
4944
* @error EINVAL if flags are bad or first is greater than last
5045
* @error EMFILE if a weird race condition happens on Linux
51-
* @error EINTR possibly on OpenBSD
46+
* @error ENOSYS if not Linux 5.9+ or FreeBSD 13+
5247
* @error ENOMEM on Linux maybe
48+
* @see closefrom()
5349
*/
5450
int close_range(unsigned int first, unsigned int last, unsigned int flags) {
55-
int rc, err;
56-
err = errno;
57-
if ((rc = sys_close_range(first, last, flags)) == -1) {
58-
if (errno == ENOSYS && first <= INT_MAX && last == UINT_MAX && !flags) {
59-
errno = err;
60-
rc = sys_closefrom(first);
61-
}
51+
int rc;
52+
if (IsLinux() || IsFreebsd()) {
53+
rc = sys_close_range(first, last, flags);
54+
} else {
55+
rc = enosys();
6256
}
6357
STRACE("close_range(%d, %d, %#x) → %d% m", first, last, flags, rc);
6458
return rc;

libc/calls/closefrom.c

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
20-
#include "libc/intrin/strace.internal.h"
2120
#include "libc/calls/syscall-sysv.internal.h"
21+
#include "libc/dce.h"
2222
#include "libc/errno.h"
23+
#include "libc/intrin/strace.internal.h"
2324
#include "libc/limits.h"
2425
#include "libc/sysv/errfuns.h"
2526

@@ -34,26 +35,27 @@
3435
* }
3536
*
3637
* @return 0 on success, or -1 w/ errno
37-
* @error ENOSYS if not Linux 5.9+ / FreeBSD / OpenBSD
38+
* @error EBADF if `first` is negative
3839
* @error EBADF on OpenBSD if `first` is greater than highest fd
3940
* @error EINVAL if flags are bad or first is greater than last
4041
* @error EMFILE if a weird race condition happens on Linux
42+
* @error ENOSYS if not Linux 5.9+, FreeBSD 8+, or OpenBSD
4143
* @error EINTR possibly on OpenBSD
4244
* @error ENOMEM on Linux maybe
43-
* @note supported on Linux 5.9+, FreeBSD 8+, and OpenBSD
4445
*/
4546
int closefrom(int first) {
4647
int rc, err;
47-
if (first >= 0) {
48-
err = errno;
49-
if ((rc = sys_close_range(first, -1, 0)) == -1) {
50-
if (errno == ENOSYS) {
51-
errno = err;
52-
rc = sys_closefrom(first);
53-
}
54-
}
55-
} else {
48+
if (IsNetbsd() || IsWindows() || IsMetal()) {
49+
rc = enosys();
50+
} else if (first < 0) {
51+
// consistent with openbsd
52+
// freebsd allows this but it's dangerous
53+
// necessary on linux due to type signature
5654
rc = ebadf();
55+
} else if (IsLinux()) {
56+
rc = sys_close_range(first, 0xffffffffu, 0);
57+
} else {
58+
rc = sys_closefrom(first);
5759
}
5860
STRACE("closefrom(%d) → %d% m", first, rc);
5961
return rc;

libc/calls/copy_file_range.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2022 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/sigset.h"
21+
#include "libc/calls/struct/sigset.internal.h"
22+
#include "libc/calls/syscall-sysv.internal.h"
23+
#include "libc/dce.h"
24+
#include "libc/errno.h"
25+
#include "libc/intrin/asan.internal.h"
26+
#include "libc/intrin/describeflags.internal.h"
27+
#include "libc/intrin/strace.internal.h"
28+
#include "libc/sysv/consts/sig.h"
29+
#include "libc/sysv/errfuns.h"
30+
#include "libc/thread/thread.h"
31+
32+
static struct CopyFileRange {
33+
pthread_once_t once;
34+
bool ok;
35+
} g_copy_file_range;
36+
37+
static bool HasCopyFileRange(void) {
38+
bool ok;
39+
int e, rc;
40+
e = errno;
41+
if (IsLinux()) {
42+
// We modernize our detection by a few years for simplicity.
43+
// This system call is chosen since it's listed by pledge().
44+
// https://www.cygwin.com/bugzilla/show_bug.cgi?id=26338
45+
ok = sys_close_range(-1, -2, 0) == -1 && errno == EINVAL;
46+
} else if (IsFreebsd()) {
47+
ok = sys_copy_file_range(-1, 0, -1, 0, 0, 0) == -1 && errno == EBADF;
48+
} else {
49+
ok = false;
50+
}
51+
errno = e;
52+
return ok;
53+
}
54+
55+
static void copy_file_range_init(void) {
56+
g_copy_file_range.ok = HasCopyFileRange();
57+
}
58+
59+
/**
60+
* Transfers data between files.
61+
*
62+
* If this system call is available (Linux c. 2018 or FreeBSD c. 2021)
63+
* and the file system supports it (e.g. ext4) and the source and dest
64+
* files are on the same file system, then this system call shall make
65+
* copies go about 2x faster.
66+
*
67+
* This implementation requires Linux 5.9+ even though the system call
68+
* was introduced in Linux 4.5. That's to ensure ENOSYS works reliably
69+
* due to a faulty backport, that happened in RHEL7. FreeBSD detection
70+
* on the other hand will work fine.
71+
*
72+
* @param infd is source file, which should be on same file system
73+
* @param opt_in_out_inoffset may be specified for pread() behavior
74+
* @param outfd should be a writable file, but not `O_APPEND`
75+
* @param opt_in_out_outoffset may be specified for pwrite() behavior
76+
* @param uptobytes is maximum number of bytes to transfer
77+
* @param flags is reserved for future use and must be zero
78+
* @return number of bytes transferred, or -1 w/ errno
79+
* @raise EXDEV if source and destination are on different filesystems
80+
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
81+
* @raise EPERM if `fdout` refers to an immutable file on Linux
82+
* @raise EINVAL if ranges overlap or `flags` is non-zero
83+
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
84+
* @raise EFAULT if one of the pointers memory is bad
85+
* @raise ERANGE if overflow happens computing ranges
86+
* @raise ENOSPC if file system has run out of space
87+
* @raise ETXTBSY if source or dest is a swap file
88+
* @raise EINTR if a signal was delivered instead
89+
* @raise EISDIR if source or dest is a directory
90+
* @raise ENOSYS if not Linux 5.9+ or FreeBSD 13+
91+
* @raise EIO if a low-level i/o error happens
92+
* @see sendfile() for seekable → socket
93+
* @see splice() for fd ↔ pipe
94+
*/
95+
ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
96+
int64_t *opt_in_out_outoffset, size_t uptobytes,
97+
uint32_t flags) {
98+
ssize_t rc;
99+
pthread_once(&g_copy_file_range.once, copy_file_range_init);
100+
if (!g_copy_file_range.ok) {
101+
rc = enosys();
102+
} else if (IsAsan() && ((opt_in_out_inoffset &&
103+
!__asan_is_valid(opt_in_out_inoffset, 8)) ||
104+
(opt_in_out_outoffset &&
105+
!__asan_is_valid(opt_in_out_outoffset, 8)))) {
106+
rc = efault();
107+
} else {
108+
rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd,
109+
opt_in_out_outoffset, uptobytes, flags);
110+
}
111+
STRACE("copy_file_range(%d, %s, %d, %s, %'zu, %#x) → %'ld% m", infd,
112+
DescribeInOutInt64(rc, opt_in_out_inoffset), outfd,
113+
DescribeInOutInt64(rc, opt_in_out_outoffset), uptobytes, flags);
114+
return rc;
115+
}

libc/calls/fadvise-nt.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "libc/calls/syscall_support-nt.internal.h"
2222
#include "libc/nt/createfile.h"
2323
#include "libc/nt/enum/fileflagandattributes.h"
24+
#include "libc/nt/enum/filetype.h"
2425
#include "libc/nt/files.h"
2526
#include "libc/nt/runtime.h"
2627
#include "libc/sysv/consts/madv.h"
@@ -33,6 +34,7 @@ textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
3334
int rc, flags, mode;
3435
uint32_t perm, share, attr;
3536

37+
if ((int64_t)len < 0) return einval();
3638
if (!__isfdkind(fd, kFdFile)) return ebadf();
3739
h1 = g_fds.p[fd].handle;
3840
mode = g_fds.p[fd].mode;
@@ -57,6 +59,10 @@ textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
5759
return -1;
5860
}
5961

62+
if (GetFileType(h1) == kNtFileTypePipe) {
63+
return espipe();
64+
}
65+
6066
// MSDN says only these are allowed, otherwise it returns EINVAL.
6167
attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
6268
kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |

0 commit comments

Comments
 (0)