summaryrefslogtreecommitdiff
path: root/util/libreboot-utils/lib/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils/lib/file.c')
-rw-r--r--util/libreboot-utils/lib/file.c830
1 files changed, 339 insertions, 491 deletions
diff --git a/util/libreboot-utils/lib/file.c b/util/libreboot-utils/lib/file.c
index 1d2de9b8..805db726 100644
--- a/util/libreboot-utils/lib/file.c
+++ b/util/libreboot-utils/lib/file.c
@@ -7,6 +7,16 @@
* Be nice to the demon.
*/
+/*
+TODO: putting it here just so it's somewhere:
+PATH_MAX is not reliable as a limit for paths,
+because the real length depends on mount point,
+and specific file systems.
+more correct usage example:
+long max = pathconf("/", _PC_PATH_MAX);
+ */
+
+
#include <sys/types.h>
#include <sys/stat.h>
@@ -34,51 +44,31 @@ same_file(int fd, struct stat *st_old,
{
struct stat st;
int saved_errno = errno;
+ int rval = 0;
+ errno = 0;
- /* 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;
+ if (if_err(st_old == NULL, EFAULT) ||
+ if_err(fd < 0, EBADF) ||
+ (rval = fstat(fd, &st)) < 0 ||
+ (rval = fd_verify_regular(fd, st_old, &st)) < 0 ||
+ if_err(check_size && st.st_size != st_old->st_size, ESTALE))
+ return with_fallback_errno(ESTALE);
- errno = saved_errno;
+ reset_caller_errno(rval);
return 0;
-
-err_same_file:
- return set_errno(saved_errno, ESTALE);
}
int
fsync_dir(const char *path)
{
int saved_errno = errno;
-
size_t pathlen = 0;
-
char *dirbuf = NULL;
int dirfd = -1;
-
char *slash = NULL;
struct stat st = {0};
-
- int close_errno;
+ int rval = 0;
+ errno = 0;
if (if_err(slen(path, PATH_MAX, &pathlen) == 0, EINVAL))
goto err_fsync_dir;
@@ -109,26 +99,23 @@ fsync_dir(const char *path)
);
if (if_err_sys(dirfd < 0) ||
- if_err_sys(fstat(dirfd, &st) < 0) ||
+ if_err_sys((rval = fstat(dirfd, &st)) < 0) ||
if_err(!S_ISDIR(st.st_mode), ENOTDIR)
||
- if_err_sys(fsync_on_eintr(dirfd) == -1))
+ if_err_sys((rval = fsync_on_eintr(dirfd)) == -1))
goto err_fsync_dir;
close_on_eintr(&dirfd);
-
free_and_set_null(&dirbuf);
- errno = saved_errno;
+ reset_caller_errno(rval);
return 0;
err_fsync_dir:
-
-
free_and_set_null(&dirbuf);
close_on_eintr(&dirfd);
- return set_errno(saved_errno, EIO);
+ return with_fallback_errno(EIO);
}
/* rw_file_exact() - Read perfectly or die
@@ -150,38 +137,25 @@ err_fsync_dir:
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)
+ off_t off, int rw_type, size_t max_retries, int off_reset)
{
- ssize_t rval;
- ssize_t rc;
-
+ int saved_errno = errno;
+ ssize_t rval = 0;
+ ssize_t rc = 0;
size_t nrw_cur;
-
off_t off_cur;
void *mem_cur;
-
- size_t retries_on_zero;
-
- int saved_errno = errno;
+ size_t retries_on_zero = 0;
errno = 0;
- 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;
+ if (if_err(rval >= 0 && (size_t)rval > (nrw - rc), EOVERFLOW))
goto err_rw_file_exact;
- }
rc += rval;
if ((size_t)rc >= nrw)
@@ -190,227 +164,79 @@ rw_file_exact(int fd, unsigned char *mem, size_t nrw,
mem_cur = (void *)(mem + (size_t)rc);
nrw_cur = (size_t)(nrw - (size_t)rc);
- if (off < 0) {
- errno = EOVERFLOW;
+ if (if_err(off < 0, 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)
+ if ((rval = rw(fd, mem_cur, nrw_cur, off_cur, rw_type)) < 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;
+ if (if_err((size_t)rc != nrw, EIO) ||
+ (rval = rw_over_nrw(rc, nrw)) < 0)
goto err_rw_file_exact;
- }
-
- rval = rw_over_nrw(rc, nrw);
- if (rval < 0)
- goto err_rw_file_exact;
-
- errno = saved_errno;
+ reset_caller_errno(rval);
return rval;
err_rw_file_exact:
-
- return set_errno(saved_errno, EIO);
+ return with_fallback_errno(EIO);
}
-/* prw() - portable read-write with more
+/* rw() - read-write but with more
* safety checks than barebones libc
*
- * 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
- *
- * REAL_POS_IO is enabled by default in common.h
- * and the fallback version was written for fun.
- * You should just use the real one (REAL_POS_IO 1),
- * since it is generally more reliable.
*/
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)
+rw(int fd, void *mem, size_t nrw,
+ off_t off, int rw_type)
{
- ssize_t rval;
- ssize_t r;
- int positional_rw;
+ ssize_t rval = 0;
+ ssize_t r = -1;
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;
errno = 0;
- 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_on_eintr(fd, mem, nrw);
- else if (rw_type == IO_READ)
- r = read_on_eintr(fd, mem, nrw);
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
- else if (rw_type == IO_PWRITE)
- r = pwrite_on_eintr(fd, mem, nrw, off);
- else if (rw_type == IO_PREAD)
- r = pread_on_eintr(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_on_eintr(fd, mem, nrw);
- else if (rw_type == IO_PWRITE)
- r = write_on_eintr(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;
+ if (io_args(fd, mem, nrw, off, rw_type) == -1)
+ return with_fallback_errno(EINVAL);
+
+ switch (rw_type) {
+ case IO_WRITE:
+ r = write_on_eintr(fd, mem, nrw);
+ break;
+ case IO_READ:
+ r = read_on_eintr(fd, mem, nrw);
+ break;
+ case IO_PWRITE:
+ r = pwrite_on_eintr(fd, mem, nrw, off);
+ break;
+ case IO_PREAD:
+ r = pread_on_eintr(fd, mem, nrw, off);
+ break;
+ default:
+ errno = EINVAL;
+ break;
}
- errno = saved_errno;
-
- rval = rw_over_nrw(r, nrw);
- if (rval < 0)
- goto err_prw;
-
- errno = saved_errno;
+ if ((rval = rw_over_nrw(r, nrw)) < 0)
+ return with_fallback_errno(EIO);
+ reset_caller_errno(rval);
return rval;
-
-#endif
-
-err_prw:
- return set_errno(saved_errno, EIO);
}
int
@@ -418,6 +244,7 @@ io_args(int fd, void *mem, size_t nrw,
off_t off, int rw_type)
{
int saved_errno = errno;
+ errno = 0;
if (if_err(mem == NULL, EFAULT) ||
if_err(fd < 0, EBADF) ||
@@ -428,70 +255,46 @@ io_args(int fd, void *mem, size_t nrw,
if_err(rw_type > IO_PWRITE, EINVAL))
goto err_io_args;
- errno = saved_errno;
+ reset_caller_errno(0);
return 0;
err_io_args:
- return set_errno(saved_errno, EINVAL);
+ return with_fallback_errno(EINVAL);
}
int
check_file(int fd, struct stat *st)
{
int saved_errno = errno;
+ int rval = 0;
+ errno = 0;
if (if_err(fd < 0, EBADF) ||
if_err(st == NULL, EFAULT) ||
- if_err(fstat(fd, st) == -1, 0) ||
+ ((rval = fstat(fd, st)) == -1) ||
if_err(!S_ISREG(st->st_mode), EBADF))
goto err_is_file;
- errno = saved_errno;
+ reset_caller_errno(rval);
return 0;
err_is_file:
- return set_errno(saved_errno, EINVAL);
+ return with_fallback_errno(EINVAL);
}
/* POSIX can say whatever it wants.
* specification != implementation
*/
-
ssize_t
rw_over_nrw(ssize_t r, size_t nrw)
{
- int saved_errno = errno;
-
- if (if_err(!nrw, 0) ||
- if_err(r == -1, 0) ||
+ if (if_err(!nrw, EIO) ||
+ (r == -1) ||
if_err((size_t)r > SSIZE_MAX, ERANGE) ||
if_err((size_t)r > nrw, ERANGE))
- goto err_rw_over_nrw;
+ return with_fallback_errno(EIO);
- errno = saved_errno;
return r;
-
-err_rw_over_nrw:
- return set_errno(saved_errno, EIO);
-}
-
-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;
}
/* two functions that reduce sloccount by
@@ -501,10 +304,8 @@ if_err(int condition, int errval)
{
if (!condition)
return 0;
-
if (errval)
errno = errval;
-
return 1;
}
int
@@ -514,102 +315,6 @@ if_err_sys(int condition)
return 0;
return 1;
}
-/* errno can never be -1, so you can
- * use this to conditionally set an integer
- * for comparison. see example in lseek_on_eintr
- */
-int
-try_err(int loop_err, int errval)
-{
- if (loop_err)
- return errval;
- return -1;
-}
-
-void
-open_on_eintr(const char *path,
- int *fd, int flags, mode_t mode,
- struct stat *st)
-{
- int r = -1;
- int saved_errno = errno;
-
- if (path == NULL)
- err_exit(EINVAL, "open_on_eintr: null path");
-
- if (fd == NULL)
- err_exit(EFAULT, "%s: open_on_eintr: null fd ptr", path);
-
- if (*fd >= 0)
- err_exit(EBADF, "%s: open_on_eintr: file already open", path);
-
- do {
- r = open(path, flags, mode);
- } while (r == -1 && (
- errno == EINTR || errno == EAGAIN ||
- errno == EWOULDBLOCK || errno == ETXTBSY));
-
- if (r < 0)
- err_exit(errno, "%s: open_on_eintr: could not close", path);
-
- *fd = r;
-
- if (st != NULL) {
- if (fstat(*fd, st) < 0)
- err_exit(errno, "%s: stat", path);
-
- if (!S_ISREG(st->st_mode))
- err_exit(errno, "%s: not a regular file", path);
- }
-
- if (lseek_on_eintr(*fd, 0, SEEK_CUR, 1, 1) == (off_t)-1)
- err_exit(errno, "%s: file not seekable", path);
-
- errno = saved_errno;
-}
-
-void
-close_on_eintr(int *fd)
-{
- int r;
- int saved_errno = errno;
-
- if (fd == NULL)
- err_exit(EINVAL, "close_on_eintr: null pointer");
-
- if (*fd < 0)
- return;
-
- do {
- r = close(*fd);
- } while (r == -1 && (
- errno == EINTR || errno == EAGAIN ||
- errno == EWOULDBLOCK || errno == ETXTBSY));
-
- if (r < 0)
- err_exit(errno, "close_on_eintr: could not close");
-
- *fd = -1;
-
- errno = saved_errno;
-}
-
-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,
@@ -653,7 +358,7 @@ rootfs(void)
global_fs.rootfd = -1;
- open_on_eintr("/", &global_fs.rootfd,
+ open_file_on_eintr("/", &global_fs.rootfd,
O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0400, NULL);
if (global_fs.rootfd < 0)
@@ -681,6 +386,7 @@ fs_resolve_at(int dirfd, const char *path, int flags)
int saved_errno = errno;
int r;
int is_last;
+ errno = 0;
if (dirfd < 0 || path == NULL || *path == '\0') {
errno = EINVAL;
@@ -711,7 +417,7 @@ fs_resolve_at(int dirfd, const char *path, int flags)
nextfd = -1;
}
- errno = saved_errno;
+ reset_caller_errno(0);
return curfd;
err:
@@ -725,7 +431,7 @@ err:
close_on_eintr(&curfd);
errno = saved_errno;
- return -1;
+ return with_fallback_errno(EIO);
}
/* NOTE:
@@ -765,41 +471,36 @@ fs_next_component(const char **p,
name[len] = '\0';
/* reject . and .. */
- if ((name[0] == '.' && name[1] == '\0') ||
- (name[0] == '.' && name[1] == '.' && name[2] == '\0')) {
- errno = EPERM;
- return -1;
- }
+ if (if_err((name[0] == '.' && name[1] == '\0') ||
+ (name[0] == '.' && name[1] == '.' && name[2] == '\0'), EPERM))
+ goto err;
*p = s + len;
return 1;
+err:
+ return with_fallback_errno(EPERM);
}
int
fs_open_component(int dirfd, const char *name,
int flags, int is_last)
{
+ int saved_errno = errno;
int fd;
struct stat st;
+ errno = 0;
- fd = openat2p(dirfd, name,
+ fd = openat_on_eintr(dirfd, name,
(is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
- if (!is_last) {
-
- if (if_err(fd < 0, EBADF) ||
- if_err_sys(fstat(fd, &st) < 0))
- return -1;
-
- if (!S_ISDIR(st.st_mode)) {
-
- close_on_eintr(&fd);
- errno = ENOTDIR;
- return -1;
- }
- }
+ if (!is_last &&
+ (if_err(fd < 0, EBADF) ||
+ if_err_sys(fstat(fd, &st) < 0) ||
+ if_err(!S_ISDIR(st.st_mode), ENOTDIR)))
+ return with_fallback_errno(EIO);
+ reset_caller_errno(fd);
return fd;
}
@@ -808,13 +509,14 @@ fs_dirname_basename(const char *path,
char **dir, char **base,
int allow_relative)
{
+ int saved_errno = errno;
char *buf = NULL;
char *slash;
size_t len;
- int rval;
+ errno = 0;
if (if_err(path == NULL || dir == NULL || base == NULL, EFAULT))
- return -1;
+ goto err;
slen(path, PATH_MAX, &len);
memcpy(smalloc(&buf, len + 1),
@@ -841,22 +543,75 @@ fs_dirname_basename(const char *path,
sdup(".", PATH_MAX, dir);
*base = buf;
} else {
- errno = EINVAL;
free_and_set_null(&buf);
- return -1;
+ goto err;
}
+ reset_caller_errno(0);
return 0;
+err:
+ return with_fallback_errno(EINVAL);
}
-/* portable wrapper for use of openat2 on linux,
- * with fallback for others e.g. openbsd
+/* TODO: why does this abort, but others
+ e.g. open_file_on_eintr, don't???
*/
+void
+open_file_on_eintr(const char *path,
+ int *fd, int flags, mode_t mode,
+ struct stat *st)
+{
+ int saved_errno = errno;
+ int rval = 0;
+ errno = 0;
+
+ if (path == NULL)
+ err_exit(EINVAL, "open_file_on_eintr: null path");
+ if (fd == NULL)
+ err_exit(EFAULT, "%s: open_file_on_eintr: null fd ptr", path);
+ if (*fd >= 0)
+ err_exit(EBADF,
+ "%s: open_file_on_eintr: file already open", path);
+
+ errno = 0;
+ while (fs_retry(saved_errno,
+ rval = open(path, flags, mode)));
+
+ if (rval < 0)
+ err_exit(errno,
+ "%s: open_file_on_eintr: could not close", path);
+
+ reset_caller_errno(rval);
+ *fd = rval;
+
+ /* we don't care about edge case behaviour here,
+ even if the next operation sets errno on success,
+ because the open() call is our main concern.
+ however, we also must preserve the new errno,
+ assuming it changed above under the same edge case */
+
+ saved_errno = errno;
+
+ if (st != NULL) {
+ if (fstat(*fd, st) < 0)
+ err_exit(errno, "%s: stat", path);
+
+ if (!S_ISREG(st->st_mode))
+ err_exit(errno, "%s: not a regular file", path);
+ }
+
+ if (lseek_on_eintr(*fd, 0, SEEK_CUR, 1, 1) == (off_t)-1)
+ err_exit(errno, "%s: file not seekable", path);
+
+ errno = saved_errno; /* see previous comment */
+}
+
+
+#ifdef __linux__ /* we use openat2 on linux */
int
-openat2p(int dirfd, const char *path,
+openat_on_eintr(int dirfd, const char *path,
int flags, mode_t mode)
{
-#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
@@ -866,37 +621,58 @@ openat2p(int dirfd, const char *path,
RESOLVE_NO_MAGICLINKS
};
int saved_errno = errno;
- int rval;
-#endif
+ long rval = 0;
+ errno = 0;
if (if_err(dirfd < 0, EBADF) ||
if_err(path == NULL, EFAULT))
- return -1;
+ goto err;
-retry:
errno = 0;
+ while (sys_retry(saved_errno,
+ rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how))));
-#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);
+ if (rval == -1) /* avoid long->int UB for -1 */
+ goto err;
+
+ reset_caller_errno(rval);
+ return (int)rval;
+err:
+ return with_fallback_errno(EIO); /* -1 */
+}
+#else /* regular openat on non-linux e.g. openbsd */
+int
+openat_on_eintr(int dirfd, const char *path,
+ int flags, mode_t mode)
+{
+ int saved_errno = errno;
+ int rval = 0;
+ errno = 0;
+
+ if (if_err(dirfd < 0, EBADF) ||
+ if_err(path == NULL, EFAULT))
+ return with_fallback_errno(EIO);
+
+ while (fs_retry(saved_errno,
+ rval = openat(dirfd, path, flags, mode)));
+
+ reset_caller_errno(rval);
+ return rval;
+}
#endif
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
- if (rval >= 0)
- errno = saved_errno;
+off_t
+lseek_on_eintr(int fd, off_t off, int whence,
+ int loop_eagain, int loop_eintr)
+{
+ int saved_errno = errno;
+ off_t rval = 0;
+ errno = 0;
+ while (off_retry(saved_errno,
+ rval = lseek(fd, off, whence)));
+
+ reset_caller_errno(rval);
return rval;
}
@@ -905,26 +681,17 @@ mkdirat_on_eintr(int dirfd,
const char *path, mode_t mode)
{
int saved_errno = errno;
- int rval;
+ int rval = 0;
+ errno = 0;
if (if_err(dirfd < 0, EBADF) ||
if_err(path == NULL, EFAULT))
- return -1;
-
-retry:
- errno = 0;
- rval = mkdirat(dirfd, path, mode);
-
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
+ return with_fallback_errno(EIO);
- if (rval >= 0)
- errno = saved_errno;
+ while (fs_retry(saved_errno,
+ rval = mkdirat(dirfd, path, mode)));
+ reset_caller_errno(rval);
return rval;
}
@@ -933,27 +700,19 @@ read_on_eintr(int fd,
void *buf, size_t count)
{
int saved_errno = errno;
- int rval;
+ ssize_t rval = 0;
+ errno = 0;
if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) ||
if_err(count == 0, EINVAL))
- goto err;
+ return with_fallback_errno(EIO);
-retry:
- errno = 0;
+ while (rw_retry(saved_errno,
+ rval = read(fd, buf, count)));
- if ((rval = read(fd, buf, count)) == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
-
- errno = saved_errno;
+ reset_caller_errno(rval);
return rval;
-err:
- return set_errno(saved_errno, EIO);
}
ssize_t
@@ -962,28 +721,20 @@ pread_on_eintr(int fd,
off_t off)
{
int saved_errno = errno;
- int rval;
+ ssize_t rval = 0;
+ errno = 0;
if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) ||
if_err(off < 0, EFAULT) ||
if_err(count == 0, EINVAL))
- goto err;
-
-retry:
- errno = 0;
+ return with_fallback_errno(EIO);
- if ((rval = pread(fd, buf, count, off)) == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
+ while (rw_retry(saved_errno,
+ rval = pread(fd, buf, count, off)));
- errno = saved_errno;
+ reset_caller_errno(rval);
return rval;
-err:
- return set_errno(saved_errno, EIO);
}
ssize_t
@@ -991,27 +742,19 @@ write_on_eintr(int fd,
void *buf, size_t count)
{
int saved_errno = errno;
- int rval;
+ ssize_t rval = 0;
+ errno = 0;
if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) ||
if_err(count == 0, EINVAL))
- goto err;
-
-retry:
- errno = 0;
+ return with_fallback_errno(EIO);
- if ((rval = write(fd, buf, count)) == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
+ while (rw_retry(saved_errno,
+ rval = write(fd, buf, count)));
- errno = saved_errno;
+ reset_caller_errno(rval);
return rval;
-err:
- return set_errno(saved_errno, EIO);
}
ssize_t
@@ -1020,26 +763,131 @@ pwrite_on_eintr(int fd,
off_t off)
{
int saved_errno = errno;
- int rval;
+ ssize_t rval = 0;
+ errno = 0;
if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) ||
if_err(off < 0, EFAULT) ||
if_err(count == 0, EINVAL))
- goto err;
+ return with_fallback_errno(EIO);
+
+ while (rw_retry(saved_errno,
+ rval = pwrite(fd, buf, count, off)));
-retry:
+ reset_caller_errno(rval);
+ return rval;
+}
+
+int
+fsync_on_eintr(int fd)
+{
+ int saved_errno = errno;
+ int rval = 0;
errno = 0;
- if ((rval = pwrite(fd, buf, count, off)) == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
+ if (if_err(fd < 0, EBADF))
+ return with_fallback_errno(EIO);
- errno = saved_errno;
+ while (fs_retry(saved_errno,
+ rval = fsync(fd)));
+
+ reset_caller_errno(rval);
return rval;
-err:
- return set_errno(saved_errno, EIO);
+}
+
+void
+close_on_eintr(int *fd)
+{
+ int saved_errno = errno;
+ int rval = 0;
+
+ if (fd == NULL)
+ err_exit(EINVAL, "close_on_eintr: null pointer");
+ if (*fd < 0)
+ return;
+
+ errno = 0;
+ while (fs_retry(saved_errno,
+ rval = close(*fd)));
+
+ if (rval < 0)
+ err_exit(errno, "close_on_eintr: could not close");
+
+ *fd = -1;
+
+ reset_caller_errno(rval);
+}
+
+/* unified eintr looping.
+ * differently typed functions
+ * to avoid potential UB
+ *
+ * ONE MACRO TO RULE THEM ALL:
+ */
+#define fs_err_retry() \
+ if ((rval == -1) && \
+ (errno == EINTR)) \
+ return 1; \
+ if (rval >= 0 && !errno) \
+ errno = saved_errno; \
+ return 0
+/*
+ * Regarding the errno logic above:
+ * on success, it is permitted that
+ * a syscall could still set errno.
+ * We reset errno after storingit
+ * for later preservation, in functions
+ * that call *_retry() functions.
+ *
+ * They rely ultimately on this
+ * macro for errno restoration. We
+ * assume therefore that errno was
+ * reset to zero before the retry
+ * loop. If errno is then *set* on
+ * success, we leave it alone. Otherwise,
+ * we restore the caller's saved errno.
+ *
+ * This offers some consistency, while
+ * complying with POSIX specification.
+ */
+
+
+/* retry switch for offset-based
+ * functions e.g. lseek
+ */
+/* retry switch for functions that
+ return long status e.g. linux syscall
+ */
+int
+off_retry(int saved_errno, off_t rval)
+{
+ fs_err_retry();
+}
+
+/* retry switch for functions that
+ return long status e.g. linux syscall
+ */
+int
+sys_retry(int saved_errno, long rval)
+{
+ fs_err_retry();
+}
+
+/* retry switch for functions that
+ return int status e.g. mkdirat
+ */
+int
+fs_retry(int saved_errno, int rval)
+{
+ fs_err_retry();
+}
+
+/* retry switch for functions that
+ return rw count in ssize_t e.g. read()
+ */
+int
+rw_retry(int saved_errno, ssize_t rval)
+{
+ fs_err_retry();
}