mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
3f0bcdc3ef
- Exhaustively document cancellation points - Rename SIGCANCEL to SIGTHR just like BSDs - Further improve POSIX thread cancellations - Ensure asynchronous cancellations work correctly - Elevate the quality of getrandom() and getentropy() - Make futexes cancel correctly on OpenBSD 6.x and 7.x - Add reboot.com and shutdown.com to examples directory - Remove underscore prefix from awesome timespec_*() APIs - Create assertions that help verify our cancellation points - Remove bad timespec APIs (cmp generalizes eq/ne/gt/gte/lt/lte)
166 lines
7.9 KiB
C
166 lines
7.9 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 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/cp.internal.h"
|
|
#include "libc/calls/internal.h"
|
|
#include "libc/calls/struct/flock.h"
|
|
#include "libc/calls/struct/flock.internal.h"
|
|
#include "libc/calls/syscall-nt.internal.h"
|
|
#include "libc/calls/syscall-sysv.internal.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/intrin/describeflags.internal.h"
|
|
#include "libc/intrin/strace.internal.h"
|
|
#include "libc/intrin/weaken.h"
|
|
#include "libc/sysv/consts/f.h"
|
|
#include "libc/sysv/errfuns.h"
|
|
#include "libc/zipos/zipos.internal.h"
|
|
|
|
/**
|
|
* Does things with file descriptor, e.g.
|
|
*
|
|
* CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC));
|
|
*
|
|
* This function lets you duplicate file descriptors without running
|
|
* into an edge case where they take over stdio handles:
|
|
*
|
|
* CHECK_GE((newfd = fcntl(oldfd, F_DUPFD, 3)), 3);
|
|
* CHECK_GE((newfd = fcntl(oldfd, F_DUPFD_CLOEXEC, 3)), 3);
|
|
*
|
|
* This function implements file record locking, which lets independent
|
|
* processes (and on Linux 3.15+, threads too!) lock arbitrary ranges
|
|
* associated with a file. See `test/libc/calls/lock_test.c` and other
|
|
* locking related tests in that folder.
|
|
*
|
|
* On Windows, the Cosmopolitan Libc polyfill for POSIX advisory locks
|
|
* only implements enough of its nuances to support SQLite's needs. Some
|
|
* possibilities, e.g. punching holes in lock, will raise `ENOTSUP`.
|
|
*
|
|
* @param fd is the file descriptor
|
|
* @param cmd can be one of:
|
|
* - `F_GETFD` gets `FD_CLOEXEC` status of `fd
|
|
* - `F_SETFD` sets `FD_CLOEXEC` status of `arg` file descriptor
|
|
* - `F_GETFL` returns file descriptor status flags
|
|
* - `F_SETFL` sets file descriptor status flags
|
|
* - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3
|
|
* - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd
|
|
* - `F_SETLK` for record locking where `arg` is `struct flock *`
|
|
* - `F_SETLKW` ditto but waits for lock (SQLite avoids this)
|
|
* - `F_GETLK` to retrieve information about a record lock
|
|
* - `F_OFD_SETLK` for better non-blocking lock (Linux 3.15+ only)
|
|
* - `F_OFD_SETLKW` for better blocking lock (Linux 3.15+ only)
|
|
* - `F_OFD_GETLK` for better lock querying (Linux 3.15+ only)
|
|
* - `F_FULLFSYNC` on MacOS for fsync() with release barrier
|
|
* - `F_BARRIERFSYNC` on MacOS for fsync() with even more barriers
|
|
* - `F_SETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
|
* - `F_GETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
|
* - `F_GETPATH` on MacOS and NetBSD where arg is `char[PATH_MAX]`
|
|
* - `F_MAXFD` on NetBSD to get max open file descriptor
|
|
* - `F_NOCACHE` on MacOS to toggle data caching
|
|
* - `F_GETPIPE_SZ` on Linux to get pipe size
|
|
* - `F_SETPIPE_SZ` on Linux to set pipe size
|
|
* - `F_NOTIFY` raise `SIGIO` upon `fd` events in `arg` (Linux only)
|
|
* - `DN_ACCESS` for file access
|
|
* - `DN_MODIFY` for file modifications
|
|
* - `DN_CREATE` for file creations
|
|
* - `DN_DELETE` for file deletions
|
|
* - `DN_RENAME` for file renames
|
|
* - `DN_ATTRIB` for file attribute changes
|
|
* - `DN_MULTISHOT` bitwise or for realtime signals (non-coalesced)
|
|
* @param arg can be FD_CLOEXEC, etc. depending
|
|
* @return 0 on success, or -1 w/ errno
|
|
* @raise EBADF if `fd` isn't a valid open file descriptor
|
|
* @raise EINVAL if `cmd` is unknown or unsupported by os
|
|
* @raise EINVAL if `cmd` is invalid or unsupported by os
|
|
* @raise EPERM if pledge() is in play w/o `stdio` or `flock` promise
|
|
* @raise ENOLCK if `F_SETLKW` would have exceeded `RLIMIT_LOCKS`
|
|
* @raise EPERM if `cmd` is `F_SETOWN` and we weren't authorized
|
|
* @raise ESRCH if `cmd` is `F_SETOWN` and process group not found
|
|
* @raise ENOTSUP on Windows if locking operation isn't supported yet
|
|
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
|
|
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
|
|
* `RLIMIT_NOFILE` would be exceeded
|
|
* @cancellationpoint when `cmd` is `F_SETLKW` or `F_OFD_SETLKW`
|
|
* @asyncsignalsafe
|
|
* @restartable
|
|
*/
|
|
int fcntl(int fd, int cmd, ...) {
|
|
int rc;
|
|
va_list va;
|
|
uintptr_t arg;
|
|
|
|
va_start(va, cmd);
|
|
arg = va_arg(va, uintptr_t);
|
|
va_end(va);
|
|
|
|
if (fd >= 0) {
|
|
if (cmd >= 0) {
|
|
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
|
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
|
} else if (!IsWindows()) {
|
|
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
|
|
BEGIN_CANCELLATION_POINT;
|
|
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
|
|
END_CANCELLATION_POINT;
|
|
} else {
|
|
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
|
|
}
|
|
} else {
|
|
rc = sys_fcntl_nt(fd, cmd, arg);
|
|
}
|
|
} else {
|
|
rc = einval();
|
|
}
|
|
} else {
|
|
rc = ebadf();
|
|
}
|
|
|
|
#ifdef SYSDEBUG
|
|
if (cmd == F_GETFD || //
|
|
cmd == F_GETOWN || //
|
|
cmd == F_FULLFSYNC || //
|
|
cmd == F_BARRIERFSYNC || //
|
|
cmd == F_MAXFD) {
|
|
STRACE("fcntl(%d, %s) → %d% m", fd, DescribeFcntlCmd(cmd), rc);
|
|
} else if (cmd == F_GETFL) {
|
|
STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd),
|
|
DescribeOpenFlags(rc));
|
|
} else if (cmd == F_SETLK || //
|
|
cmd == F_SETLKW || //
|
|
cmd == F_GETLK || //
|
|
((SupportsLinux() || SupportsXnu()) && //
|
|
cmd != -1 && //
|
|
(cmd == F_OFD_SETLK || //
|
|
cmd == F_OFD_SETLKW || //
|
|
cmd == F_OFD_GETLK))) {
|
|
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
|
DescribeFlock(cmd, (struct flock *)arg), rc);
|
|
} else if ((SupportsNetbsd() || SupportsXnu()) && cmd != -1 &&
|
|
cmd == F_GETPATH) {
|
|
STRACE("fcntl(%d, %s, [%s]) → %d% m", fd, DescribeFcntlCmd(cmd),
|
|
!rc ? (const char *)arg : "n/a", rc);
|
|
} else if (SupportsLinux() && cmd != -1 && cmd == F_NOTIFY) {
|
|
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
|
DescribeDnotifyFlags(arg), rc);
|
|
} else {
|
|
STRACE("fcntl(%d, %s, %ld) → %#x% m", fd, DescribeFcntlCmd(cmd), arg, rc);
|
|
}
|
|
#endif
|
|
|
|
return rc;
|
|
}
|