summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib/file.c')
-rw-r--r--util/nvmutil/lib/file.c1136
1 files changed, 0 insertions, 1136 deletions
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
deleted file mode 100644
index ea2bcd0b..00000000
--- a/util/nvmutil/lib/file.c
+++ /dev/null
@@ -1,1136 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Pathless i/o, and some stuff you probably never saw.
- * Be nice to the demon.
- */
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* for openat2: */
-#ifdef __linux__
-#include <linux/openat2.h>
-#include <sys/syscall.h>
-#endif
-
-#include "../include/common.h"
-
-/* check that a file changed
- */
-
-int
-same_file(int fd, struct stat *st_old,
- int check_size)
-{
- struct stat st;
- int saved_errno = errno;
-
- /* TODO: null/-1 checks
- * like this can be
- * generalised
- */
- if (st_old == NULL) {
- errno = EFAULT;
- goto err_same_file;
- }
- if (fd < 0) {
- errno = EBADF;
- goto err_same_file;
- }
-
- if (fstat(fd, &st) == -1)
- goto err_same_file;
-
- if (fd_verify_regular(fd, st_old, &st) < 0)
- goto err_same_file;
-
- if (check_size &&
- st.st_size != st_old->st_size)
- goto err_same_file;
-
- errno = saved_errno;
- return 0;
-
-err_same_file:
-
- if (errno == saved_errno)
- errno = ESTALE;
-
- return -1;
-}
-
-/* open() but with abort traps
- */
-/* TODO: also support other things here than files.
- and then use, throughout the program.
- in particular, use of openat might help
- (split the path)
- (see: link attack mitigations throughout nvmutil)
-
- make it return, and handle the return value/errno
-
- (this could return e.g. EINTR)
-
- TODO: this function is not used by mkhtemp, nor will
- it probably be, it's currently used by nvmutil,
- for opening intel gbe nvm config files. i can
- probably remove it though and unify witth some
- of the verification code now used for mkhtemp
-
-TODO: and don't abort. return -1. and handle in the caller.
-
-minor obstacle: the mkhtemp code always requires absolute
-paths, whereas the gbe editor takes relative paths.
- */
-void
-xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
-{
- if ((*fd_ptr = open(path, flags)) < 0)
- err(errno, "%s", path);
-
- if (fstat(*fd_ptr, st) < 0)
- err(errno, "%s: stat", path);
-
- if (!S_ISREG(st->st_mode))
- err(errno, "%s: not a regular file", path);
-
- if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1)
- err(errno, "%s: file not seekable", path);
-}
-
-/* fsync() the directory of a file,
- * useful for atomic writes
- */
-
-int
-fsync_dir(const char *path)
-{
- int saved_errno = errno;
-
- size_t pathlen = 0;
- size_t maxlen = 0;
-
- char *dirbuf = NULL;
- int dirfd = -1;
-
- char *slash = NULL;
- struct stat st = {0};
-
- int close_errno;
-
-#if defined(PATH_LEN) && \
- (PATH_LEN) >= 256
- maxlen = PATH_LEN;
-#else
- maxlen = 4096;
-#endif
-
- if (path == NULL) {
- errno = EFAULT;
- goto err_fsync_dir;
- }
-
- if (slen(path, maxlen, &pathlen) < 0)
- goto err_fsync_dir;
-
- if (pathlen >= maxlen || pathlen < 0) {
- errno = EMSGSIZE;
- goto err_fsync_dir;
- }
-
- if (pathlen == 0)
- {
- errno = EINVAL;
- goto err_fsync_dir;
- }
-
- dirbuf = malloc(pathlen + 1);
- if (dirbuf == NULL) {
-
- errno = ENOMEM;
- goto err_fsync_dir;
- }
-
- memcpy(dirbuf, path, pathlen + 1);
- slash = strrchr(dirbuf, '/');
-
- if (slash != NULL) {
- *slash = '\0';
- if (*dirbuf == '\0') {
- dirbuf[0] = '/';
- dirbuf[1] = '\0';
- }
- } else {
- dirbuf[0] = '.';
- dirbuf[1] = '\0';
- }
-
- dirfd = fs_open(dirbuf,
- O_RDONLY | O_CLOEXEC | O_NOCTTY
-#ifdef O_DIRECTORY
- | O_DIRECTORY
-#endif
-#ifdef O_NOFOLLOW
- | O_NOFOLLOW
-#endif
-);
- if (dirfd < 0)
- goto err_fsync_dir;
-
- if (fstat(dirfd, &st) < 0)
- goto err_fsync_dir;
-
- if (!S_ISDIR(st.st_mode)) {
-
- errno = ENOTDIR;
- goto err_fsync_dir;
- }
-
- /* sync file on disk */
- if (fsync_on_eintr(dirfd) == -1)
- goto err_fsync_dir;
-
- if (close_on_eintr(dirfd) == -1) {
-
- dirfd = -1;
- goto err_fsync_dir;
- }
-
- if (dirbuf != NULL) {
-
- free(dirbuf);
- dirbuf = NULL;
- }
-
- dirbuf = NULL;
-
- errno = saved_errno;
- return 0;
-
-err_fsync_dir:
-
- if (errno == saved_errno)
- errno = EIO;
-
- if (dirbuf != NULL) {
-
- free(dirbuf);
- dirbuf = NULL;
- }
-
- if (dirfd >= 0) {
-
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
- dirfd = -1;
- }
-
- return -1;
-}
-
-/*
- * Safe I/O functions wrapping around
- * read(), write() and providing a portable
- * analog of both pread() and pwrite().
- * These functions are designed for maximum
- * robustness, checking NULL inputs, overflowed
- * outputs, and all kinds of errors that the
- * standard libc functions don't.
- *
- * Looping on EINTR and EAGAIN is supported.
- * EINTR/EAGAIN looping is done indefinitely.
- */
-
-/* rw_file_exact() - Read perfectly or die
- *
- * Read/write, and absolutely insist on an
- * absolute read; e.g. if 100 bytes are
- * requested, this MUST return 100.
- *
- * This function will never return zero.
- * It will only return below (error),
- * or above (success). On error, -1 is
- * returned and errno is set accordingly.
- *
- * Zero-byte returns are not allowed.
- * It will re-spin a finite number of
- * times upon zero-return, to recover,
- * otherwise it will return an error.
- */
-
-ssize_t
-rw_file_exact(int fd, unsigned char *mem, size_t nrw,
- off_t off, int rw_type, int loop_eagain,
- int loop_eintr, size_t max_retries,
- int off_reset)
-{
- ssize_t rval;
- ssize_t rc;
-
- size_t nrw_cur;
-
- off_t off_cur;
- void *mem_cur;
-
- size_t retries_on_zero;
-
- int saved_errno = errno;
-
- rval = 0;
-
- rc = 0;
- retries_on_zero = 0;
-
- if (io_args(fd, mem, nrw, off, rw_type) == -1)
- goto err_rw_file_exact;
-
- while (1) {
-
- /* Prevent theoretical overflow */
- if (rval >= 0 && (size_t)rval > (nrw - rc)) {
- errno = EOVERFLOW;
- goto err_rw_file_exact;
- }
-
- rc += rval;
- if ((size_t)rc >= nrw)
- break;
-
- mem_cur = (void *)(mem + (size_t)rc);
- nrw_cur = (size_t)(nrw - (size_t)rc);
-
- if (off < 0) {
- errno = EOVERFLOW;
- goto err_rw_file_exact;
- }
-
- off_cur = off + (off_t)rc;
-
- rval = prw(fd, mem_cur, nrw_cur, off_cur,
- rw_type, loop_eagain, loop_eintr,
- off_reset);
-
- if (rval < 0)
- goto err_rw_file_exact;
-
- if (rval == 0) {
- if (retries_on_zero++ < max_retries)
- continue;
-
- errno = EIO;
- goto err_rw_file_exact;
- }
-
- retries_on_zero = 0;
- }
-
- if ((size_t)rc != nrw) {
-
- errno = EIO;
- goto err_rw_file_exact;
- }
-
- rval = rw_over_nrw(rc, nrw);
- if (rval < 0)
- goto err_rw_file_exact;
-
- errno = saved_errno;
-
- return rval;
-
-err_rw_file_exact:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-/* prw() - portable read-write with more
- * safety checks than barebones libc
- *
- * portable pwrite/pread on request, or real
- * pwrite/pread libc functions can be used.
- * the portable (non-libc) pread/pwrite is not
- * thread-safe, because it does not prevent or
- * mitigate race conditions on file descriptors
- *
- * If you need real pwrite/pread, just compile
- * with flag: REAL_POS_IO=1
- *
- * A fallback is provided for regular read/write.
- * rw_type can be IO_READ (read), IO_WRITE (write),
- * IO_PREAD (pread) or IO_PWRITE
- *
- * loop_eagain does a retry loop on EAGAIN if set
- * loop_eintr does a retry loop on EINTR if set
- *
- * race conditions on non-libc pread/pwrite:
- * if a file offset changes, abort, to mitage.
- *
- * off_reset 1: reset the file offset *once* if
- * a change was detected, assuming
- * nothing else is touching it now
- * off_reset 0: never reset if changed
- */
-
-ssize_t
-prw(int fd, void *mem, size_t nrw,
- off_t off, int rw_type,
- int loop_eagain, int loop_eintr,
- int off_reset)
-{
- ssize_t rval;
- ssize_t r;
- int positional_rw;
- struct stat st;
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
- off_t verified;
- off_t off_orig;
- off_t off_last;
-#endif
- int saved_errno = errno;
-
- if (io_args(fd, mem, nrw, off, rw_type)
- == -1)
- goto err_prw;
-
- r = -1;
-
- /* do not use loop_eagain on
- * normal files
- */
-
- if (!loop_eagain) {
- /* check whether the file
- * changed
- */
-
- if (check_file(fd, &st) == -1)
- goto err_prw;
- }
-
- if (rw_type >= IO_PREAD)
- positional_rw = 1; /* pread/pwrite */
- else
- positional_rw = 0; /* read/write */
-
-try_rw_again:
-
- if (!positional_rw) {
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
-real_pread_pwrite:
-#endif
- if (rw_type == IO_WRITE)
- r = write(fd, mem, nrw);
- else if (rw_type == IO_READ)
- r = read(fd, mem, nrw);
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
- else if (rw_type == IO_PWRITE)
- r = pwrite(fd, mem, nrw, off);
- else if (rw_type == IO_PREAD)
- r = pread(fd, mem, nrw, off);
-#endif
-
- if (r == -1 && (errno == try_err(loop_eintr, EINTR)
- || errno == try_err(loop_eagain, EAGAIN)))
- goto try_rw_again;
-
- rval = rw_over_nrw(r, nrw);
- if (rval < 0)
- goto err_prw;
-
- errno = saved_errno;
-
- return rval;
- }
-
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
- goto real_pread_pwrite;
-#else
- if ((off_orig = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr)) == (off_t)-1) {
- r = -1;
- } else if (lseek_on_eintr(fd, off, SEEK_SET,
- loop_eagain, loop_eintr) == (off_t)-1) {
- r = -1;
- } else {
- verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr);
-
- /* abort if the offset changed,
- * indicating race condition. if
- * off_reset enabled, reset *ONCE*
- */
-
- if (off_reset && off != verified)
- lseek_on_eintr(fd, off, SEEK_SET,
- loop_eagain, loop_eintr);
-
- do {
- /* check offset again, repeatedly.
- * even if off_reset is set, this
- * aborts if offsets change again
- */
-
- verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr);
-
- if (off != verified) {
-
- errno = EBUSY;
- goto err_prw;
- }
-
- if (rw_type == IO_PREAD)
- r = read(fd, mem, nrw);
- else if (rw_type == IO_PWRITE)
- r = write(fd, mem, nrw);
-
- if (rw_over_nrw(r, nrw) == -1)
- break;
-
- } while (r == -1 &&
- (errno == try_err(loop_eintr, EINTR) ||
- errno == try_err(loop_eagain, EAGAIN)));
- }
-
- saved_errno = errno;
-
- off_last = lseek_on_eintr(fd, off_orig, SEEK_SET,
- loop_eagain, loop_eintr);
-
- if (off_last != off_orig) {
- errno = saved_errno;
- goto err_prw;
- }
-
- errno = saved_errno;
-
- rval = rw_over_nrw(r, nrw);
- if (rval < 0)
- goto err_prw;
-
- errno = saved_errno;
-
- return rval;
-
-#endif
-
-err_prw:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-int
-io_args(int fd, void *mem, size_t nrw,
- off_t off, int rw_type)
-{
- int saved_errno = errno;
-
- /* obviously */
- if (mem == NULL) {
-
- errno = EFAULT;
- goto err_io_args;
- }
-
- /* uninitialised fd */
- if (fd < 0) {
-
- errno = EBADF;
- goto err_io_args;
- }
-
- /* negative offset */
- if (off < 0) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- /* prevent zero-byte rw */
- if (!nrw)
- goto err_io_args;
-
- /* prevent overflow */
- if (nrw > (size_t)SSIZE_MAX) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- /* prevent overflow */
- if (((size_t)off + nrw) < (size_t)off) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- if (rw_type > IO_PWRITE) {
-
- errno = EINVAL;
- goto err_io_args;
- }
-
- errno = saved_errno;
-
- return 0;
-
-err_io_args:
-
- if (errno == saved_errno)
- errno = EINVAL;
-
- return -1;
-}
-
-int
-check_file(int fd, struct stat *st)
-{
- int saved_errno = errno;
-
- if (fd < 0) {
- errno = EBADF;
- goto err_is_file;
- }
-
- if (st == NULL) {
- errno = EFAULT;
- goto err_is_file;
- }
-
- if (fstat(fd, st) == -1)
- goto err_is_file;
-
- if (!S_ISREG(st->st_mode)) {
-
- errno = EBADF;
- goto err_is_file;
- }
-
- errno = saved_errno;
-
- return 0;
-
-err_is_file:
-
- if (errno == saved_errno)
- errno = EINVAL;
-
- return -1;
-}
-
-/* POSIX can say whatever it wants.
- * specification != implementation
- */
-
-ssize_t
-rw_over_nrw(ssize_t r, size_t nrw)
-{
- int saved_errno = errno;
-
- /* not a libc bug, but we
- * don't like the number zero
- */
- if (!nrw)
- goto err_rw_over_nrw;
-
- if (r == -1)
- return r;
-
- if ((size_t)
- r > SSIZE_MAX) {
-
- /* Theoretical buggy libc
- * check. Extremely academic.
- *
- * Specifications never
- * allow this return value
- * to exceed SSIZE_T, but
- * spec != implementation
- *
- * Check this after using
- * [p]read() or [p]write()
- *
- * NOTE: here, we assume
- * ssize_t integers are the
- * same size as SSIZE_T
- */
-
- errno = ERANGE;
- goto err_rw_over_nrw;
- }
-
- /* Theoretical buggy libc:
- * Should never return a number of
- * bytes above the requested length.
- */
- if ((size_t)r > nrw) {
-
- errno = ERANGE;
- goto err_rw_over_nrw;
- }
-
- errno = saved_errno;
-
- return r;
-
-err_rw_over_nrw:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
-off_t
-lseek_on_eintr(int fd, off_t off, int whence,
- int loop_eagain, int loop_eintr)
-{
- off_t old;
-
- old = -1;
-
- do {
- old = lseek(fd, off, whence);
- } while (old == (off_t)-1 && (
- errno == try_err(loop_eintr, EINTR) ||
- errno == try_err(loop_eintr, ETXTBSY) ||
- errno == try_err(loop_eagain, EAGAIN) ||
- errno == try_err(loop_eagain, EWOULDBLOCK)));
-
- return old;
-}
-#endif
-
-int
-try_err(int loop_err, int errval)
-{
- if (loop_err)
- return errval;
-
- return -1;
-}
-
-int
-close_on_eintr(int fd)
-{
- int r;
- int saved_errno = errno;
-
- do {
- r = close(fd);
- } while (r == -1 && (
- errno == EINTR || errno == EAGAIN ||
- errno == EWOULDBLOCK || errno == ETXTBSY));
-
- if (r >= 0)
- errno = saved_errno;
-
- return r;
-}
-
-int
-fsync_on_eintr(int fd)
-{
- int r;
- int saved_errno = errno;
-
- do {
- r = fsync(fd);
- } while (r == -1 && (errno == EINTR || errno == EAGAIN ||
- errno == ETXTBSY || errno == EWOULDBLOCK));
-
- if (r >= 0)
- errno = saved_errno;
-
- return r;
-}
-
-int
-fs_rename_at(int olddirfd, const char *old,
- int newdirfd, const char *new)
-{
- if (new == NULL || old == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- if (olddirfd < 0 || newdirfd < 0) {
-
- errno = EBADF;
- return -1;
- }
-
- return renameat(olddirfd, old, newdirfd, new);
-}
-
-/* secure open, based on
- * relative path to root
- *
- * always a fixed fd for /
- * see: rootfs()
- */
-int
-fs_open(const char *path, int flags)
-{
- struct filesystem *fs;
- const char *rel;
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
- if (path[0] != '/') {
- errno = EINVAL;
- return -1;
- }
-
- fs = rootfs();
- if (fs == NULL)
- return -1;
-
- rel = path + 1;
-
- return fs_resolve_at(fs->rootfd, rel, flags);
-}
-
-/* singleton function
- * that returns a fixed
- * descriptor of /
- *
- * used throughout, for
- * repeated integrity checks
- */
-struct filesystem *
-rootfs(void)
-{
- static struct filesystem global_fs;
- static int fs_initialised = 0;
-
- if (!fs_initialised) {
-
- global_fs.rootfd =
- open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
-
- if (global_fs.rootfd < 0)
- return NULL;
-
- fs_initialised = 1;
- }
-
- return &global_fs;
-}
-
-/* filesystem sandboxing.
- * (in userspace)
- */
-int
-fs_resolve_at(int dirfd, const char *path, int flags)
-{
- int nextfd = -1;
- int curfd;
- const char *p;
- char name[256];
- int saved_errno = errno;
- int r;
- int is_last;
-
- if (dirfd < 0 || path == NULL || *path == '\0') {
- errno = EINVAL;
- return -1;
- }
-
- p = path;
- curfd = dirfd; /* start here */
-
- for (;;) {
- r = fs_next_component(&p, name, sizeof(name));
- if (r < 0)
- goto err;
- if (r == 0)
- break;
-
- is_last = (*p == '\0');
-
- nextfd = fs_open_component(curfd, name, flags, is_last);
- if (nextfd < 0)
- goto err;
-
- /* close previous fd IF it is not the original input */
- if (curfd != dirfd) {
- (void) close_on_eintr(curfd);
- }
-
- curfd = nextfd;
- nextfd = -1;
- }
-
- errno = saved_errno;
- return curfd;
-
-err:
- saved_errno = errno;
-
- if (nextfd >= 0)
- (void) close_on_eintr(nextfd);
-
- /* close curfd only if it's not the original */
- if (curfd != dirfd && curfd >= 0)
- (void) close_on_eintr(curfd);
-
- errno = saved_errno;
- return -1;
-}
-
-int
-fs_next_component(const char **p,
- char *name, size_t namesz)
-{
- const char *s = *p;
- size_t len = 0;
-#if defined(PATH_LEN) && \
-(PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
-
- while (*s == '/')
- s++;
-
- if (*s == '\0') {
- *p = s;
- return 0;
- }
-
- while (s[len] != '/' && s[len] != '\0')
- len++;
-
- if (len == 0 || len >= namesz ||
- len >= maxlen) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- memcpy(name, s, len);
- name[len] = '\0';
-
- /* reject . and .. */
- if ((name[0] == '.' && name[1] == '\0') ||
- (name[0] == '.' && name[1] == '.' && name[2] == '\0')) {
- errno = EPERM;
- return -1;
- }
-
- *p = s + len;
- return 1;
-}
-
-int
-fs_open_component(int dirfd, const char *name,
- int flags, int is_last)
-{
- int fd;
- struct stat st;
-
- fd = openat2p(dirfd, name,
- (is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
- O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
-
- /* the patient always lies
- */
- if (!is_last) {
-
- if (fd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (fstat(fd, &st) < 0)
- return -1;
-
- if (!S_ISDIR(st.st_mode)) {
-
- (void) close_on_eintr(fd);
- errno = ENOTDIR;
- return -1;
- }
- }
-
- return fd;
-}
-
-int
-fs_dirname_basename(const char *path,
- char **dir, char **base,
- int allow_relative)
-{
- char *buf;
- char *slash;
- size_t len;
- int rval;
-#if defined(PATH_LEN) && \
-(PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
-
- if (path == NULL || dir == NULL || base == NULL)
- return -1;
-
- if (slen(path, maxlen, &len) < 0)
- return -1;
-
- buf = malloc(len + 1);
- if (buf == NULL)
- return -1;
-
- memcpy(buf, path, len + 1);
-
- /* strip trailing slashes */
- while (len > 1 && buf[len - 1] == '/')
- buf[--len] = '\0';
-
- slash = strrchr(buf, '/');
-
- if (slash) {
-
- *slash = '\0';
- *dir = buf;
- *base = slash + 1;
-
- if (**dir == '\0') {
- (*dir)[0] = '/';
- (*dir)[1] = '\0';
- }
- } else if (allow_relative) {
-
- *dir = strdup(".");
- *base = buf;
- } else {
- errno = EINVAL;
- free(buf);
- return -1;
- }
-
- return 0;
-}
-
-/* portable wrapper for use of openat2 on linux,
- * with fallback for others e.g. openbsd
- *
- * BONUS: arg checks
- * TODO: consider EINTR/EAGAIN retry loop
- */
-int
-openat2p(int dirfd, const char *path,
- int flags, mode_t mode)
-{
-#ifdef __linux__
- struct open_how how = {
- .flags = flags,
- .mode = mode,
- .resolve =
- RESOLVE_BENEATH |
- RESOLVE_NO_SYMLINKS |
- RESOLVE_NO_MAGICLINKS
- };
- int saved_errno = errno;
- int rval;
-#endif
-
- if (dirfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-retry:
- errno = 0;
-
-#ifdef __linux__
- /* more secure than regular openat,
- * but linux-only at the time of writing
- */
- rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
-#else
- /* less secure, but e.g. openbsd
- * doesn't have openat2 yet
- */
- rval = openat(dirfd, path, flags, mode);
-#endif
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
-
- if (rval >= 0)
- errno = saved_errno;
-
- return rval;
-}
-
-int
-mkdirat_on_eintr( /* <-- say that 10 times to please the demon */
- int dirfd,
- const char *path, mode_t mode)
-{
- int saved_errno = errno;
- int rval;
-
- if (dirfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-retry:
- errno = 0;
- rval = mkdirat(dirfd, path, mode);
-
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
-
- if (rval >= 0)
- errno = saved_errno;
-
- return rval;
-}