diff --git a/libc/calls/access.c b/libc/calls/access.c index 1c0dabc74..5feb8cc04 100644 --- a/libc/calls/access.c +++ b/libc/calls/access.c @@ -22,9 +22,14 @@ /** * 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 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 */ int access(const char *path, int mode) { diff --git a/libc/calls/close.c b/libc/calls/close.c index 4008aa78a..4aa2dbca8 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -32,17 +32,25 @@ /** * Closes file descriptor. * - * This function may be used for file descriptors returned by functions - * like open, socket, accept, epoll_create, and landlock_create_ruleset. + * This function releases resources returned by functions such as: * - * This function should never be called twice for the same file - * descriptor, regardless of whether or not an error happened. However - * that doesn't mean the error should be ignored. + * - openat() + * - socket() + * - 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 - * @error EINTR means a signal was received while closing in which case - * close() does not need to be called again, since the fd will close - * in the background + * @raise EINTR if signal was delivered; do *not* retry + * @raise EBADF if `fd` is negative or not open; however, an exception + * 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 * @vforksafe */ @@ -51,7 +59,7 @@ int close(int fd) { if (fd == -1) { rc = 0; } else if (fd < 0) { - rc = einval(); + rc = ebadf(); } else { // for performance reasons we want to avoid holding __fds_lock() // while sys_close() is happening. this leaves the kernel / libc diff --git a/libc/calls/copy_file_range.c b/libc/calls/copy_file_range.c index 708df3977..b5ce0ff76 100644 --- a/libc/calls/copy_file_range.c +++ b/libc/calls/copy_file_range.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.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 EBADF if `infd` or `outfd` aren't open files or append-only * @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 EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded * @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 && !__asan_is_valid(opt_in_out_outoffset, 8)))) { rc = efault(); + } else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) { + rc = enotsup(); } else { rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd, opt_in_out_outoffset, uptobytes, flags); diff --git a/libc/calls/creat.c b/libc/calls/creat.c index 46a3d7442..e6a8b4ab8 100644 --- a/libc/calls/creat.c +++ b/libc/calls/creat.c @@ -21,20 +21,21 @@ #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 mode is an octal user/group/other permission, e.g. 0755 - * @return a number registered with the system to track the open file, - * which must be stored using a 64-bit type in order to support both - * System V and Windows, and must be closed later on using close() - * @see open(), touch() + * @param file specifies filesystem path to create + * @param mode is octal bits, e.g. 0644 usually + * @return file descriptor, or -1 w/ errno + * @see openat() for further documentation * @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); } diff --git a/libc/calls/dup.c b/libc/calls/dup.c index fa85500a9..c56f4c9b2 100644 --- a/libc/calls/dup.c +++ b/libc/calls/dup.c @@ -32,15 +32,16 @@ * * @param fd remains open afterwards * @return some arbitrary new number for fd - * @raise EOPNOTSUPP if zipos file - * @raise EBADF if fd isn't open + * @raise EPERM if pledge() is in play without stdio + * @raise ENOTSUP if `fd` is a zip file descriptor + * @raise EBADF if `fd` is negative or not open * @asyncsignalsafe * @vforksafe */ int dup(int fd) { int rc; if (__isfdkind(fd, kFdZip)) { - rc = eopnotsupp(); + rc = enotsup(); } else if (!IsWindows()) { rc = sys_dup(fd); } else { diff --git a/libc/calls/dup2.c b/libc/calls/dup2.c index 636ab6be3..a2496f710 100644 --- a/libc/calls/dup2.c +++ b/libc/calls/dup2.c @@ -18,10 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/intrin/strace.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/errfuns.h" /** @@ -38,16 +38,19 @@ * @param newfd if already assigned, is silently closed beforehand; * unless it's equal to oldfd, in which case dup2() is a no-op * @return new file descriptor, or -1 w/ errno - * @raise EBADF is oldfd isn't open - * @raise EBADF is newfd negative or too big + * @raise EPERM if pledge() is in play without stdio + * @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 EBADF is `newfd` negative or too big + * @raise EBADF is `oldfd` isn't open * @asyncsignalsafe * @vforksafe */ int dup2(int oldfd, int newfd) { int rc; if (__isfdkind(oldfd, kFdZip)) { - rc = eopnotsupp(); + rc = enotsup(); } else if (!IsWindows()) { rc = sys_dup2(oldfd, newfd); } else if (newfd < 0) { diff --git a/libc/calls/dup3.c b/libc/calls/dup3.c index c06b175d7..a4d9413e0 100644 --- a/libc/calls/dup3.c +++ b/libc/calls/dup3.c @@ -38,11 +38,13 @@ * @param flags may have O_CLOEXEC which is needed to preserve the * close-on-execve() state after file descriptor duplication * @return newfd on success, or -1 w/ errno - * @raise EINVAL if flags has unsupported bits - * @raise EINVAL if newfd equals oldfd - * @raise EBADF is oldfd isn't open - * @raise EBADF is newfd negative or too big + * @raise ENOTSUP if `oldfd` is a zip file descriptor + * @raise EPERM if pledge() is in play without stdio + * @raise EINVAL if `flags` has unsupported bits * @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() */ 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) { rc = ebadf(); } else if (__isfdkind(oldfd, kFdZip)) { - rc = eopnotsupp(); + rc = enotsup(); } else if (!IsWindows()) { rc = sys_dup3(oldfd, newfd, flags); } else { diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 5b0ff4599..2f8d0d9c7 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" @@ -35,33 +36,42 @@ * @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 * @param path is a filename or directory - * @param mode can be R_OK, W_OK, X_OK, F_OK - * @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW - * @note on Linux flags is only supported on Linux 5.8+ + * @param amode can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` + * @param flags can have `AT_EACCESS` and/or `AT_SYMLINK_NOFOLLOW` * @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 */ -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; - if (IsAsan() && !__asan_is_valid(path, 1)) { + struct ZiposUri zipname; + if (!path || (IsAsan() && !__asan_is_valid(path, 1))) { rc = efault(); - } else if (_weaken(__zipos_notat) && - _weaken(__zipos_notat)(dirfd, path) == -1) { - rc = -1; /* TODO(jart): implement me */ + } else if (__isfdkind(dirfd, kFdZip)) { + rc = enotsup(); + } else if (_weaken(__zipos_open) && + _weaken(__zipos_parseuri)(path, &zipname) != -1) { + rc = _weaken(__zipos_access)(&zipname, amode); } else if (!IsWindows()) { e = errno; 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) { errno = e; NoFlags: - rc = sys_faccessat(dirfd, path, mode, flags); + rc = sys_faccessat(dirfd, path, amode, flags); } } } 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, - mode, flags, rc); + amode, flags, rc); return rc; } diff --git a/libc/calls/fchmodat.c b/libc/calls/fchmodat.c index a9effc2d2..f02fd76c1 100644 --- a/libc/calls/fchmodat.c +++ b/libc/calls/fchmodat.c @@ -37,6 +37,7 @@ * @param path must exist * @param mode contains octal flags (base 8) * @param flags can have `AT_SYMLINK_NOFOLLOW` + * @raise ENOTSUP if `dirfd` or `path` use zip file system * @errors ENOENT, ENOTDIR, ENOSYS * @asyncsignalsafe * @see fchmod() @@ -47,7 +48,7 @@ int fchmodat(int dirfd, const char *path, uint32_t mode, int flags) { rc = efault(); } else if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) { - rc = eopnotsupp(); + rc = enotsup(); } else if (!IsWindows()) { rc = sys_fchmodat(dirfd, path, mode, flags); } else { diff --git a/libc/calls/fchownat.c b/libc/calls/fchownat.c index 26eb892c4..e56c05c7c 100644 --- a/libc/calls/fchownat.c +++ b/libc/calls/fchownat.c @@ -34,6 +34,7 @@ * @param gid is group id, or -1 to not change * @param flags can have AT_SYMLINK_NOFOLLOW, etc. * @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 /etc/passwd for user 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(); } else if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) { - STRACE("zipos fchownat not supported yet"); + rc = enotsup(); } else { rc = sys_fchownat(dirfd, path, uid, gid, flags); } diff --git a/libc/calls/getsetpriority-nt.c b/libc/calls/getsetpriority-nt.c index 5b4d81d9b..188394cf9 100644 --- a/libc/calls/getsetpriority-nt.c +++ b/libc/calls/getsetpriority-nt.c @@ -24,6 +24,6 @@ textwindows int sys_getsetpriority_nt(int which, unsigned who, int value, int (*impl)(int)) { 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); } diff --git a/libc/calls/open.c b/libc/calls/open.c index 670946cd0..48262b7d8 100644 --- a/libc/calls/open.c +++ b/libc/calls/open.c @@ -22,16 +22,17 @@ /** * Opens file. * - * @param file is a UTF-8 string, preferably relative w/ forward slashes - * @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 - * @param mode is an octal user/group/other permission signifier, that's - * ignored if O_CREAT or O_TMPFILE weren't passed - * @return number needing close(), or -1 w/ errno - * @asyncsignalsafe (zip files may have issues) - * @vforksafe (raises error if zip file) + * This is equivalent to saying: + * + * int fd = openat(AT_FDCWD, file, flags, ...); + * + * @param file specifies filesystem path to open + * @return file descriptor, or -1 w/ errno + * @see openat() for further documentation + * @asyncsignalsafe * @restartable * @threadsafe + * @vforksafe */ int open(const char *file, int flags, ...) { va_list va; diff --git a/libc/calls/openat.c b/libc/calls/openat.c index aef4d2b02..3637da916 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -36,17 +36,113 @@ /** * Opens file. * - * @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 - * @param file is a UTF-8 string, preferably relative w/ forward slashes - * @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 - * @param mode is an octal user/group/other permission signifier, that's - * ignored if O_CREAT or O_TMPFILE weren't passed - * @return number needing close(), or -1 w/ errno - * @asyncsignalsafe (zip files may have issues) - * @vforksafe (raises error if zip file) + * Here's an example of how a file can be created: + * + * int fd = openat(AT_FDCWD, "hi.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644); + * write(fd, "hello\n", 6); + * close(fd); + * + * Here's an example of how that file could read back into memory: + * + * char data[513] = {0}; + * 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 + * @vforksafe */ int openat(int dirfd, const char *file, int flags, ...) { int rc; @@ -63,7 +159,7 @@ int openat(int dirfd, const char *file, int flags, ...) { if (!__vforked && dirfd == AT_FDCWD) { rc = _weaken(__zipos_open)(&zipname, flags, mode); } else { - rc = eopnotsupp(); /* TODO */ + rc = enotsup(); /* TODO */ } } else if (!IsWindows() && !IsMetal()) { 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); } } else { - rc = eopnotsupp(); /* TODO */ + rc = enotsup(); /* TODO */ } } else { rc = efault(); diff --git a/libc/calls/splice.c b/libc/calls/splice.c index 4f7d6a39a..244d44230 100644 --- a/libc/calls/splice.c +++ b/libc/calls/splice.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.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 * @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 ENOTSUP if `infd` or `outfd` is a zip file descriptor * @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 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 && !__asan_is_valid(opt_in_out_outoffset, 8)))) { rc = efault(); + } else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) { + rc = enotsup(); } else { rc = sys_splice(infd, opt_in_out_inoffset, outfd, opt_in_out_outoffset, uptobytes, flags); diff --git a/libc/zipos/access.c b/libc/zipos/access.c new file mode 100644 index 000000000..d0e5bab5c --- /dev/null +++ b/libc/zipos/access.c @@ -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; +} diff --git a/libc/zipos/zipos.S b/libc/zipos/zipos.S index 5103002d9..acb603e11 100644 --- a/libc/zipos/zipos.S +++ b/libc/zipos/zipos.S @@ -27,6 +27,7 @@ .yoink __zipos_close .yoink __zipos_fcntl .yoink __zipos_fstat + .yoink __zipos_access .yoink __zipos_lseek .yoink __zipos_open .yoink __zipos_parseuri diff --git a/libc/zipos/zipos.h b/libc/zipos/zipos.h deleted file mode 100755 index e69de29bb..000000000 diff --git a/libc/zipos/zipos.internal.h b/libc/zipos/zipos.internal.h index 764916634..ef7bd7ebe 100644 --- a/libc/zipos/zipos.internal.h +++ b/libc/zipos/zipos.internal.h @@ -37,6 +37,7 @@ void __zipos_free(struct Zipos *, struct ZiposHandle *) hidden; ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden; ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *); 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_fstat(const struct ZiposHandle *, struct stat *) hidden; int __zipos_stat_impl(struct Zipos *, size_t, struct stat *) hidden;