Improve system call documentation

This change also introduces partial faccessat() support for zipos and
makes some slight breaking changes in errno results. close() is fixed
to use `EBADF` rather than `EINVAL` and we're now using `ENOTSUP` not
`EOPNOTSUPP` to indicate that zipos doesn't support a system call yet
This commit is contained in:
Justine Tunney 2022-10-02 07:42:44 -07:00
parent 0b5f84dd20
commit ad97775370
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
18 changed files with 273 additions and 67 deletions

View file

@ -22,9 +22,14 @@
/** /**
* Checks if effective user can access path in particular ways. * Checks if effective user can access path in particular ways.
* *
* This is equivalent to saying:
*
* faccessat(AT_FDCWD, path, mode, 0);
*
* @param path is a filename or directory * @param path is a filename or directory
* @param mode can be R_OK, W_OK, X_OK, F_OK * @param mode can be R_OK, W_OK, X_OK, F_OK
* @return 0 if ok, or -1 and sets errno * @return 0 if ok, or -1 w/ errno
* @see faccessat() for further documentation
* @asyncsignalsafe * @asyncsignalsafe
*/ */
int access(const char *path, int mode) { int access(const char *path, int mode) {

View file

@ -32,17 +32,25 @@
/** /**
* Closes file descriptor. * Closes file descriptor.
* *
* This function may be used for file descriptors returned by functions * This function releases resources returned by functions such as:
* like open, socket, accept, epoll_create, and landlock_create_ruleset.
* *
* This function should never be called twice for the same file * - openat()
* descriptor, regardless of whether or not an error happened. However * - socket()
* that doesn't mean the error should be ignored. * - accept()
* - epoll_create()
* - landlock_create_ruleset()
*
* This function should never be reattempted if an error is returned;
* however, that doesn't mean the error should be ignored. This goes
* against the conventional wisdom of looping on `EINTR`.
* *
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @error EINTR means a signal was received while closing in which case * @raise EINTR if signal was delivered; do *not* retry
* close() does not need to be called again, since the fd will close * @raise EBADF if `fd` is negative or not open; however, an exception
* in the background * is made by Cosmopolitan Libc for `close(-1)` which returns zero
* and does nothing, in order to assist with code that may wish to
* close the same resource multiple times without dirtying `errno`
* @raise EIO if a low-level i/o error occurred
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
@ -51,7 +59,7 @@ int close(int fd) {
if (fd == -1) { if (fd == -1) {
rc = 0; rc = 0;
} else if (fd < 0) { } else if (fd < 0) {
rc = einval(); rc = ebadf();
} else { } else {
// for performance reasons we want to avoid holding __fds_lock() // for performance reasons we want to avoid holding __fds_lock()
// while sys_close() is happening. this leaves the kernel / libc // while sys_close() is happening. this leaves the kernel / libc

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
@ -79,6 +80,7 @@ static void copy_file_range_init(void) {
* @raise EXDEV if source and destination are on different filesystems * @raise EXDEV if source and destination are on different filesystems
* @raise EBADF if `infd` or `outfd` aren't open files or append-only * @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EPERM if `fdout` refers to an immutable file on Linux * @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ENOTSUP if `infd` or `outfd` is a zip file descriptor
* @raise EINVAL if ranges overlap or `flags` is non-zero * @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded * @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
* @raise EFAULT if one of the pointers memory is bad * @raise EFAULT if one of the pointers memory is bad
@ -104,6 +106,8 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
(opt_in_out_outoffset && (opt_in_out_outoffset &&
!__asan_is_valid(opt_in_out_outoffset, 8)))) { !__asan_is_valid(opt_in_out_outoffset, 8)))) {
rc = efault(); rc = efault();
} else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) {
rc = enotsup();
} else { } else {
rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd, rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd,
opt_in_out_outoffset, uptobytes, flags); opt_in_out_outoffset, uptobytes, flags);

View file

@ -21,20 +21,21 @@
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
/** /**
* Creates new file, returning open()'d file descriptor. * Creates file.
* *
* This function is shorthand for: * This is equivalent to saying:
* *
* open(file, O_CREAT | O_WRONLY | O_TRUNC, mode) * int fd = openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
* *
* @param file is a UTF-8 string, which is truncated if it exists * @param file specifies filesystem path to create
* @param mode is an octal user/group/other permission, e.g. 0755 * @param mode is octal bits, e.g. 0644 usually
* @return a number registered with the system to track the open file, * @return file descriptor, or -1 w/ errno
* which must be stored using a 64-bit type in order to support both * @see openat() for further documentation
* System V and Windows, and must be closed later on using close()
* @see open(), touch()
* @asyncsignalsafe * @asyncsignalsafe
* @restartable
* @threadsafe
* @vforksafe
*/ */
dontdiscard int creat(const char *file, uint32_t mode) { int creat(const char *file, uint32_t mode) {
return openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode); return openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
} }

View file

@ -32,15 +32,16 @@
* *
* @param fd remains open afterwards * @param fd remains open afterwards
* @return some arbitrary new number for fd * @return some arbitrary new number for fd
* @raise EOPNOTSUPP if zipos file * @raise EPERM if pledge() is in play without stdio
* @raise EBADF if fd isn't open * @raise ENOTSUP if `fd` is a zip file descriptor
* @raise EBADF if `fd` is negative or not open
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
int dup(int fd) { int dup(int fd) {
int rc; int rc;
if (__isfdkind(fd, kFdZip)) { if (__isfdkind(fd, kFdZip)) {
rc = eopnotsupp(); rc = enotsup();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_dup(fd); rc = sys_dup(fd);
} else { } else {

View file

@ -18,10 +18,10 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
/** /**
@ -38,16 +38,19 @@
* @param newfd if already assigned, is silently closed beforehand; * @param newfd if already assigned, is silently closed beforehand;
* unless it's equal to oldfd, in which case dup2() is a no-op * unless it's equal to oldfd, in which case dup2() is a no-op
* @return new file descriptor, or -1 w/ errno * @return new file descriptor, or -1 w/ errno
* @raise EBADF is oldfd isn't open * @raise EPERM if pledge() is in play without stdio
* @raise EBADF is newfd negative or too big * @raise EMFILE if `RLIMIT_NOFILE` has been reached
* @raise ENOTSUP if `oldfd` is on zip file system
* @raise EINTR if a signal handler was called * @raise EINTR if a signal handler was called
* @raise EBADF is `newfd` negative or too big
* @raise EBADF is `oldfd` isn't open
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
int dup2(int oldfd, int newfd) { int dup2(int oldfd, int newfd) {
int rc; int rc;
if (__isfdkind(oldfd, kFdZip)) { if (__isfdkind(oldfd, kFdZip)) {
rc = eopnotsupp(); rc = enotsup();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_dup2(oldfd, newfd); rc = sys_dup2(oldfd, newfd);
} else if (newfd < 0) { } else if (newfd < 0) {

View file

@ -38,11 +38,13 @@
* @param flags may have O_CLOEXEC which is needed to preserve the * @param flags may have O_CLOEXEC which is needed to preserve the
* close-on-execve() state after file descriptor duplication * close-on-execve() state after file descriptor duplication
* @return newfd on success, or -1 w/ errno * @return newfd on success, or -1 w/ errno
* @raise EINVAL if flags has unsupported bits * @raise ENOTSUP if `oldfd` is a zip file descriptor
* @raise EINVAL if newfd equals oldfd * @raise EPERM if pledge() is in play without stdio
* @raise EBADF is oldfd isn't open * @raise EINVAL if `flags` has unsupported bits
* @raise EBADF is newfd negative or too big
* @raise EINTR if a signal handler was called * @raise EINTR if a signal handler was called
* @raise EBADF is `newfd` negative or too big
* @raise EINVAL if `newfd` equals oldfd
* @raise EBADF is `oldfd` isn't open
* @see dup(), dup2() * @see dup(), dup2()
*/ */
int dup3(int oldfd, int newfd, int flags) { int dup3(int oldfd, int newfd, int flags) {
@ -52,7 +54,7 @@ int dup3(int oldfd, int newfd, int flags) {
} else if (oldfd < 0 || newfd < 0) { } else if (oldfd < 0 || newfd < 0) {
rc = ebadf(); rc = ebadf();
} else if (__isfdkind(oldfd, kFdZip)) { } else if (__isfdkind(oldfd, kFdZip)) {
rc = eopnotsupp(); rc = enotsup();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_dup3(oldfd, newfd, flags); rc = sys_dup3(oldfd, newfd, flags);
} else { } else {

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
@ -35,33 +36,42 @@
* @param dirfd is normally AT_FDCWD but if it's an open directory and * @param dirfd is normally AT_FDCWD but if it's an open directory and
* file is a relative path, then file is opened relative to dirfd * file is a relative path, then file is opened relative to dirfd
* @param path is a filename or directory * @param path is a filename or directory
* @param mode can be R_OK, W_OK, X_OK, F_OK * @param amode can be `R_OK`, `W_OK`, `X_OK`, or `F_OK`
* @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW * @param flags can have `AT_EACCESS` and/or `AT_SYMLINK_NOFOLLOW`
* @note on Linux flags is only supported on Linux 5.8+
* @return 0 if ok, or -1 and sets errno * @return 0 if ok, or -1 and sets errno
* @raise EINVAL if `mode` has bad value
* @raise EPERM if pledge() is in play without rpath promise
* @raise EACCES if access for requested `mode` would be denied
* @raise ENOTDIR if a directory component in `path` exists as non-directory
* @raise ENOENT if component of `path` doesn't exist or `path` is empty
* @raise ENOTSUP if `path` is a zip file and `dirfd` isn't `AT_FDCWD`
* @note on Linux `flags` is only supported on Linux 5.8+
* @asyncsignalsafe * @asyncsignalsafe
*/ */
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) { int faccessat(int dirfd, const char *path, int amode, uint32_t flags) {
int e, rc; int e, rc;
if (IsAsan() && !__asan_is_valid(path, 1)) { struct ZiposUri zipname;
if (!path || (IsAsan() && !__asan_is_valid(path, 1))) {
rc = efault(); rc = efault();
} else if (_weaken(__zipos_notat) && } else if (__isfdkind(dirfd, kFdZip)) {
_weaken(__zipos_notat)(dirfd, path) == -1) { rc = enotsup();
rc = -1; /* TODO(jart): implement me */ } else if (_weaken(__zipos_open) &&
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
rc = _weaken(__zipos_access)(&zipname, amode);
} else if (!IsWindows()) { } else if (!IsWindows()) {
e = errno; e = errno;
if (!flags) goto NoFlags; if (!flags) goto NoFlags;
if ((rc = sys_faccessat2(dirfd, path, mode, flags)) == -1) { if ((rc = sys_faccessat2(dirfd, path, amode, flags)) == -1) {
if (errno == ENOSYS) { if (errno == ENOSYS) {
errno = e; errno = e;
NoFlags: NoFlags:
rc = sys_faccessat(dirfd, path, mode, flags); rc = sys_faccessat(dirfd, path, amode, flags);
} }
} }
} else { } else {
rc = sys_faccessat_nt(dirfd, path, mode, flags); rc = sys_faccessat_nt(dirfd, path, amode, flags);
} }
STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", DescribeDirfd(dirfd), path, STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", DescribeDirfd(dirfd), path,
mode, flags, rc); amode, flags, rc);
return rc; return rc;
} }

View file

@ -37,6 +37,7 @@
* @param path must exist * @param path must exist
* @param mode contains octal flags (base 8) * @param mode contains octal flags (base 8)
* @param flags can have `AT_SYMLINK_NOFOLLOW` * @param flags can have `AT_SYMLINK_NOFOLLOW`
* @raise ENOTSUP if `dirfd` or `path` use zip file system
* @errors ENOENT, ENOTDIR, ENOSYS * @errors ENOENT, ENOTDIR, ENOSYS
* @asyncsignalsafe * @asyncsignalsafe
* @see fchmod() * @see fchmod()
@ -47,7 +48,7 @@ int fchmodat(int dirfd, const char *path, uint32_t mode, int flags) {
rc = efault(); rc = efault();
} else if (_weaken(__zipos_notat) && } else if (_weaken(__zipos_notat) &&
(rc = __zipos_notat(dirfd, path)) == -1) { (rc = __zipos_notat(dirfd, path)) == -1) {
rc = eopnotsupp(); rc = enotsup();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_fchmodat(dirfd, path, mode, flags); rc = sys_fchmodat(dirfd, path, mode, flags);
} else { } else {

View file

@ -34,6 +34,7 @@
* @param gid is group id, or -1 to not change * @param gid is group id, or -1 to not change
* @param flags can have AT_SYMLINK_NOFOLLOW, etc. * @param flags can have AT_SYMLINK_NOFOLLOW, etc.
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @raise ENOTSUP if `dirfd` or `path` use zip file system
* @see chown(), lchown() for shorthand notation * @see chown(), lchown() for shorthand notation
* @see /etc/passwd for user ids * @see /etc/passwd for user ids
* @see /etc/group for group ids * @see /etc/group for group ids
@ -46,7 +47,7 @@ int fchownat(int dirfd, const char *path, uint32_t uid, uint32_t gid,
rc = efault(); rc = efault();
} else if (_weaken(__zipos_notat) && } else if (_weaken(__zipos_notat) &&
(rc = __zipos_notat(dirfd, path)) == -1) { (rc = __zipos_notat(dirfd, path)) == -1) {
STRACE("zipos fchownat not supported yet"); rc = enotsup();
} else { } else {
rc = sys_fchownat(dirfd, path, uid, gid, flags); rc = sys_fchownat(dirfd, path, uid, gid, flags);
} }

View file

@ -24,6 +24,6 @@
textwindows int sys_getsetpriority_nt(int which, unsigned who, int value, textwindows int sys_getsetpriority_nt(int which, unsigned who, int value,
int (*impl)(int)) { int (*impl)(int)) {
if (which != PRIO_PROCESS && which != PRIO_PGRP) return einval(); if (which != PRIO_PROCESS && which != PRIO_PGRP) return einval();
if (who && who != getpid() && who != gettid()) return eopnotsupp(); if (who && who != getpid() && who != gettid()) return esrch();
return impl(value); return impl(value);
} }

View file

@ -22,16 +22,17 @@
/** /**
* Opens file. * Opens file.
* *
* @param file is a UTF-8 string, preferably relative w/ forward slashes * This is equivalent to saying:
* @param flags should be O_RDONLY, O_WRONLY, or O_RDWR, and can be or'd *
* with O_CREAT, O_TRUNC, O_APPEND, O_EXCL, O_CLOEXEC, O_TMPFILE * int fd = openat(AT_FDCWD, file, flags, ...);
* @param mode is an octal user/group/other permission signifier, that's *
* ignored if O_CREAT or O_TMPFILE weren't passed * @param file specifies filesystem path to open
* @return number needing close(), or -1 w/ errno * @return file descriptor, or -1 w/ errno
* @asyncsignalsafe (zip files may have issues) * @see openat() for further documentation
* @vforksafe (raises error if zip file) * @asyncsignalsafe
* @restartable * @restartable
* @threadsafe * @threadsafe
* @vforksafe
*/ */
int open(const char *file, int flags, ...) { int open(const char *file, int flags, ...) {
va_list va; va_list va;

View file

@ -36,17 +36,113 @@
/** /**
* Opens file. * Opens file.
* *
* @param dirfd is normally AT_FDCWD but if it's an open directory and * Here's an example of how a file can be created:
* file is a relative path, then file is opened relative to dirfd *
* @param file is a UTF-8 string, preferably relative w/ forward slashes * int fd = openat(AT_FDCWD, "hi.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
* @param flags should be O_RDONLY, O_WRONLY, or O_RDWR, and can be or'd * write(fd, "hello\n", 6);
* with O_CREAT, O_TRUNC, O_APPEND, O_EXCL, O_CLOEXEC, O_TMPFILE * close(fd);
* @param mode is an octal user/group/other permission signifier, that's *
* ignored if O_CREAT or O_TMPFILE weren't passed * Here's an example of how that file could read back into memory:
* @return number needing close(), or -1 w/ errno *
* @asyncsignalsafe (zip files may have issues) * char data[513] = {0};
* @vforksafe (raises error if zip file) * int fd = openat(AT_FDCWD, "hi.txt", O_RDONLY);
* read(fd, data, 512);
* close(fd);
* assert(!strcmp(data, "hello\n"));
*
* If your main() source file has this statement:
*
* STATIC_YOINK("zip_uri_support");
*
* Then you can read zip assets by adding a `"/zip/..."` prefix to `file`, e.g.
*
* // run `zip program.com hi.txt` beforehand
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
*
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* `file` names a relative path then it's opened relative to `dirfd`
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`,
* which on Windows is bludgeoned into a WIN32 path automatically, e.g.
* - `foo/bar.txt` becomes `foo\bar.txt`
* - `/tmp/...` becomes whatever GetTempPath() is
* - `\\...` or `//...` is passed through to WIN32 unchanged
* - `/c/foo` or `\c\foo` becomes `\\?\c:\foo`
* - `c:/foo` or `c:\foo` becomes `\\?\c:\foo`
* - `/D` becomes `\\?\D:\`
* @param flags must have one of the following under the `O_ACCMODE` bits:
* - `O_RDONLY` to open `file` for reading only
* - `O_WRONLY` to open `file` for writing
* - `O_RDWR` to open `file` for reading and writing
* The following may optionally be bitwise or'd into `flags`:
* - `O_CREAT` create file if it doesn't exist
* - `O_TRUNC` automatic `ftruncate(fd,0)` if exists
* - `O_CLOEXEC` automatic close() upon execve()
* - `O_EXCL` exclusive access (see below)
* - `O_APPEND` open file for appending only
* - `O_EXEC` open file for execution only; see fexecve()
* - `O_NOCTTY` prevents `file` possibly becoming controlling terminal
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
* - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
* - `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
* - `O_DSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_RSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_VERIFY` it's complicated (zero on non-FreeBSD)
* - `O_SHLOCK` it's complicated (zero on non-BSD)
* - `O_EXLOCK` it's complicated (zero on non-BSD)
* - `O_PATH` open only for metadata (Linux 2.6.39+ otherwise zero)
* - `O_NOATIME` don't record access time (zero on non-Linux)
* - `O_RANDOM` hint random access intent (zero on non-Windows)
* - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows)
* - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows)
* - `O_INDEXED` turns on that slow performance (zero on non-Windows)
* - `O_TMPFILE` should not be used; use tmpfd() or tmpfile() instead
* There are three regular combinations for the above flags:
* - `O_RDONLY`: Opens existing file for reading. If it doesn't
* exist then nil is returned and errno will be `ENOENT` (or in
* some other cases `ENOTDIR`).
* - `O_WRONLY|O_CREAT|O_TRUNC`: Creates file. If it already
* exists, then the existing copy is destroyed and the opened
* file will start off with a length of zero. This is the
* behavior of the traditional creat() system call.
* - `O_WRONLY|O_CREAT|O_EXCL`: Create file only if doesn't exist
* already. If it does exist then `nil` is returned along with
* `errno` set to `EEXIST`.
* @param mode is an octal user/group/other permission signifier that's
* ignored if `O_CREAT` isn't passed in `flags`; when creating files
* you'll usually want `mode` to be `0644` which enables global read
* and only permits the owner to write; or when creating executables
* you'll usually want `mode` to be `0755` which is the same, except
* the executable bit is set thrice too
* @return file descriptor (which needs to be close()'d), or -1 w/ errno
* @raise EPERM if pledge() is in play w/o appropriate rpath/wpath/cpath
* @raise EACCES if unveil() is in play and didn't unveil your `file` path
* @raise EACCES if we don't have permission to search a component of `file`
* @raise EACCES if file exists but requested `flags & O_ACCMODE` was denied
* @raise EACCES if file doesn't exist and parent dir lacks write permissions
* @raise EACCES if `O_TRUNC` was specified in `flags` but writing was denied
* @raise ENOTSUP if `file` is on zip file system and `dirfd` isn't `AT_FDCWD`
* @raise ENOTDIR if a directory component in `file` exists as non-directory
* @raise ENOTDIR if `file` is relative and `dirfd` isn't an open directory
* @raise EROFS when writing is requested w/ `file` on read-only filesystem
* @raise ENAMETOOLONG if symlink-resolved `file` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `file` exists longer than `NAME_MAX`
* @raise ENOTSUP if `file` is on zip file system and process is vfork()'d
* @raise ENOSPC if file system is full when `file` would be `O_CREAT`ed
* @raise EINTR if we needed to block and a signal was delivered instead
* @raise ENOENT if `file` doesn't exist when `O_CREAT` isn't in `flags`
* @raise ENOENT if `file` points to a string that's empty
* @raise ENOMEM if insufficient memory was available
* @raise EMFILE if `RLIMIT_NOFILE` has been reached
* @raise EOPNOTSUPP if `file` names a named socket
* @raise ETXTBSY if writing is requested on `file` that's being executed
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `file` is a symbolic link
* @raise ELOOP if a loop was detected resolving components of `file`
* @raise EISDIR if writing is requested and `file` names a directory
* @asyncsignalsafe
* @restartable
* @threadsafe * @threadsafe
* @vforksafe
*/ */
int openat(int dirfd, const char *file, int flags, ...) { int openat(int dirfd, const char *file, int flags, ...) {
int rc; int rc;
@ -63,7 +159,7 @@ int openat(int dirfd, const char *file, int flags, ...) {
if (!__vforked && dirfd == AT_FDCWD) { if (!__vforked && dirfd == AT_FDCWD) {
rc = _weaken(__zipos_open)(&zipname, flags, mode); rc = _weaken(__zipos_open)(&zipname, flags, mode);
} else { } else {
rc = eopnotsupp(); /* TODO */ rc = enotsup(); /* TODO */
} }
} else if (!IsWindows() && !IsMetal()) { } else if (!IsWindows() && !IsMetal()) {
rc = sys_openat(dirfd, file, flags, mode); rc = sys_openat(dirfd, file, flags, mode);
@ -73,7 +169,7 @@ int openat(int dirfd, const char *file, int flags, ...) {
rc = sys_open_nt(dirfd, file, flags, mode); rc = sys_open_nt(dirfd, file, flags, mode);
} }
} else { } else {
rc = eopnotsupp(); /* TODO */ rc = enotsup(); /* TODO */
} }
} else { } else {
rc = efault(); rc = efault();

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
@ -64,6 +65,7 @@ static void splice_init(void) {
* used as both an input and output parameter for pwrite() behavior * used as both an input and output parameter for pwrite() behavior
* @return number of bytes transferred, 0 on input end, or -1 w/ errno * @return number of bytes transferred, 0 on input end, or -1 w/ errno
* @raise EBADF if `infd` or `outfd` aren't open files or append-only * @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise ENOTSUP if `infd` or `outfd` is a zip file descriptor
* @raise ESPIPE if an offset arg was specified for a pipe fd * @raise ESPIPE if an offset arg was specified for a pipe fd
* @raise EINVAL if offset was given for non-seekable device * @raise EINVAL if offset was given for non-seekable device
* @raise EINVAL if file system doesn't support splice() * @raise EINVAL if file system doesn't support splice()
@ -85,6 +87,8 @@ ssize_t splice(int infd, int64_t *opt_in_out_inoffset, int outfd,
(opt_in_out_outoffset && (opt_in_out_outoffset &&
!__asan_is_valid(opt_in_out_outoffset, 8)))) { !__asan_is_valid(opt_in_out_outoffset, 8)))) {
rc = efault(); rc = efault();
} else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) {
rc = enotsup();
} else { } else {
rc = sys_splice(infd, opt_in_out_inoffset, outfd, opt_in_out_outoffset, rc = sys_splice(infd, opt_in_out_inoffset, outfd, opt_in_out_outoffset,
uptobytes, flags); uptobytes, flags);

67
libc/zipos/access.c Normal file
View file

@ -0,0 +1,67 @@
/*-*- 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 2022 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/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.internal.h"
// TODO: this should check parent directory components
/**
* Checks access metadata in αcτµαlly pδrταblε εxεcµταblε object store.
*
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
*/
int __zipos_access(const struct ZiposUri *name, int amode) {
ssize_t cf;
int rc, mode;
struct Zipos *z;
if ((z = __zipos_get()) && (cf = __zipos_find(z, name)) != -1) {
mode = GetZipCfileMode(z->map + cf);
if (amode == F_OK) {
rc = 0;
} else if (amode == R_OK) {
if (mode & 0444) {
rc = 0;
} else {
rc = eacces();
}
} else if (amode == W_OK) {
if (mode & 0222) {
rc = 0;
} else {
rc = eacces();
}
} else if (amode == X_OK) {
if (mode & 0111) {
rc = 0;
} else {
rc = eacces();
}
} else {
rc = einval();
}
} else {
rc = enoent();
}
return rc;
}

View file

@ -27,6 +27,7 @@
.yoink __zipos_close .yoink __zipos_close
.yoink __zipos_fcntl .yoink __zipos_fcntl
.yoink __zipos_fstat .yoink __zipos_fstat
.yoink __zipos_access
.yoink __zipos_lseek .yoink __zipos_lseek
.yoink __zipos_open .yoink __zipos_open
.yoink __zipos_parseuri .yoink __zipos_parseuri

View file

View file

@ -37,6 +37,7 @@ void __zipos_free(struct Zipos *, struct ZiposHandle *) hidden;
ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden; ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden;
ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *); ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *);
int __zipos_open(const struct ZiposUri *, unsigned, int) hidden; int __zipos_open(const struct ZiposUri *, unsigned, int) hidden;
int __zipos_access(const struct ZiposUri *, int) hidden;
int __zipos_stat(const struct ZiposUri *, struct stat *) hidden; int __zipos_stat(const struct ZiposUri *, struct stat *) hidden;
int __zipos_fstat(const struct ZiposHandle *, struct stat *) hidden; int __zipos_fstat(const struct ZiposHandle *, struct stat *) hidden;
int __zipos_stat_impl(struct Zipos *, size_t, struct stat *) hidden; int __zipos_stat_impl(struct Zipos *, size_t, struct stat *) hidden;