diff options
Diffstat (limited to 'util/nvmutil/lib/file.c')
| -rw-r--r-- | util/nvmutil/lib/file.c | 890 |
1 files changed, 0 insertions, 890 deletions
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c deleted file mode 100644 index b4925ccd..00000000 --- a/util/nvmutil/lib/file.c +++ /dev/null @@ -1,890 +0,0 @@ -/* SPDX-License-Identifier: MIT - * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> - */ - -#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> - -#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; - - if (st_old == NULL || fd < 0) - goto err_same_file; - - if (fstat(fd, &st) == -1) - return -1; - - if (st.st_dev != st_old->st_dev || - st.st_ino != st_old->st_ino || - !S_ISREG(st.st_mode)) - 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: - - errno = EIO; - return -1; -} - -/* open() but with abort traps - */ - -void -xopen(int *fd_ptr, const char *path, int flags, struct stat *st) -{ - if ((*fd_ptr = open(path, flags)) == -1) - err(errno, "%s", path); - - if (fstat(*fd_ptr, st) == -1) - err(errno, "%s: stat", path); - - if (!S_ISREG(st->st_mode)) - err(errno, "%s: not a regular file", path); - - if (lseek(*fd_ptr, 0, SEEK_CUR) == (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; - - unsigned long pathlen; - unsigned long maxlen; - - char *dirbuf; - int dirfd; - - char *slash; - - struct stat st; - -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - maxlen = PATH_LEN; -#else - maxlen = 1024; -#endif - - dirbuf = NULL; - dirfd = -1; - - pathlen = xstrxlen(path, maxlen); - - if (pathlen >= maxlen) { - fprintf(stderr, "Path too long for fsync_parent_dir\n"); - goto err_fsync_dir; - } - - if (pathlen == 0) - { - errno = EINVAL; - goto err_fsync_dir; - } - - dirbuf = malloc(pathlen + 1); - if (dirbuf == NULL) - 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 = open(dirbuf, O_RDONLY -#ifdef O_DIRECTORY - | O_DIRECTORY -#endif -#ifdef O_NOFOLLOW - | O_NOFOLLOW -#endif - ); - if (dirfd == -1) - goto err_fsync_dir; - - if (fstat(dirfd, &st) < 0) - goto err_fsync_dir; - - if (!S_ISDIR(st.st_mode)) { - fprintf(stderr, "%s: not a directory\n", dirbuf); - goto err_fsync_dir; - } - - /* sync file on disk */ - if (fsync_on_eintr(dirfd) == -1) - goto err_fsync_dir; - - if (close_on_eintr(dirfd) == -1) - goto err_fsync_dir; - - if (dirbuf != NULL) - free(dirbuf); - - errno = saved_errno; - return 0; - -err_fsync_dir: - if (!errno) - errno = EIO; - - if (errno != saved_errno) - fprintf(stderr, "%s: %s\n", path, strerror(errno)); - - if (dirbuf != NULL) - free(dirbuf); - - if (dirfd > -1) - close_on_eintr(dirfd); - - errno = saved_errno; - - return -1; -} - -/* returns ptr to path (string). if local>0: - * make tmpfile in the same directory as the - * file. if local==0, use TMPDIR - * - * if local==0, the 3rd argument is ignored - */ - -char * -new_tmpfile(int *fd, int local, const char *path) -{ - unsigned long maxlen; - struct stat st; - - /* please do not modify the - * strings or I will get mad - */ - char tmp_none[] = ""; - char tmp_default[] = "/tmp"; - char default_tmpname[] = "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; - char *tmpname; - - char *base = NULL; - char *dest = NULL; - - unsigned long tmpdir_len = 0; - unsigned long tmpname_len = 0; - unsigned long tmppath_len = 0; - - int fd_tmp = -1; - int flags; - -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - maxlen = PATH_LEN; -#else - maxlen = 1024; -#endif - - tmpname = default_tmpname; - if (local) { - if (path == NULL) - goto err_new_tmpfile; - if (*path == '\0') - goto err_new_tmpfile; - - if (stat(path, &st) == -1) - goto err_new_tmpfile; - - if (!S_ISREG(st.st_mode)) - goto err_new_tmpfile; - - tmpname = (char *)path; - } - - if (local) { - base = tmp_none; - - /* appended to filename for tmp: - */ - tmpdir_len = xstrxlen(default_tmpname, maxlen); - } else { - base = get_tmpdir(); - - if (base == NULL) - base = tmp_default; - if (*base == '\0') - base = tmp_default; - - tmpdir_len = xstrxlen(base, maxlen); - } - - tmpname_len = xstrxlen(tmpname, maxlen); - - tmppath_len = tmpdir_len + tmpname_len; - ++tmppath_len; /* for '/' or '.' */ - - /* max length -1 of maxlen - * for termination - */ - if (tmpdir_len > maxlen - tmpname_len - 1) - goto err_new_tmpfile; - - /* +1 for NULL */ - dest = malloc(tmppath_len + 1); - if (dest == NULL) - goto err_new_tmpfile; - - if (local) { - - *dest = '.'; /* hidden file */ - - memcpy(dest + (unsigned long)1, tmpname, tmpname_len); - - memcpy(dest + (unsigned long)1 + tmpname_len, - default_tmpname, tmpdir_len); - } else { - - memcpy(dest, base, tmpdir_len); - - dest[tmpdir_len] = '/'; - - memcpy(dest + tmpdir_len + 1, tmpname, tmpname_len); - } - - dest[tmppath_len] = '\0'; - - fd_tmp = mkstemp_n(dest); - if (fd_tmp == -1) - goto err_new_tmpfile; - - if (fchmod(fd_tmp, 0600) == -1) - goto err_new_tmpfile; - - flags = fcntl(fd_tmp, F_GETFL); - - if (flags == -1) - goto err_new_tmpfile; - - /* - * O_APPEND would permit offsets - * to be ignored, which breaks - * positional read/write - */ - if (flags & O_APPEND) - goto err_new_tmpfile; - - if (lock_file(fd_tmp, flags) == -1) - goto err_new_tmpfile; - - if (fstat(fd_tmp, &st) == -1) - goto err_new_tmpfile; - - /* - * Extremely defensive - * likely pointless checks - */ - - /* check if it's a file */ - if (!S_ISREG(st.st_mode)) - goto err_new_tmpfile; - - /* check if it's seekable */ - if (lseek(fd_tmp, 0, SEEK_CUR) == (off_t)-1) - goto err_new_tmpfile; - - /* tmpfile has >1 hardlinks */ - if (st.st_nlink > 1) - goto err_new_tmpfile; - - /* tmpfile unlinked while opened */ - if (st.st_nlink == 0) - goto err_new_tmpfile; - - *fd = fd_tmp; - - return dest; - -err_new_tmpfile: - - if (dest != NULL) - free(dest); - - if (fd_tmp > -1) - close_on_eintr(fd_tmp); - - return NULL; -} - -int -lock_file(int fd, int flags) -{ - struct flock fl; - - memset(&fl, 0, sizeof(fl)); - - if ((flags & O_ACCMODE) == O_RDONLY) - fl.l_type = F_RDLCK; - else - fl.l_type = F_WRLCK; - - fl.l_whence = SEEK_SET; - - if (fcntl(fd, F_SETLK, &fl) == -1) - return -1; - - return 0; -} - -/* return TMPDIR, or fall back - * to portable defaults - */ - -char * -get_tmpdir(void) -{ - char *t; - struct stat st; - - t = getenv("TMPDIR"); - - if (t && *t) { - - if (stat(t, &st) == 0 && S_ISDIR(st.st_mode)) { - - if ((st.st_mode & S_IWOTH) && !(st.st_mode & S_ISVTX)) - return NULL; - - return t; - } - } - - if (stat("/tmp", &st) == 0 && S_ISDIR(st.st_mode)) - return "/tmp"; - - if (stat("/var/tmp", &st) == 0 && S_ISDIR(st.st_mode)) - return "/var/tmp"; - - return "."; -} - -/* portable mkstemp - */ - -int -mkstemp_n(char *template) -{ - int fd; - unsigned long i, j; - unsigned long len; - char *p; - - unsigned long xc = 0; - - static char ch[] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - - unsigned long r; -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - unsigned long max_len = PATH_LEN; -#else - unsigned long max_len = 4096; -#endif - - len = xstrxlen(template, max_len); - - if (len < 6) { - errno = EINVAL; - return -1; - } - - p = template + len; - - while (p > template && p[-1] == 'X') { - --p; - ++xc; - } - - if (xc < 6) { - errno = EINVAL; - return -1; - } - - for (i = 0; i < 200; i++) { - - for (j = 0; j < xc; j++) { - - r = rlong(); - - p[j] = ch[(unsigned long)(r >> 1) % (sizeof(ch) - 1)]; - } - - fd = open(template, - O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600); - - if (fd >= 0) - return fd; - - if (errno != EEXIST) - return -1; - } - - errno = EEXIST; - 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. - */ - -long -rw_file_exact(int fd, unsigned char *mem, unsigned long nrw, - off_t off, int rw_type, int loop_eagain, - int loop_eintr, unsigned long max_retries, - int off_reset) -{ - long rval; - long rc; - - unsigned long nrw_cur; - - off_t off_cur; - void *mem_cur; - - unsigned long retries_on_zero; - - rval = 0; - - rc = 0; - retries_on_zero = 0; - - if (io_args(fd, mem, nrw, off, rw_type) == -1) - return -1; - - while (1) { - - /* Prevent theoretical overflow */ - if (rval >= 0 && (unsigned long)rval > (nrw - rc)) - goto err_rw_file_exact; - - rc += rval; - if ((unsigned long)rc >= nrw) - break; - - mem_cur = (void *)(mem + (unsigned long)rc); - nrw_cur = (unsigned long)(nrw - (unsigned long)rc); - if (off < 0) - 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) - return -1; - - if (rval == 0) { - if (retries_on_zero++ < max_retries) - continue; - goto err_rw_file_exact; - } - - retries_on_zero = 0; - } - - if ((unsigned long)rc != nrw) - goto err_rw_file_exact; - - return rw_over_nrw(rc, nrw); - -err_rw_file_exact: - 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: HAVE_REAL_PREAD_PWRITE=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 - */ - -long -prw(int fd, void *mem, unsigned long nrw, - off_t off, int rw_type, - int loop_eagain, int loop_eintr, - int off_reset) -{ - long r; - int positional_rw; - struct stat st; -#if !defined(HAVE_REAL_PREAD_PWRITE) || \ - HAVE_REAL_PREAD_PWRITE < 1 - int saved_errno; - off_t verified; - off_t off_orig; - off_t off_last; -#endif - - if (io_args(fd, mem, nrw, off, rw_type) - == -1) { - return -1; - } - - r = -1; - - /* do not use loop_eagain on - * normal files - */ - - if (!loop_eagain) { - /* check whether the file - * changed - */ - - if (check_file(fd, &st) == -1) - return -1; - } - - if (rw_type >= IO_PREAD) - positional_rw = 1; /* pread/pwrite */ - else - positional_rw = 0; /* read/write */ - -try_rw_again: - - if (!positional_rw) { -#if defined(HAVE_REAL_PREAD_PWRITE) && \ - HAVE_REAL_PREAD_PWRITE > 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(HAVE_REAL_PREAD_PWRITE) && \ - HAVE_REAL_PREAD_PWRITE > 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; - - return rw_over_nrw(r, nrw); - } - -#if defined(HAVE_REAL_PREAD_PWRITE) && \ - HAVE_REAL_PREAD_PWRITE > 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) - 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) { - errno = EIO; - 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; - - return rw_over_nrw(r, nrw); -#endif - -err_prw: - errno = EIO; - return -1; -} - -int -io_args(int fd, void *mem, unsigned long nrw, - off_t off, int rw_type) -{ - /* obviously */ - if (mem == NULL) - goto err_io_args; - - /* uninitialised fd */ - if (fd < 0) - goto err_io_args; - - /* negative offset */ - if (off < 0) - goto err_io_args; - - /* prevent zero-byte rw */ - if (!nrw) - goto err_io_args; - - /* prevent overflow */ - if (nrw > (unsigned long)X_LONG_MAX) - goto err_io_args; - - /* prevent overflow */ - if (((unsigned long)off + nrw) < (unsigned long)off) - goto err_io_args; - - if (rw_type > IO_PWRITE) - goto err_io_args; - - return 0; - -err_io_args: - errno = EIO; - return -1; -} - -int -check_file(int fd, struct stat *st) -{ - if (fstat(fd, st) == -1) - goto err_is_file; - - if (!S_ISREG(st->st_mode)) - goto err_is_file; - - return 0; - -err_is_file: - errno = EIO; - return -1; -} - -/* POSIX can say whatever it wants. - * specification != implementation - */ - -long -rw_over_nrw(long r, unsigned long nrw) -{ - /* 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 ((unsigned long) - r > X_LONG_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 - * long integers are the - * same size as SSIZE_T - */ - - goto err_rw_over_nrw; - } - - /* Theoretical buggy libc: - * Should never return a number of - * bytes above the requested length. - */ - if ((unsigned long)r > nrw) - goto err_rw_over_nrw; - - return r; - -err_rw_over_nrw: - - errno = EIO; - return -1; -} - -#if !defined(HAVE_REAL_PREAD_PWRITE) || \ - HAVE_REAL_PREAD_PWRITE < 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_eagain, EAGAIN))); - - 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); - - if (r > -1) - errno = saved_errno; - - return r; -} - -int -fsync_on_eintr(int fd) -{ - int r; - - do { - r = fsync(fd); - } while (r == -1 && errno == EINTR); - - return r; -} |
