summaryrefslogtreecommitdiff
path: root/util/nvmutil/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/io.c')
-rw-r--r--util/nvmutil/io.c746
1 files changed, 0 insertions, 746 deletions
diff --git a/util/nvmutil/io.c b/util/nvmutil/io.c
deleted file mode 100644
index 738a2744..00000000
--- a/util/nvmutil/io.c
+++ /dev/null
@@ -1,746 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * I/O functions specific to nvmutil.
- *
- * Related: file.c
- */
-
-#ifdef __OpenBSD__
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "include/common.h"
-
-void
-open_gbe_file(void)
-{
- struct xstate *x = xstatus();
- struct commands *cmd;
- struct xfile *f;
-
- struct stat _st;
- int _flags;
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
- xopen(&f->gbe_fd, f->fname,
- cmd->flags | O_BINARY |
- O_NOFOLLOW | O_CLOEXEC, &_st);
-
- /* inode will be checked later on write */
- f->gbe_dev = _st.st_dev;
- f->gbe_ino = _st.st_ino;
-
- if (_st.st_nlink > 1)
- err(EINVAL,
- "%s: warning: file has multiple (%lu) hard links\n",
- f->fname, (unsigned long)_st.st_nlink);
-
- if (_st.st_nlink == 0)
- err(EIO, "%s: file unlinked while open", f->fname);
-
- _flags = fcntl(f->gbe_fd, F_GETFL);
- if (_flags == -1)
- err(errno, "%s: fcntl(F_GETFL)", f->fname);
-
- /*
- * O_APPEND must not be used, because this
- * allows POSIX write() to ignore the
- * current write offset and write at EOF,
- * which would therefore break pread/pwrite
- */
- if (_flags & O_APPEND)
- err(EIO, "%s: O_APPEND flag", f->fname);
-
- f->gbe_file_size = _st.st_size;
-
- switch (f->gbe_file_size) {
- case SIZE_8KB:
- case SIZE_16KB:
- case SIZE_128KB:
- break;
- default:
- err(EINVAL, "File size must be 8KB, 16KB or 128KB");
- }
-
- if (lock_file(f->gbe_fd, cmd->flags) == -1)
- err(errno, "%s: can't lock", f->fname);
-}
-
-/*
- * We copy the entire gbe file
- * to the tmpfile, and then we
- * work on that. We copy back
- * afterward. this is the copy.
- *
- * we copy to tmpfile even on
- * read-only commands, for the
- * double-read verification,
- * which also benefits cmd_cat.
- */
-void
-copy_gbe(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- f = &x->f;
-
- read_file();
-
- /*
- regular operations post-read operate only on the first
- 8KB, because each GbE part is the first 4KB of each
- half of the file.
-
- we no longer care about anything past 8KB, until we get
- to writing, at which point we will flush the buffer
- again
- */
-
- if (f->gbe_file_size == SIZE_8KB)
- return;
-
- x_v_memcpy(f->buf + (unsigned long)GBE_PART_SIZE,
- f->buf + (unsigned long)(f->gbe_file_size >> 1),
- (unsigned long)GBE_PART_SIZE);
-}
-
-void
-read_file(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- struct stat _st;
- long _r;
-
- f = &x->f;
-
- /* read main file */
- _r = rw_file_exact(f->gbe_fd, f->buf, f->gbe_file_size,
- 0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (_r < 0)
- err(errno, "%s: read failed", f->fname);
-
- /* copy to tmpfile */
-
- _r = rw_file_exact(f->tmp_fd, f->buf, f->gbe_file_size,
- 0, IO_PWRITE, NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (_r < 0)
- err(errno, "%s: %s: copy failed",
- f->fname, f->tname);
-
- /*
- * file size comparison
- */
-
- if (fstat(f->tmp_fd, &_st) == -1)
- err(errno, "%s: stat", f->tname);
-
- f->gbe_tmp_size = _st.st_size;
-
- if (f->gbe_tmp_size != f->gbe_file_size)
- err(EIO, "%s: %s: not the same size",
- f->fname, f->tname);
-
- /*
- * fsync tmp gbe file, because we will compare
- * its contents to what was read (for safety)
- */
- if (x_i_fsync(f->tmp_fd) == -1)
- err(errno, "%s: fsync (tmpfile copy)", f->tname);
-
- _r = rw_file_exact(f->tmp_fd, f->bufcmp, f->gbe_file_size,
- 0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (_r < 0)
- err(errno, "%s: read failed (cmp)", f->tname);
-
- if (x_i_memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0)
- err(errno, "%s: %s: read contents differ (pre-test)",
- f->fname, f->tname);
-}
-void
-write_gbe_file(void)
-{
- struct xstate *x = xstatus();
- struct commands *cmd;
- struct xfile *f;
-
- struct stat _gbe_st;
- struct stat _tmp_st;
-
- unsigned long p;
- unsigned char update_checksum;
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
- if ((cmd->flags & O_ACCMODE) == O_RDONLY)
- return;
-
- if (fstat(f->gbe_fd, &_gbe_st) == -1)
- err(errno, "%s: re-check", f->fname);
- if (_gbe_st.st_dev != f->gbe_dev || _gbe_st.st_ino != f->gbe_ino)
- err(EIO, "%s: file replaced while open", f->fname);
- if (_gbe_st.st_size != f->gbe_file_size)
- err(errno, "%s: file size changed before write", f->fname);
- if (!S_ISREG(_gbe_st.st_mode))
- err(errno, "%s: file type changed before write", f->fname);
-
- if (fstat(f->tmp_fd, &_tmp_st) == -1)
- err(errno, "%s: re-check", f->tname);
- if (_tmp_st.st_dev != f->tmp_dev || _tmp_st.st_ino != f->tmp_ino)
- err(EIO, "%s: file replaced while open", f->tname);
- if (_tmp_st.st_size != f->gbe_file_size)
- err(errno, "%s: file size changed before write", f->tname);
- if (!S_ISREG(_tmp_st.st_mode))
- err(errno, "%s: file type changed before write", f->tname);
-
- update_checksum = cmd->chksum_write;
-
- for (p = 0; p < 2; p++) {
- if (!f->part_modified[p])
- continue;
-
- if (update_checksum)
- set_checksum(p);
-
- rw_gbe_file_part(p, IO_PWRITE, "pwrite");
- }
-}
-
-void
-rw_gbe_file_part(unsigned long p, int rw_type,
- const char *rw_type_str)
-{
- struct xstate *x = xstatus();
- struct commands *cmd;
- struct xfile *f;
-
- long rval;
-
- off_t file_offset;
-
- unsigned long gbe_rw_size;
- unsigned char *mem_offset;
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
- gbe_rw_size = cmd->rw_size;
-
- if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
- err(errno, "%s: %s: part %lu: invalid rw_type, %d",
- f->fname, rw_type_str, (unsigned long)p, rw_type);
-
- mem_offset = gbe_mem_offset(p, rw_type_str);
- file_offset = (off_t)gbe_file_offset(p, rw_type_str);
-
- rval = rw_gbe_file_exact(f->tmp_fd, mem_offset,
- gbe_rw_size, file_offset, rw_type);
-
- if (rval == -1)
- err(errno, "%s: %s: part %lu",
- f->fname, rw_type_str, (unsigned long)p);
-
- if ((unsigned long)rval != gbe_rw_size)
- err(EIO, "%s: partial %s: part %lu",
- f->fname, rw_type_str, (unsigned long)p);
-}
-
-void
-write_to_gbe_bin(void)
-{
- struct xstate *x = xstatus();
- struct commands *cmd;
- struct xfile *f;
-
- int saved_errno;
- int mv;
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
- if ((cmd->flags & O_ACCMODE) != O_RDWR)
- return;
-
- write_gbe_file();
-
- /*
- * We may otherwise read from
- * cache, so we must sync.
- */
- if (x_i_fsync(f->tmp_fd) == -1)
- err(errno, "%s: fsync (pre-verification)",
- f->tname);
-
- check_written_part(0);
- check_written_part(1);
-
- report_io_err_rw();
-
- if (f->io_err_gbe)
- err(EIO, "%s: bad write", f->fname);
-
- /*
- * success!
- * now just rename the tmpfile
- */
-
- saved_errno = errno;
-
- if (x_i_close(f->tmp_fd) == -1) {
- fprintf(stderr, "FAIL: %s: close\n", f->tname);
- f->io_err_gbe_bin = 1;
- }
-
- if (x_i_close(f->gbe_fd) == -1) {
- fprintf(stderr, "FAIL: %s: close\n", f->fname);
- f->io_err_gbe_bin = 1;
- }
-
- errno = saved_errno;
-
- f->tmp_fd = -1;
- f->gbe_fd = -1;
-
- if (!f->io_err_gbe_bin) {
-
- mv = gbe_mv();
-
- if (mv < 0) {
-
- f->io_err_gbe_bin = 1;
-
- fprintf(stderr, "%s: %s\n",
- f->fname, strerror(errno));
- } else {
- /*
- * tmpfile removed
- * by the rename
- */
-
- if (f->tname != NULL)
- free(f->tname);
-
- f->tname = NULL;
- }
- }
-
- /*
- * finally:
- * must sync to disk!
- * very nearly done
- */
-
- if (!f->io_err_gbe_bin)
- return;
-
- fprintf(stderr, "FAIL (rename): %s: skipping fsync\n",
- f->fname);
- if (errno)
- fprintf(stderr,
- "errno %d: %s\n", errno, strerror(errno));
-}
-
-void
-check_written_part(unsigned long p)
-{
- struct xstate *x = xstatus();
- struct commands *cmd;
- struct xfile *f;
-
- long rval;
-
- unsigned long gbe_rw_size;
-
- off_t file_offset;
- unsigned char *mem_offset;
-
- struct stat st;
- unsigned char *buf_restore;
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
- if (!f->part_modified[p])
- return;
-
- gbe_rw_size = cmd->rw_size;
-
- mem_offset = gbe_mem_offset(p, "pwrite");
- file_offset = (off_t)gbe_file_offset(p, "pwrite");
-
- memset(f->pad, 0xff, sizeof(f->pad));
-
- if (fstat(f->gbe_fd, &st) == -1)
- err(errno, "%s: fstat (post-write)", f->fname);
- if (st.st_dev != f->gbe_dev || st.st_ino != f->gbe_ino)
- err(EIO, "%s: file changed during write", f->fname);
-
- if (fstat(f->tmp_fd, &st) == -1)
- err(errno, "%s: fstat (post-write)", f->tname);
- if (st.st_dev != f->tmp_dev || st.st_ino != f->tmp_ino)
- err(EIO, "%s: file changed during write", f->tname);
-
- rval = rw_gbe_file_exact(f->tmp_fd, f->pad,
- gbe_rw_size, file_offset, IO_PREAD);
-
- if (rval == -1)
- f->rw_check_err_read[p] = f->io_err_gbe = 1;
- else if ((unsigned long)rval != gbe_rw_size)
- f->rw_check_partial_read[p] = f->io_err_gbe = 1;
- else if (x_i_memcmp(mem_offset, f->pad, gbe_rw_size) != 0)
- f->rw_check_bad_part[p] = f->io_err_gbe = 1;
-
- if (f->rw_check_err_read[p] ||
- f->rw_check_partial_read[p])
- return;
-
- /*
- * We only load one part on-file, into memory but
- * always at offset zero, for post-write checks.
- * That's why we hardcode good_checksum(0).
- */
- buf_restore = f->buf;
-
- /*
- * good_checksum works on f->buf
- * so let's change f->buf for now
- */
- f->buf = f->pad;
-
- if (good_checksum(0))
- f->post_rw_checksum[p] = 1;
-
- f->buf = buf_restore;
-}
-
-void
-report_io_err_rw(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- unsigned long p;
-
- f = &x->f;
-
- if (!f->io_err_gbe)
- return;
-
- for (p = 0; p < 2; p++) {
- if (!f->part_modified[p])
- continue;
-
- if (f->rw_check_err_read[p])
- fprintf(stderr,
- "%s: pread: p%lu (post-verification)\n",
- f->fname, (unsigned long)p);
- if (f->rw_check_partial_read[p])
- fprintf(stderr,
- "%s: partial pread: p%lu (post-verification)\n",
- f->fname, (unsigned long)p);
- if (f->rw_check_bad_part[p])
- fprintf(stderr,
- "%s: pwrite: corrupt write on p%lu\n",
- f->fname, (unsigned long)p);
-
- if (f->rw_check_err_read[p] ||
- f->rw_check_partial_read[p]) {
- fprintf(stderr,
- "%s: p%lu: skipped checksum verification "
- "(because read failed)\n",
- f->fname, (unsigned long)p);
-
- continue;
- }
-
- fprintf(stderr, "%s: ", f->fname);
-
- if (f->post_rw_checksum[p])
- fprintf(stderr, "GOOD");
- else
- fprintf(stderr, "BAD");
-
- fprintf(stderr, " checksum in p%lu on-disk.\n",
- (unsigned long)p);
-
- if (f->post_rw_checksum[p]) {
- fprintf(stderr,
- " This does NOT mean it's safe. it may be\n"
- " salvageable if you use the cat feature.\n");
- }
- }
-}
-
-int
-gbe_mv(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- int rval;
-
- int saved_errno;
- int tmp_gbe_bin_exists;
-
- char *dest_tmp;
- int dest_fd;
-
- f = &x->f;
-
- /* will be set 0 if it doesn't */
- tmp_gbe_bin_exists = 1;
-
- dest_tmp = NULL;
- dest_fd = -1;
-
- saved_errno = errno;
-
- rval = rename(f->tname, f->fname);
-
- if (rval > -1) {
- /*
- * same filesystem
- */
-
- tmp_gbe_bin_exists = 0;
-
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- rval = -1;
- }
-
- goto ret_gbe_mv;
- }
-
- if (errno != EXDEV)
- goto ret_gbe_mv;
-
- /* cross-filesystem rename */
-
- if ((rval = f->tmp_fd = open(f->tname,
- O_RDONLY | O_BINARY)) == -1)
- goto ret_gbe_mv;
-
- /* create replacement temp in target directory */
- dest_tmp = new_tmpfile(&dest_fd, 1, f->fname);
- if (dest_tmp == NULL)
- goto ret_gbe_mv;
-
- /* copy data */
-
- rval = rw_file_exact(f->tmp_fd, f->bufcmp,
- f->gbe_file_size, 0, IO_PREAD,
- NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (rval < 0)
- goto ret_gbe_mv;
-
- rval = rw_file_exact(dest_fd, f->bufcmp,
- f->gbe_file_size, 0, IO_PWRITE,
- NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (rval < 0)
- goto ret_gbe_mv;
-
- if (x_i_fsync(dest_fd) == -1)
- goto ret_gbe_mv;
-
- if (x_i_close(dest_fd) == -1)
- goto ret_gbe_mv;
-
- if (rename(dest_tmp, f->fname) == -1)
- goto ret_gbe_mv;
-
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- goto ret_gbe_mv;
- }
-
- free(dest_tmp);
- dest_tmp = NULL;
-
-ret_gbe_mv:
-
- if (f->gbe_fd > -1) {
- if (x_i_close(f->gbe_fd) < 0)
- rval = -1;
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- rval = -1;
- }
- f->gbe_fd = -1;
- }
-
- if (f->tmp_fd > -1) {
- if (x_i_close(f->tmp_fd) < 0)
- rval = -1;
-
- f->tmp_fd = -1;
- }
-
- /*
- * before this function is called,
- * tmp_fd may have been moved
- */
- if (tmp_gbe_bin_exists) {
- if (unlink(f->tname) < 0)
- rval = -1;
- else
- tmp_gbe_bin_exists = 0;
- }
-
- if (rval < 0) {
- /*
- * if nothing set errno,
- * we assume EIO, or we
- * use what was set
- */
- if (errno == saved_errno)
- errno = EIO;
- } else {
- errno = saved_errno;
- }
-
- return rval;
-}
-
-/*
- * This one is similar to gbe_file_offset,
- * but used to check Gbe bounds in memory,
- * and it is *also* used during file I/O.
- */
-unsigned char *
-gbe_mem_offset(unsigned long p, const char *f_op)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- off_t gbe_off;
-
- f = &x->f;
-
- gbe_off = gbe_x_offset(p, f_op, "mem",
- GBE_PART_SIZE, GBE_WORK_SIZE);
-
- return (unsigned char *)
- (f->buf + (unsigned long)gbe_off);
-}
-
-/*
- * I/O operations filtered here. These operations must
- * only write from the 0th position or the half position
- * within the GbE file, and write 4KB of data.
- *
- * This check is called, to ensure just that.
- */
-off_t
-gbe_file_offset(unsigned long p, const char *f_op)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- off_t gbe_file_half_size;
-
- f = &x->f;
-
- gbe_file_half_size = f->gbe_file_size >> 1;
-
- return gbe_x_offset(p, f_op, "file",
- gbe_file_half_size, f->gbe_file_size);
-}
-
-off_t
-gbe_x_offset(unsigned long p, const char *f_op, const char *d_type,
- off_t nsize, off_t ncmp)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- off_t off;
-
- check_bin(p, "part number");
-
- f = &x->f;
-
- off = ((off_t)p) * (off_t)nsize;
-
- if (off > ncmp - GBE_PART_SIZE)
- err(ECANCELED, "%s: GbE %s %s out of bounds",
- f->fname, d_type, f_op);
-
- if (off != 0 && off != ncmp >> 1)
- err(ECANCELED, "%s: GbE %s %s at bad offset",
- f->fname, d_type, f_op);
-
- return off;
-}
-
-long
-rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
- off_t off, int rw_type)
-{
- struct xstate *x = xstatus();
- struct xfile *f;
-
- long r;
-
- f = &x->f;
-
- if (io_args(fd, mem, nrw, off, rw_type) == -1)
- return -1;
-
- if (mem != (void *)f->pad) {
- if (mem < f->buf)
- goto err_rw_gbe_file_exact;
-
- if ((unsigned long)(mem - f->buf) >= GBE_WORK_SIZE)
- goto err_rw_gbe_file_exact;
- }
-
- if (off < 0 || off >= f->gbe_file_size)
- goto err_rw_gbe_file_exact;
-
- if (nrw > (unsigned long)(f->gbe_file_size - off))
- goto err_rw_gbe_file_exact;
-
- if (nrw > (unsigned long)GBE_PART_SIZE)
- goto err_rw_gbe_file_exact;
-
- r = rw_file_exact(fd, mem, nrw, off, rw_type,
- NO_LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY,
- OFF_ERR);
-
- return rw_over_nrw(r, nrw);
-
-err_rw_gbe_file_exact:
- errno = EIO;
- return -1;
-}