summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-20 04:02:51 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-25 12:32:57 +0000
commit210922bc9174bcce3444f9bc2782b033622b4c70 (patch)
tree7153f7b848bf1ebc60baae09f4384c7a43d83d0a /util/nvmutil/lib
parentf50ffd6bb13c04cb185fb6311f8875582bf18388 (diff)
util/mkhtemp: extremely hardened mkhtemp
This will also be used in lbmk itself at some point, which currently just uses regular mktemp, for tmpdir handling during the build process. Renamed util/nvmutil to util/libreboot-utils, which now contains two tools. The new tool, mkhtemp, is a hardened implementation of mktemp, which nvmutil also uses now. Still experimental, but good enough for nvmutil. Mkhtemp attempts to provide TOCTOU resistance on Linux, by using modern features in Linux such as Openat2 (syscall) with O_EXCL and O_TMPFILE, and many various security checks e.g. inode/dev during creation. Checks are done constantly, to try to detect race conditions. The code is very strict about things like sticky bits in world writeable directories, also ownership (it can be made to bar even root access on files and directories it doesn't own). It's a security-first implementation of mktemp, likely even more secure than the OpenBSD mkstemp, but more auditing and testing is needed - more features are also planned, including a compatibility mode to make it also work like traditional mktemp/mkstemp. The intention, once this becomes stable, is that it will become a modern drop-in replacement for mkstemp on Linux and BSD systems. Some legacy code has been removed, and in general cleaned up. I wrote mkhtemp for nvmutil, as part of its atomic write behaviour, but mktemp was the last remaining liability, so I rewrote that too! Docs/manpage/website will be made for mkhtemp once the code is mature. Other changes have also been made. This is from another experimental branch of Libreboot, that I'm pushing early. For example, nvmutil's state machine has been tidied up, moving more logic back into main. Mktemp is historically prone to race conditions, e.g. symlink attacks, directory replacement, remounting during operation, all sorts of things. Mkhtemp has been written to solve, or otherwise mitigate, that problem. Mkhtemp is currently experimental and will require a major cleanup at some point, but it already works well enough, and you can in fact use it; at this time, the -d, -p and -q flags are supported, and you can add a custom template at the end, e.g. mkhtemp -p test -d Eventually, I will make this have complete parity with the GNU and BSD implementations, so that it is fully useable on existing setups, while optionally providing the hardening as well. A lot of code has also been tidied up. I didn't track the changes I made with this one, because it was a major re-write of nvmutil; it is now libreboot-utils, and I will continue to write more programs in here over time. It's basically now a bunch of hardened wrappers around various libc functions, e.g. there is also a secure I/O wrapper for read/write. There is a custom randomisation function, rlong, which simply uses arc4random or getrandom, on BSD and Linux respectively. Efforts are made to make it as reliable as possible, to the extent that it never returns with failure; in the unlikely event that it fails, it aborts. It also sleeps between failure, to mitigate certain DoS attacks. You can just go in util/libreboot-utils and type make, then you will have the nvmutil and mkhtemp binaries, which you can just use. It all works. Everything was massively rewritten. Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib')
-rw-r--r--util/nvmutil/lib/checksum.c108
-rw-r--r--util/nvmutil/lib/command.c546
-rw-r--r--util/nvmutil/lib/file.c890
-rw-r--r--util/nvmutil/lib/io.c649
-rw-r--r--util/nvmutil/lib/num.c349
-rw-r--r--util/nvmutil/lib/state.c280
-rw-r--r--util/nvmutil/lib/string.c75
-rw-r--r--util/nvmutil/lib/usage.c30
-rw-r--r--util/nvmutil/lib/word.c68
9 files changed, 0 insertions, 2995 deletions
diff --git a/util/nvmutil/lib/checksum.c b/util/nvmutil/lib/checksum.c
deleted file mode 100644
index 8565361b..00000000
--- a/util/nvmutil/lib/checksum.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- *
- * Functions related to GbE NVM checksums.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "../include/common.h"
-
-void
-read_checksums(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- unsigned long _p;
- unsigned long _skip_part;
-
- unsigned char _num_invalid;
- unsigned char _max_invalid;
-
- f->part_valid[0] = 0;
- f->part_valid[1] = 0;
-
- if (!cmd->chksum_read)
- return;
-
- _num_invalid = 0;
- _max_invalid = 2;
-
- if (cmd->arg_part)
- _max_invalid = 1;
-
- /* Skip verification on this part,
- * but only when arg_part is set.
- */
- _skip_part = f->part ^ 1;
-
- for (_p = 0; _p < 2; _p++) {
-
- /* Only verify a part if it was *read*
- */
- if (cmd->arg_part && (_p == _skip_part))
- continue;
-
- f->part_valid[_p] = good_checksum(_p);
- if (!f->part_valid[_p])
- ++_num_invalid;
- }
-
- if (_num_invalid >= _max_invalid) {
-
- if (_max_invalid == 1)
- err(ECANCELED, "%s: part %lu has a bad checksum",
- f->fname, (unsigned long)f->part);
-
- err(ECANCELED, "%s: No valid checksum found in file",
- f->fname);
- }
-}
-
-int
-good_checksum(unsigned long partnum)
-{
- unsigned short expected_checksum;
- unsigned short actual_checksum;
-
- expected_checksum =
- calculated_checksum(partnum);
-
- actual_checksum =
- nvm_word(NVM_CHECKSUM_WORD, partnum);
-
- if (expected_checksum == actual_checksum) {
- return 1;
- } else {
- return 0;
- }
-}
-
-void
-set_checksum(unsigned long p)
-{
- check_bin(p, "part number");
- set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
-}
-
-unsigned short
-calculated_checksum(unsigned long p)
-{
- unsigned long c;
- unsigned int val16;
-
- val16 = 0;
-
- for (c = 0; c < NVM_CHECKSUM_WORD; c++)
- val16 += (unsigned int)nvm_word(c, p);
-
- return (unsigned short)((NVM_CHECKSUM - val16) & 0xffff);
-}
diff --git a/util/nvmutil/lib/command.c b/util/nvmutil/lib/command.c
deleted file mode 100644
index 95e1b4f7..00000000
--- a/util/nvmutil/lib/command.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "../include/common.h"
-
-/* Guard against regressions by maintainers (command table)
- */
-
-void
-sanitize_command_list(void)
-{
- struct xstate *x = xstatus(0, NULL);
-
- unsigned long c;
- unsigned long num_commands;
-
- num_commands = items(x->cmd);
-
- for (c = 0; c < num_commands; c++)
- sanitize_command_index(c);
-}
-
-void
-sanitize_command_index(unsigned long c)
-{
- struct xstate *x = xstatus(0, NULL);
- struct commands *cmd = &x->cmd[c];
-
- int _flag;
- unsigned long gbe_rw_size;
-
- check_command_num(c);
-
- if (cmd->argc < 3)
- err(EINVAL, "cmd index %lu: argc below 3, %d",
- (unsigned long)c, cmd->argc);
-
- if (cmd->str == NULL)
- err(EINVAL, "cmd index %lu: NULL str",
- (unsigned long)c);
-
- if (*cmd->str == '\0')
- err(EINVAL, "cmd index %lu: empty str",
- (unsigned long)c);
-
- if (xstrxlen(cmd->str, MAX_CMD_LEN + 1) >
- MAX_CMD_LEN) {
- err(EINVAL, "cmd index %lu: str too long: %s",
- (unsigned long)c, cmd->str);
- }
-
- if (cmd->run == NULL)
- err(EINVAL, "cmd index %lu: cmd ptr null",
- (unsigned long)c);
-
- check_bin(cmd->arg_part, "cmd.arg_part");
- check_bin(cmd->chksum_read, "cmd.chksum_read");
- check_bin(cmd->chksum_write, "cmd.chksum_write");
-
- gbe_rw_size = cmd->rw_size;
-
- switch (gbe_rw_size) {
- case GBE_PART_SIZE:
- case NVM_SIZE:
- break;
- default:
- err(EINVAL, "Unsupported rw_size: %lu",
- (unsigned long)gbe_rw_size);
- }
-
- if (gbe_rw_size > GBE_PART_SIZE)
- err(EINVAL, "rw_size larger than GbE part: %lu",
- (unsigned long)gbe_rw_size);
-
- _flag = (cmd->flags & O_ACCMODE);
-
- if (_flag != O_RDONLY &&
- _flag != O_RDWR)
- err(EINVAL, "invalid cmd.flags setting");
-}
-
-void
-set_cmd(int argc, char *argv[])
-{
- struct xstate *x = xstatus(0, NULL);
- const char *cmd;
-
- unsigned long c;
-
- for (c = 0; c < items(x->cmd); c++) {
-
- cmd = x->cmd[c].str;
-
- /* not the right command */
- if (xstrxcmp(argv[2], cmd, MAX_CMD_LEN) != 0)
- continue;
-
- /* valid command found */
- if (argc >= x->cmd[c].argc) {
- x->no_cmd = 0;
- x->i = c; /* set command */
-
- return;
- }
-
- err(EINVAL,
- "Too few args on command '%s'", cmd);
- }
-
- x->no_cmd = 1;
-}
-
-void
-set_cmd_args(int argc, char *argv[])
-{
- struct xstate *x = xstatus(0, NULL);
- unsigned long i = x->i;
- struct commands *cmd = &x->cmd[i];
- struct xfile *f = &x->f;
-
- if (!valid_command(i) || argc < 3)
- usage();
-
- if (x->no_cmd)
- usage();
-
- /* Maintainer bug
- */
- if (cmd->arg_part && argc < 4)
- err(EINVAL,
- "arg_part set for command that needs argc4");
-
- if (cmd->arg_part && i == CMD_SETMAC)
- err(EINVAL,
- "arg_part set on CMD_SETMAC");
-
- if (i == CMD_SETMAC) {
-
- if (argc >= 4)
- x->mac.str = argv[3];
- else
- x->mac.str = x->mac.rmac;
-
- } else if (cmd->arg_part) {
-
- f->part = conv_argv_part_num(argv[3]);
- }
-}
-
-unsigned long
-conv_argv_part_num(const char *part_str)
-{
- unsigned char ch;
-
- if (part_str[0] == '\0' || part_str[1] != '\0')
- err(EINVAL, "Partnum string '%s' wrong length", part_str);
-
- /* char signedness is implementation-defined
- */
- ch = (unsigned char)part_str[0];
- if (ch < '0' || ch > '1')
- err(EINVAL, "Bad part number (%c)", ch);
-
- return (unsigned long)(ch - '0');
-}
-
-void
-check_command_num(unsigned long c)
-{
- if (!valid_command(c))
- err(EINVAL, "Invalid run_cmd arg: %lu",
- (unsigned long)c);
-}
-
-unsigned char
-valid_command(unsigned long c)
-{
- struct xstate *x = xstatus(0, NULL);
- struct commands *cmd;
-
- if (c >= items(x->cmd))
- return 0;
-
- cmd = &x->cmd[c];
-
- if (c != cmd->chk)
- err(EINVAL,
- "Invalid cmd chk value (%lu) vs arg: %lu",
- cmd->chk, c);
-
- return 1;
-}
-
-void
-cmd_helper_setmac(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct macaddr *mac = &x->mac;
-
- unsigned long partnum;
-
- check_cmd(cmd_helper_setmac, "setmac");
-
- printf("MAC address to be written: %s\n", mac->str);
- parse_mac_string();
-
- for (partnum = 0; partnum < 2; partnum++)
- write_mac_part(partnum);
-}
-
-void
-parse_mac_string(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct macaddr *mac = &x->mac;
-
- unsigned long mac_byte;
-
- if (xstrxlen(x->mac.str, 18) != 17)
- err(EINVAL, "MAC address is the wrong length");
-
- memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
-
- for (mac_byte = 0; mac_byte < 6; mac_byte++)
- set_mac_byte(mac_byte);
-
- if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0)
- err(EINVAL, "Must not specify all-zeroes MAC address");
-
- if (mac->mac_buf[0] & 1)
- err(EINVAL, "Must not specify multicast MAC address");
-}
-
-void
-set_mac_byte(unsigned long mac_byte_pos)
-{
- struct xstate *x = xstatus(0, NULL);
- struct macaddr *mac = &x->mac;
-
- char separator;
-
- unsigned long mac_str_pos;
- unsigned long mac_nib_pos;
-
- mac_str_pos = mac_byte_pos * 3;
-
- if (mac_str_pos < 15) {
- if ((separator = mac->str[mac_str_pos + 2]) != ':')
- err(EINVAL, "Invalid MAC address separator '%c'",
- separator);
- }
-
- for (mac_nib_pos = 0; mac_nib_pos < 2; mac_nib_pos++)
- set_mac_nib(mac_str_pos, mac_byte_pos, mac_nib_pos);
-}
-
-void
-set_mac_nib(unsigned long mac_str_pos,
- unsigned long mac_byte_pos, unsigned long mac_nib_pos)
-{
- struct xstate *x = xstatus(0, NULL);
- struct macaddr *mac = &x->mac;
-
- char mac_ch;
- unsigned short hex_num;
-
- mac_ch = mac->str[mac_str_pos + mac_nib_pos];
-
- if ((hex_num = hextonum(mac_ch)) > 15)
- err(EINVAL, "Invalid character '%c'",
- mac->str[mac_str_pos + mac_nib_pos]);
-
- /* If random, ensure that local/unicast bits are set.
- */
- if ((mac_byte_pos == 0) && (mac_nib_pos == 1) &&
- ((mac_ch | 0x20) == 'x' ||
- (mac_ch == '?')))
- hex_num = (hex_num & 0xE) | 2; /* local, unicast */
-
- /* MAC words stored big endian in-file, little-endian
- * logically, so we reverse the order.
- */
- mac->mac_buf[mac_byte_pos >> 1] |= hex_num <<
- (((mac_byte_pos & 1) << 3) /* left or right byte? */
- | ((mac_nib_pos ^ 1) << 2)); /* left or right nib? */
-}
-
-void
-write_mac_part(unsigned long partnum)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
- struct macaddr *mac = &x->mac;
-
- unsigned long w;
-
- check_bin(partnum, "part number");
- if (!f->part_valid[partnum])
- return;
-
- for (w = 0; w < 3; w++)
- set_nvm_word(w, partnum, mac->mac_buf[w]);
-
- printf("Wrote MAC address to part %lu: ",
- (unsigned long)partnum);
- print_mac_from_nvm(partnum);
-}
-
-void
-cmd_helper_dump(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- unsigned long p;
-
- check_cmd(cmd_helper_dump, "dump");
-
- f->part_valid[0] = good_checksum(0);
- f->part_valid[1] = good_checksum(1);
-
- for (p = 0; p < 2; p++) {
-
- if (!f->part_valid[p]) {
-
- fprintf(stderr,
- "BAD checksum %04x in part %lu (expected %04x)\n",
- nvm_word(NVM_CHECKSUM_WORD, p),
- (unsigned long)p,
- calculated_checksum(p));
- }
-
- printf("MAC (part %lu): ",
- (unsigned long)p);
-
- print_mac_from_nvm(p);
-
- hexdump(p);
- }
-}
-
-void
-print_mac_from_nvm(unsigned long partnum)
-{
- unsigned long c;
- unsigned short val16;
-
- for (c = 0; c < 3; c++) {
-
- val16 = nvm_word(c, partnum);
-
- printf("%02x:%02x",
- (unsigned int)(val16 & 0xff),
- (unsigned int)(val16 >> 8));
-
- if (c == 2)
- printf("\n");
- else
- printf(":");
- }
-}
-
-void
-hexdump(unsigned long partnum)
-{
- unsigned long c;
- unsigned long row;
- unsigned short val16;
-
- for (row = 0; row < 8; row++) {
-
- printf("%08lx ",
- (unsigned long)((unsigned long)row << 4));
-
- for (c = 0; c < 8; c++) {
-
- val16 = nvm_word((row << 3) + c, partnum);
-
- if (c == 4)
- printf(" ");
-
- printf(" %02x %02x",
- (unsigned int)(val16 & 0xff),
- (unsigned int)(val16 >> 8));
-
- }
-
- printf("\n");
- }
-}
-
-void
-cmd_helper_swap(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- check_cmd(cmd_helper_swap, "swap");
-
- memcpy(
- f->buf + (unsigned long)GBE_WORK_SIZE,
- f->buf,
- GBE_PART_SIZE);
-
- memcpy(
- f->buf,
- f->buf + (unsigned long)GBE_PART_SIZE,
- GBE_PART_SIZE);
-
- memcpy(
- f->buf + (unsigned long)GBE_PART_SIZE,
- f->buf + (unsigned long)GBE_WORK_SIZE,
- GBE_PART_SIZE);
-
- set_part_modified(0);
- set_part_modified(1);
-}
-
-void
-cmd_helper_copy(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- check_cmd(cmd_helper_copy, "copy");
-
- memcpy(
- f->buf + (unsigned long)((f->part ^ 1) * GBE_PART_SIZE),
- f->buf + (unsigned long)(f->part * GBE_PART_SIZE),
- GBE_PART_SIZE);
-
- set_part_modified(f->part ^ 1);
-}
-
-void
-cmd_helper_cat(void)
-{
- struct xstate *x = xstatus(0, NULL);
-
- check_cmd(cmd_helper_cat, "cat");
-
- x->cat = 0;
- cat(0);
-}
-
-void
-cmd_helper_cat16(void)
-{
- struct xstate *x = xstatus(0, NULL);
-
- check_cmd(cmd_helper_cat16, "cat16");
-
- x->cat = 1;
- cat(1);
-}
-
-void
-cmd_helper_cat128(void)
-{
- struct xstate *x = xstatus(0, NULL);
-
- check_cmd(cmd_helper_cat128, "cat128");
-
- x->cat = 15;
- cat(15);
-}
-
-void
-cat(unsigned long nff)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- unsigned long p;
- unsigned long ff;
-
- p = 0;
- ff = 0;
-
- if ((unsigned long)x->cat != nff) {
-
- err(ECANCELED, "erroneous call to cat");
- }
-
- fflush(NULL);
-
- memset(f->pad, 0xff, GBE_PART_SIZE);
-
- for (p = 0; p < 2; p++) {
-
- cat_buf(f->bufcmp +
- (unsigned long)(p * (f->gbe_file_size >> 1)));
-
- for (ff = 0; ff < nff; ff++) {
-
- cat_buf(f->pad);
- }
- }
-}
-
-void
-cat_buf(unsigned char *b)
-{
- if (b == NULL)
- err(errno, "null pointer in cat command");
-
- if (rw_file_exact(STDOUT_FILENO, b,
- GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR) < 0)
- err(errno, "stdout: cat");
-}
-void
-check_cmd(void (*fn)(void),
- const char *name)
-{
- struct xstate *x = xstatus(0, NULL);
- unsigned long i = x->i;
-
- if (x->cmd[i].run != fn)
- err(ECANCELED, "Running %s, but cmd %s is set",
- name, x->cmd[i].str);
-
- /* prevent second command
- */
- for (i = 0; i < items(x->cmd); i++)
- x->cmd[i].run = cmd_helper_err;
-}
-
-void
-cmd_helper_err(void)
-{
- err(ECANCELED,
- "Erroneously running command twice");
-}
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;
-}
diff --git a/util/nvmutil/lib/io.c b/util/nvmutil/lib/io.c
deleted file mode 100644
index 5769dd05..00000000
--- a/util/nvmutil/lib/io.c
+++ /dev/null
@@ -1,649 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * I/O functions specific to nvmutil.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "../include/common.h"
-
-void
-open_gbe_file(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- int _flags;
-
- xopen(&f->gbe_fd, f->fname,
- cmd->flags | O_BINARY |
- O_NOFOLLOW | O_CLOEXEC, &f->gbe_st);
-
- if (f->gbe_st.st_nlink > 1)
- err(EINVAL,
- "%s: warning: file has multiple (%lu) hard links\n",
- f->fname, (unsigned long)f->gbe_st.st_nlink);
-
- if (f->gbe_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 allows POSIX write() to ignore
- * the current write offset and write at EOF,
- * which would break positional read/write
- */
-
- if (_flags & O_APPEND)
- err(EIO, "%s: O_APPEND flag", f->fname);
-
- f->gbe_file_size = f->gbe_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);
-}
-
-void
-copy_gbe(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- read_file();
-
- if (f->gbe_file_size == SIZE_8KB)
- return;
-
- 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(0, NULL);
- struct xfile *f = &x->f;
-
- struct stat _st;
- long _r;
-
- /* 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);
-
- /* needs sync, for verification
- */
- if (fsync_on_eintr(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 (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(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- unsigned long p;
- unsigned char update_checksum;
-
- if ((cmd->flags & O_ACCMODE) == O_RDONLY)
- return;
-
- if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
- err(errno, "%s: file inode/device changed", f->tname);
-
- if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
- err(errno, "%s: file has changed", f->fname);
-
- 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(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- long rval;
-
- off_t file_offset;
-
- unsigned long gbe_rw_size;
- unsigned char *mem_offset;
-
- 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(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- int saved_errno;
- int mv;
-
- if ((cmd->flags & O_ACCMODE) != O_RDWR)
- return;
-
- write_gbe_file();
-
- /* We may otherwise read from
- * cache, so we must sync.
- */
-
- if (fsync_on_eintr(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);
-
- saved_errno = errno;
-
- if (close_on_eintr(f->tmp_fd) == -1) {
- fprintf(stderr, "FAIL: %s: close\n", f->tname);
- f->io_err_gbe_bin = 1;
- }
-
- if (close_on_eintr(f->gbe_fd) == -1) {
- fprintf(stderr, "FAIL: %s: close\n", f->fname);
- f->io_err_gbe_bin = 1;
- }
-
- errno = saved_errno;
-
- /* tmpfile written, now we
- * rename it back to the main file
- * (we do atomic writes)
- */
-
- 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 {
-
- /* removed by rename
- */
-
- if (f->tname != NULL)
- free(f->tname);
-
- f->tname = NULL;
- }
- }
-
- 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(0, NULL);
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- long rval;
-
- unsigned long gbe_rw_size;
-
- off_t file_offset;
- unsigned char *mem_offset;
-
- unsigned char *buf_restore;
-
- 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 (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
- err(errno, "%s: file inode/device changed", f->tname);
-
- if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
- err(errno, "%s: file changed during write", f->fname);
-
- 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 (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(0, NULL);
- struct xfile *f = &x->f;
-
- unsigned long p;
-
- 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(0, NULL);
- struct xfile *f = &x->f;
-
- int rval;
-
- int saved_errno;
- int tmp_gbe_bin_exists;
-
- char *dest_tmp;
- int dest_fd;
-
- /* 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) {
-
- /* rename on 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;
-
- /*
- * OR, 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 (fsync_on_eintr(dest_fd) == -1)
- goto ret_gbe_mv;
-
- if (close_on_eintr(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 (close_on_eintr(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 (close_on_eintr(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(0, NULL);
- struct xfile *f = &x->f;
-
- off_t gbe_off;
-
- 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.
- */
-off_t
-gbe_file_offset(unsigned long p, const char *f_op)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- off_t gbe_file_half_size;
-
- 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(0, NULL);
- struct xfile *f = &x->f;
-
- off_t off;
-
- check_bin(p, "part number");
-
- 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(0, NULL);
- struct xfile *f = &x->f;
-
- long r;
-
- 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;
-}
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c
deleted file mode 100644
index bbb5a83e..00000000
--- a/util/nvmutil/lib/num.c
+++ /dev/null
@@ -1,349 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Numerical functions.
- */
-
-#ifdef __OpenBSD__
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#if defined(FALLBACK_RAND_1989) && \
- (FALLBACK_RAND_1989) > 0
-#include <sys/time.h>
-#endif
-
-#include <errno.h>
-#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
- defined(__FreeBSD__) || \
- defined(__NetBSD__) || defined(__APPLE__))
-#include <fcntl.h> /* if not arc4random: /dev/urandom */
-#endif
-#include <limits.h>
-#include <stddef.h>
-#include <string.h>
-#if defined(FALLBACK_RAND_1989) && \
- (FALLBACK_RAND_1989) > 0
-#include <time.h>
-#endif
-#include <unistd.h>
-
-#include "../include/common.h"
-
-unsigned short
-hextonum(char ch_s)
-{
- unsigned char ch;
-
- ch = (unsigned char)ch_s;
-
- if ((unsigned int)(ch - '0') <= 9)
- return ch - '0';
-
- ch |= 0x20;
-
- if ((unsigned int)(ch - 'a') <= 5)
- return ch - 'a' + 10;
-
- if (ch == '?' || ch == 'x')
- return (unsigned short)rlong() & 0xf;
-
- return 16; /* invalid character */
-}
-
-/* Random numbers
- */
-
-unsigned long
-rlong(void)
-{
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
- defined(__FreeBSD__) || \
- defined(__NetBSD__) || defined(__APPLE__)
-
- unsigned long rval;
- arc4random_buf(&rval, sizeof(unsigned long));
-
- return rval;
-#else
- static int fd = -1;
- static long nr = -1;
- static unsigned long off = 0;
-#if defined (BUFSIZ)
- static char rbuf[BUFSIZ];
-#else
-#ifndef PORTABLE
- static char rbuf[4096];
-#elif ((PORTABLE) > 0)
- static char rbuf[256]; /* scarce memory on old systems */
-#else
- static char rbuf[4096]; /* typical 32-bit BUFSIZ */
-#endif
-#endif
- unsigned long rval;
- long new_nr;
-
- int retries = 0;
- int max_retries = 100;
-
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-
- /* linux getrandom()
- *
- * we *can* use arc4random on
- * modern linux, but not on
- * every libc. better use the
- * official linux function
- *
- * similar benefits to arc4random
- * e.g. works in chroot, blocks
- * until it has enough entropy,
- * and works even when /dev/urandom
- * is available (doesn't use it);
- * it's generally more reliable
- */
-
- if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0)
- return rval;
-
- /*
- * now fall back to urandom if getrandom failed:
- */
-#endif
-#endif
-
- /* reading from urandom is inherently
- * unreliable on old systems, even if
- * newer systems make it more reliable
- *
- * modern linux/bsd make it safe, but
- * we have to assume that someone is
- * compiling this on linux from 1999
- *
- * this logic therefore applies various
- * tricks to mitigate possible os bugs
- */
-
-retry_urandom_read:
-
- if (++retries > max_retries)
- goto rlong_next;
-
- if (nr < 0 || nr < (long)sizeof(unsigned long)) {
-
- if (fd < 0) {
-
- fd = open("/dev/urandom",
- O_RDONLY | O_BINARY | O_NOFOLLOW |
- O_CLOEXEC);
-
-#ifdef USE_OLD_DEV_RANDOM
-#if (USE_OLD_DEV_RANDOM) > 0
- /* WARNING:
- * /dev/random may block
- * forever and does **NOT**
- * guarantee better entropy
- * on old systems
- *
- * only use it if needed
- */
-
- if (fd < 0)
- fd = open("/dev/random",
- O_RDONLY | O_BINARY | O_NOFOLLOW |
- O_CLOEXEC);
-#endif
-#endif
-
- if (fd < 0)
- goto retry_urandom_read;
-
- retries = 0;
- }
-
- new_nr = rw_file_exact(fd, (unsigned char *)rbuf,
- sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN,
- LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (new_nr < 0 || new_nr < (long)sizeof(rbuf))
- goto retry_urandom_read;
-
- /* only reset buffer after successful refill */
- nr = new_nr;
- off = 0;
-
- /* to mitigate file descriptor
- * injection, we do not re-use
- * the same descriptor each time
- */
- (void) close_on_eintr(fd);
- fd = -1;
- }
-
- fd = -1;
- retries = 0;
-
- memcpy(&rval, rbuf + off, sizeof(unsigned long));
-
- nr -= (long)sizeof(unsigned long);
- off += sizeof(unsigned long);
-
- return rval;
-
-rlong_next:
-
- fd = -1;
- off = 0;
- nr = -1;
-
- err(EIO, "Can't read from /dev/[ua]random");
- return 0;
-
-#endif
-#else /* FALLBACK_RAND_1989 */
- /* your computer is from a museum
- */
- unsigned long mix = 0;
- int nr;
-
- /* 100 times, for entropy
- */
- for (nr = 0; nr < 100; nr++)
- mix ^= fallback_rand_1989();
-
- /* 101 times ;)
- */
- return fallback_rand_1989();
-#endif
-}
-
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-int
-fallback_rand_getrandom(void *buf, unsigned long len)
-{
- unsigned long off = 0;
- long rval = -1;
-
- if (!len)
- return -1;
-
- if (buf == NULL)
- return -1;
-
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-
- while (off < len) {
-
-#if defined(HAVE_GETRANDOM)
- rval = (long)getrandom((char *)buf + off, len - off, 0);
-#elif defined(HAVE_GETRANDOM_SYSCALL)
- rval = (long)syscall(SYS_getrandom,
- (char *)buf + off, len - off, 0);
-#endif
-
- if (rval < 0) {
- if (errno == EINTR)
- continue;
-
- return -1; /* unsupported by kernel */
- }
-
- off += (unsigned long)rval;
- }
-
- return 0;
-
-#else
- (void)buf;
- (void)len;
-
- return -1;
-#endif
-}
-#endif
-#endif
-#else
-/* nobody should use this
- * (not crypto-safe)
- */
-unsigned long
-fallback_rand_1989(void)
-{
- static unsigned long mix = 0;
- static unsigned long counter = 0;
-
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- mix ^= (unsigned long)tv.tv_sec
- ^ (unsigned long)tv.tv_usec
- ^ (unsigned long)getpid()
- ^ (unsigned long)&mix
- ^ counter++
- ^ entropy_jitter();
-
- /*
- * Stack addresses can vary between
- * calls, thus increasing entropy.
- */
- mix ^= (unsigned long)&mix;
- mix ^= (unsigned long)&tv;
- mix ^= (unsigned long)&counter;
-
- return mix;
-}
-
-unsigned long
-entropy_jitter(void)
-{
- unsigned long mix;
-
- struct timeval a, b;
- long mix_diff;
-
- int c;
-
- mix = 0;
-
- gettimeofday(&a, NULL);
-
- for (c = 0; c < 32; c++) {
-
- getpid();
- gettimeofday(&b, NULL);
-
- /*
- * prevent negative numbers to prevent overflow,
- * which would bias rand to large numbers
- */
- mix_diff = (long)(b.tv_usec - a.tv_usec);
- if (mix_diff < 0)
- mix_diff = -mix_diff;
-
- mix ^= (unsigned long)(mix_diff);
-
- mix ^= (unsigned long)&mix;
-
- }
-
- return mix;
-}
-#endif
-
-void
-check_bin(unsigned long a, const char *a_name)
-{
- if (a > 1)
- err(EINVAL, "%s must be 0 or 1, but is %lu",
- a_name, (unsigned long)a);
-}
diff --git a/util/nvmutil/lib/state.c b/util/nvmutil/lib/state.c
deleted file mode 100644
index 02a3e51c..00000000
--- a/util/nvmutil/lib/state.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- *
- * State machine (singleton) for nvmutil data.
- */
-
-#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 <unistd.h>
-
-#include "../include/common.h"
-
-struct xstate *
-xstatus(int argc, char *argv[])
-{
- static struct xstate us = {
- /* DO NOT MESS THIS UP, OR THERE WILL BE DEMONS */
- {
- {
- CMD_DUMP, "dump", cmd_helper_dump, ARGC_3,
- ARG_NOPART,
- SKIP_CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- NVM_SIZE, O_RDONLY
- }, {
- CMD_SETMAC, "setmac", cmd_helper_setmac, ARGC_3,
- ARG_NOPART,
- CHECKSUM_READ, CHECKSUM_WRITE,
- NVM_SIZE, O_RDWR
- }, {
- CMD_SWAP, "swap", cmd_helper_swap, ARGC_3,
- ARG_NOPART,
- CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- GBE_PART_SIZE, O_RDWR
- }, {
- CMD_COPY, "copy", cmd_helper_copy, ARGC_4,
- ARG_PART,
- CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- GBE_PART_SIZE, O_RDWR
- }, {
- CMD_CAT, "cat", cmd_helper_cat, ARGC_3,
- ARG_NOPART,
- CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- GBE_PART_SIZE, O_RDONLY
- }, {
- CMD_CAT16, "cat16", cmd_helper_cat16, ARGC_3,
- ARG_NOPART,
- CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- GBE_PART_SIZE, O_RDONLY
- }, {
- CMD_CAT128, "cat128", cmd_helper_cat128, ARGC_3,
- ARG_NOPART,
- CHECKSUM_READ, SKIP_CHECKSUM_WRITE,
- GBE_PART_SIZE, O_RDONLY
- }
- },
-
- /* ->mac */
- {NULL, "xx:xx:xx:xx:xx:xx", {0, 0, 0}}, /* .str, .rmac, .mac_buf */
-
- /* .f */
- {0},
-
- /* .argv0 (for our getprogname implementation) */
- NULL,
-
- /* ->i (index to cmd[]) */
- 0,
-
- /* .no_cmd (set 0 when a command is found) */
- 1,
-
- /* .cat (cat helpers set this) */
- -1
-
- };
-
- static int first_run = 1;
-
- if (!first_run)
- return &us;
-
- us.f.buf = us.f.real_buf;
-
- first_run = 0;
- us.argv0 = argv[0];
-
- if (argc > 1)
- us.f.fname = argv[1];
-
- if (argc < 3)
- usage();
-
-/* https://man.openbsd.org/pledge.2
- https://man.openbsd.org/unveil.2 */
-#if defined(__OpenBSD__) && defined(OpenBSD)
-#if (OpenBSD) >= 604
- if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
- err(errno, "pledge plus unveil");
-#elif (OpenBSD) >= 509
- if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
- err(errno, "pledge");
-#endif
-#endif
-
-#ifndef S_ISREG
- err(ECANCELED, "Can't determine file types (S_ISREG undefined)");
-#endif
-
-#ifndef CHAR_BIT
- err(ECANCELED, "Unknown char size");
-#else
- if (CHAR_BIT != 8)
- err(EINVAL, "Unsupported char size");
-#endif
-
-#if defined(__OpenBSD__) && defined(OpenBSD) && \
- (OpenBSD) >= 604
- /* can only use local tmp on openbsd, due to unveil */
- us.f.tname = new_tmpfile(&us.f.tmp_fd, 1, NULL);
-#else
- us.f.tname = new_tmpfile(&us.f.tmp_fd, 0, NULL);
-#endif
- if (us.f.tname == NULL)
- err(errno, "Can't create tmpfile");
- if (*us.f.tname == '\0')
- err(errno, "tmp dir is an empty string");
-
-#if defined(__OpenBSD__) && defined(OpenBSD) && \
- OpenBSD >= 604
- if (unveil(us.f.tname, "rwc") == -1)
- err(errno, "unveil rwc: %s", us.f.tname);
-#endif
- if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
- err(errno, "%s: stat", us.f.tname);
-
- sanitize_command_list();
-
- /* parse user command */
- set_cmd(argc, argv);
- set_cmd_args(argc, argv);
-
-#if defined(__OpenBSD__) && defined(OpenBSD) && \
- (OpenBSD) >= 604
- if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
- if (unveil(us.f.fname, "r") == -1)
- err(errno, "%s: unveil r", us.f.fname);
- } else {
- if (unveil(us.f.fname, "rwc") == -1)
- err(errno, "%s: unveil rw", us.f.fname);
- }
-
- if (unveil(us.f.tname, "rwc") == -1)
- err(errno, "%s: unveil rwc", us.f.tname);
-
- if (unveil(NULL, NULL) == -1)
- err(errno, "unveil block (rw)");
-
- if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
- err(errno, "pledge (kill unveil)");
-#endif
-
- open_gbe_file();
-
- memset(us.f.real_buf, 0, sizeof(us.f.real_buf));
- memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp));
-
- /* for good measure */
- memset(us.f.pad, 0, sizeof(us.f.pad));
-
- copy_gbe();
- read_checksums();
-
- return &us;
-}
-
-void
-err(int nvm_errval, const char *msg, ...)
-{
- struct xstate *x = xstatus(0, NULL);
-
- va_list args;
-
- if (errno == 0)
- errno = nvm_errval;
- if (!errno)
- errno = ECANCELED;
-
- (void)exit_cleanup();
-
- if (x != NULL)
- fprintf(stderr, "%s: ", getnvmprogname());
-
- va_start(args, msg);
- vfprintf(stderr, msg, args);
- va_end(args);
-
- fprintf(stderr, ": %s\n", strerror(errno));
-
- exit(EXIT_FAILURE);
-}
-
-const char *
-getnvmprogname(void)
-{
- struct xstate *x = xstatus(0, NULL);
-
- const char *p;
- static char fallback[] = "nvmutil";
-
- char *rval = fallback;
-
- if (x != NULL) {
- if (x->argv0 == NULL || *x->argv0 == '\0')
- return "";
-
- rval = x->argv0;
- }
-
- p = strrchr(rval, '/');
-
- if (p)
- return p + 1;
- else
- return rval;
-}
-
-int
-exit_cleanup(void)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f;
-
- int close_err;
- int saved_errno;
-
- close_err = 0;
- saved_errno = errno;
-
- if (x != NULL) {
- f = &x->f;
-
- if (f->gbe_fd > -1) {
- if (close_on_eintr(f->gbe_fd) == -1)
- close_err = 1;
- f->gbe_fd = -1;
- }
-
- if (f->tmp_fd > -1) {
- if (close_on_eintr(f->tmp_fd) == -1)
- close_err = 1;
- }
-
- if (f->tname != NULL) {
- if (unlink(f->tname) == -1)
- close_err = 1;
- }
-
- f->tmp_fd = -1;
- }
-
- if (saved_errno)
- errno = saved_errno;
-
- if (close_err)
- return -1;
-
- return 0;
-}
diff --git a/util/nvmutil/lib/string.c b/util/nvmutil/lib/string.c
deleted file mode 100644
index b1a5c3e2..00000000
--- a/util/nvmutil/lib/string.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * String functions
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stddef.h>
-#include <unistd.h>
-
-#include "../include/common.h"
-
-/* Portable strncmp() that blocks
- * NULL/empty/unterminated strings
- */
-
-int
-xstrxcmp(const char *a, const char *b, unsigned long maxlen)
-{
- unsigned long i;
-
- if (a == NULL || b == NULL)
- err(EINVAL, "NULL input to xstrxcmp");
-
- if (*a == '\0' || *b == '\0')
- err(EINVAL, "Empty string in xstrxcmp");
-
- for (i = 0; i < maxlen; i++) {
-
- unsigned char ac = (unsigned char)a[i];
- unsigned char bc = (unsigned char)b[i];
-
- if (ac == '\0' || bc == '\0') {
- if (ac == bc)
- return 0;
- return ac - bc;
- }
-
- if (ac != bc)
- return ac - bc;
- }
-
- err(EINVAL, "Unterminated string in xstrxcmp");
-
- errno = EINVAL;
- return -1;
-}
-
-/* Portable strncmp() that blocks
- * NULL/empty/unterminated strings
- */
-
-unsigned long
-xstrxlen(const char *scmp, unsigned long maxlen)
-{
- unsigned long xstr_index;
-
- if (scmp == NULL)
- err(EINVAL, "NULL input to xstrxlen");
-
- if (*scmp == '\0')
- err(EINVAL, "Empty string in xstrxlen");
-
- for (xstr_index = 0;
- xstr_index < maxlen && scmp[xstr_index] != '\0';
- xstr_index++);
-
- if (xstr_index == maxlen)
- err(EINVAL, "Unterminated string in xstrxlen");
-
- return xstr_index;
-}
diff --git a/util/nvmutil/lib/usage.c b/util/nvmutil/lib/usage.c
deleted file mode 100644
index 3b0614e8..00000000
--- a/util/nvmutil/lib/usage.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com>
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- */
-
-#include <errno.h>
-#include <stdio.h>
-
-#include "../include/common.h"
-
-void
-usage(void)
-{
- const char *util = getnvmprogname();
-
- fprintf(stderr,
- "Modify Intel GbE NVM images e.g. set MAC\n"
- "USAGE:\n"
- "\t%s FILE dump\n"
- "\t%s FILE setmac [MAC]\n"
- "\t%s FILE swap\n"
- "\t%s FILE copy 0|1\n"
- "\t%s FILE cat\n"
- "\t%s FILE cat16\n"
- "\t%s FILE cat128\n",
- util, util, util, util,
- util, util, util);
-
- err(EINVAL, "Too few arguments");
-}
diff --git a/util/nvmutil/lib/word.c b/util/nvmutil/lib/word.c
deleted file mode 100644
index 5d9220c7..00000000
--- a/util/nvmutil/lib/word.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- *
- * Manipulate Intel GbE NVM words, which are 16-bit little
- * endian in the files (MAC address words are big endian).
- */
-
-#include <sys/types.h>
-
-#include <errno.h>
-#include <stddef.h>
-
-#include "../include/common.h"
-
-unsigned short
-nvm_word(unsigned long pos16, unsigned long p)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- unsigned long pos;
-
- check_nvm_bound(pos16, p);
- pos = (pos16 << 1) + (p * GBE_PART_SIZE);
-
- return (unsigned short)f->buf[pos] |
- ((unsigned short)f->buf[pos + 1] << 8);
-}
-
-void
-set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- unsigned long pos;
-
- check_nvm_bound(pos16, p);
- pos = (pos16 << 1) + (p * GBE_PART_SIZE);
-
- f->buf[pos] = (unsigned char)(val16 & 0xff);
- f->buf[pos + 1] = (unsigned char)(val16 >> 8);
-
- set_part_modified(p);
-}
-
-void
-set_part_modified(unsigned long p)
-{
- struct xstate *x = xstatus(0, NULL);
- struct xfile *f = &x->f;
-
- check_bin(p, "part number");
- f->part_modified[p] = 1;
-}
-
-void
-check_nvm_bound(unsigned long c, unsigned long p)
-{
- /* Block out of bound NVM access
- */
-
- check_bin(p, "part number");
-
- if (c >= NVM_WORDS)
- err(ECANCELED, "check_nvm_bound: out of bounds %lu",
- (unsigned long)c);
-}