summaryrefslogtreecommitdiff
path: root/util/nvmutil
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil')
-rw-r--r--util/nvmutil/.gitignore5
-rw-r--r--util/nvmutil/AUTHORS2
-rw-r--r--util/nvmutil/COPYING21
-rw-r--r--util/nvmutil/Makefile118
-rw-r--r--util/nvmutil/include/common.h606
-rw-r--r--util/nvmutil/lib/checksum.c108
-rw-r--r--util/nvmutil/lib/command.c563
-rw-r--r--util/nvmutil/lib/file.c1136
-rw-r--r--util/nvmutil/lib/io.c673
-rw-r--r--util/nvmutil/lib/mkhtemp.c1133
-rw-r--r--util/nvmutil/lib/num.c444
-rw-r--r--util/nvmutil/lib/state.c279
-rw-r--r--util/nvmutil/lib/string.c114
-rw-r--r--util/nvmutil/lib/usage.c30
-rw-r--r--util/nvmutil/lib/word.c68
-rw-r--r--util/nvmutil/nvmutil.c134
16 files changed, 0 insertions, 5434 deletions
diff --git a/util/nvmutil/.gitignore b/util/nvmutil/.gitignore
deleted file mode 100644
index 9414c836..00000000
--- a/util/nvmutil/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/nvm
-/nvmutil
-*.bin
-*.o
-*.d
diff --git a/util/nvmutil/AUTHORS b/util/nvmutil/AUTHORS
deleted file mode 100644
index f38ea210..00000000
--- a/util/nvmutil/AUTHORS
+++ /dev/null
@@ -1,2 +0,0 @@
-Leah Rowe
-Riku Viitanen
diff --git a/util/nvmutil/COPYING b/util/nvmutil/COPYING
deleted file mode 100644
index 47c35a86..00000000
--- a/util/nvmutil/COPYING
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright (C) 2022-2026 Leah Rowe <leah@libreboot.org>
-Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile
deleted file mode 100644
index a2b65aeb..00000000
--- a/util/nvmutil/Makefile
+++ /dev/null
@@ -1,118 +0,0 @@
-# SPDX-License-Identifier: MIT
-# Copyright (c) 2022,2026 Leah Rowe <leah@libreboot.org>
-# Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com>
-
-# Makefile for nvmutil, which is an application
-# that modifies Intel GbE NVM configurations.
-
-CC = cc
-HELLCC = clang
-
-CFLAGS =
-LDFLAGS =
-DESTDIR =
-PREFIX = /usr/local
-INSTALL = install
-
-.SUFFIXES: .c .o
-
-LDIR =
-
-PORTABLE = $(LDIR) $(CFLAGS)
-WARN = $(PORTABLE) -Wall -Wextra
-STRICT = $(WARN) -std=c90 -pedantic -Werror
-HELLFLAGS = $(STRICT) -Weverything
-
-PROG = nvmutil
-
-OBJS = \
- obj/nvmutil.o \
- obj/lib/state.o \
- obj/lib/file.o \
- obj/lib/string.o \
- obj/lib/usage.o \
- obj/lib/command.o \
- obj/lib/num.o \
- obj/lib/io.o \
- obj/lib/checksum.o \
- obj/lib/word.o \
- obj/lib/mkhtemp.o
-
-# default mode
-CFLAGS_MODE = $(PORTABLE)
-CC_MODE = $(CC)
-
-all: $(PROG)
-
-$(PROG): $(OBJS)
- $(CC_MODE) $(OBJS) -o $(PROG) $(LDFLAGS)
-
-# ensure obj directory exists
-$(OBJS): obj
-
-obj:
- mkdir obj || true
- mkdir obj/lib || true
-
-# main program object
-
-obj/nvmutil.o: nvmutil.c
- $(CC_MODE) $(CFLAGS_MODE) -c nvmutil.c -o obj/nvmutil.o
-
-# library/helper objects
-
-obj/lib/state.o: lib/state.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/state.c -o obj/lib/state.o
-
-obj/lib/file.o: lib/file.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/file.c -o obj/lib/file.o
-
-obj/lib/string.o: lib/string.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/string.c -o obj/lib/string.o
-
-obj/lib/usage.o: lib/usage.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/usage.c -o obj/lib/usage.o
-
-obj/lib/command.o: lib/command.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/command.c -o obj/lib/command.o
-
-obj/lib/num.o: lib/num.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/num.c -o obj/lib/num.o
-
-obj/lib/io.o: lib/io.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/io.c -o obj/lib/io.o
-
-obj/lib/checksum.o: lib/checksum.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/checksum.c -o obj/lib/checksum.o
-
-obj/lib/word.o: lib/word.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/word.c -o obj/lib/word.o
-
-obj/lib/mkhtemp.o: lib/mkhtemp.c
- $(CC_MODE) $(CFLAGS_MODE) -c lib/mkhtemp.c -o obj/lib/mkhtemp.o
-
-# install
-
-install: $(PROG)
- $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
- $(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
- chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
-
-uninstall:
- rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
-
-clean:
- rm -f $(PROG) $(OBJS)
-
-distclean: clean
-
-# mode targets (portable replacement for ifeq)
-
-warn:
- $(MAKE) CFLAGS_MODE="$(WARN)"
-
-strict:
- $(MAKE) CFLAGS_MODE="$(STRICT)"
-
-hell:
- $(MAKE) CFLAGS_MODE="$(HELLFLAGS)" CC_MODE="$(HELLCC)"
diff --git a/util/nvmutil/include/common.h b/util/nvmutil/include/common.h
deleted file mode 100644
index 4aca1772..00000000
--- a/util/nvmutil/include/common.h
+++ /dev/null
@@ -1,606 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- */
-
-
-#ifndef COMMON_H
-#define COMMON_H
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-
-/* for linux getrandom
- */
-#if defined(__linux__)
-#include <errno.h>
-#if defined(__has_include)
-#if __has_include(<sys/random.h>)
-#include <sys/random.h>
-#define HAVE_GETRANDOM 1
-#endif
-#endif
-#if !defined(HAVE_GETRANDOM)
-#include <sys/syscall.h>
-#if defined(SYS_getrandom)
-#define HAVE_GETRANDOM_SYSCALL 1
-#endif
-#endif
-
-#endif
-
-#define items(x) (sizeof((x)) / sizeof((x)[0]))
-
-/* system prototypes
- */
-
-int fchmod(int fd, mode_t mode);
-
-#define MKHTEMP_RETRY_MAX 512
-#define MKHTEMP_SPIN_THRESHOLD 32
-
-#define MKHTEMP_FILE 0
-#define MKHTEMP_DIR 1
-
-
-/* if 1: on operations that
- * check ownership, always
- * permit root to access even
- * if not the file/dir owner
- */
-#ifndef ALLOW_ROOT_OVERRIDE
-#define ALLOW_ROOT_OVERRIDE 0
-#endif
-
-/*
- */
-
-#ifndef SSIZE_MAX
-#define SSIZE_MAX ((ssize_t)(~((ssize_t)1 << (sizeof(ssize_t)*CHAR_BIT-1))))
-#endif
-
-
-/* build config
- */
-
-#ifndef NVMUTIL_H
-#define NVMUTIL_H
-
-#define MAX_CMD_LEN 50
-
-#ifndef PATH_LEN
-#define PATH_LEN 4096
-#endif
-
-#define OFF_ERR 0
-#ifndef OFF_RESET
-#define OFF_RESET 1
-#endif
-
-#ifndef S_ISVTX
-#define S_ISVTX 01000
-#endif
-
-#if defined(S_IFMT) && ((S_ISVTX & S_IFMT) != 0)
-#error "Unexpected bit layout"
-#endif
-
-#ifndef MAX_ZERO_RW_RETRY
-#define MAX_ZERO_RW_RETRY 5
-#endif
-
-#ifndef REAL_POS_IO
-#define REAL_POS_IO 0
-#endif
-
-#ifndef LOOP_EAGAIN
-#define LOOP_EAGAIN 1
-#endif
-#ifndef LOOP_EINTR
-#define LOOP_EINTR 1
-#endif
-
-#ifndef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 64
-#endif
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif
-
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif
-
-#ifndef O_NOCTTY
-#define O_NOCTTY 0
-#endif
-
-#ifndef O_ACCMODE
-#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
-#endif
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#ifndef O_EXCL
-#define O_EXCL 0
-#endif
-
-#ifndef O_CREAT
-#define O_CREAT 0
-#endif
-
-#ifndef O_NONBLOCK
-#define O_NONBLOCK 0
-#endif
-
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
-#ifndef O_NOFOLLOW
-#define O_NOFOLLOW 0
-#endif
-
-#ifndef FD_CLOEXEC
-#define FD_CLOEXEC 0
-#endif
-
-/* Sizes in bytes:
- */
-
-#define SIZE_1KB 1024
-#define SIZE_4KB (4 * SIZE_1KB)
-#define SIZE_8KB (8 * SIZE_1KB)
-#define SIZE_16KB (16 * SIZE_1KB)
-#define SIZE_128KB (128 * SIZE_1KB)
-
-#define GBE_BUF_SIZE (SIZE_128KB)
-
-/* First 128 bytes of gbe.bin is NVM.
- * Then extended area. All of NVM must
- * add up to BABA, truncated (LE)
- *
- * First 4KB of each half of the file
- * contains NVM+extended.
- */
-
-#define GBE_WORK_SIZE (SIZE_8KB)
-#define GBE_PART_SIZE (GBE_WORK_SIZE >> 1)
-#define NVM_CHECKSUM 0xBABA
-#define NVM_SIZE 128
-#define NVM_WORDS (NVM_SIZE >> 1)
-#define NVM_CHECKSUM_WORD (NVM_WORDS - 1)
-
-/* argc minimum (dispatch)
- */
-
-#define ARGC_3 3
-#define ARGC_4 4
-
-#define NO_LOOP_EAGAIN 0
-#define NO_LOOP_EINTR 0
-
-/* For checking if an fd is a normal file.
- * Portable for old Unix e.g. v7 (S_IFREG),
- * 4.2BSD (S_IFMT), POSIX (S_ISREG).
- *
- * IFREG: assumed 0100000 (classic bitmask)
- */
-
-#ifndef S_ISREG
-#if defined(S_IFMT) && defined(S_IFREG)
-#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-#elif defined(S_IFREG)
-#define S_ISREG(m) (((m) & S_IFREG) != 0)
-#else
-#error "can't determine types with stat()"
-#endif
-#endif
-
-#define IO_READ 0
-#define IO_WRITE 1
-#define IO_PREAD 2
-#define IO_PWRITE 3
-
-/* for nvmutil commands
- */
-
-#define CMD_DUMP 0
-#define CMD_SETMAC 1
-#define CMD_SWAP 2
-#define CMD_COPY 3
-#define CMD_CAT 4
-#define CMD_CAT16 5
-#define CMD_CAT128 6
-
-#define ARG_NOPART 0
-#define ARG_PART 1
-
-#define SKIP_CHECKSUM_READ 0
-#define CHECKSUM_READ 1
-
-#define SKIP_CHECKSUM_WRITE 0
-#define CHECKSUM_WRITE 1
-
-/* command table
- */
-
-struct commands {
- size_t chk;
- char *str;
- void (*run)(void);
- int argc;
- unsigned char arg_part;
- unsigned char chksum_read;
- unsigned char chksum_write;
- size_t rw_size; /* within the 4KB GbE part */
- int flags; /* e.g. O_RDWR or O_RDONLY */
-};
-
-/* mac address
- */
-
-struct macaddr {
- char *str; /* set to rmac, or argv string */
- char rmac[18]; /* xx:xx:xx:xx:xx:xx */
- unsigned short mac_buf[3];
-};
-
-/* gbe.bin and tmpfile
- */
-
-struct xfile {
- int gbe_fd;
- struct stat gbe_st;
-
- int tmp_fd;
- struct stat tmp_st;
-
- char *tname; /* path of tmp file */
- char *fname; /* path of gbe file */
-
- unsigned char *buf; /* work memory for files */
-
- int io_err_gbe; /* intermediary write (verification) */
- int io_err_gbe_bin; /* final write (real file) */
- int rw_check_err_read[2];
- int rw_check_partial_read[2];
- int rw_check_bad_part[2];
-
- int post_rw_checksum[2];
-
- off_t gbe_file_size;
- off_t gbe_tmp_size;
-
- size_t part;
- unsigned char part_modified[2];
- unsigned char part_valid[2];
-
- unsigned char real_buf[GBE_BUF_SIZE];
- unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
-
- unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
-};
-
-/* Command table, MAC address, files
- *
- * BE CAREFUL when editing this
- * to ensure that you also update
- * the tables in xstatus()
- */
-
-struct xstate {
- struct commands cmd[7];
- struct macaddr mac;
- struct xfile f;
-
- char *argv0;
-
- size_t i; /* index to cmd[] for current command */
- int no_cmd;
-
- /* Cat commands set this.
- the cat cmd helpers check it */
- int cat;
-};
-
-struct filesystem {
- int rootfd;
-};
-
-struct xstate *xstart(int argc, char *argv[]);
-struct xstate *xstatus(void);
-
-/* Sanitize command tables.
- */
-
-void sanitize_command_list(void);
-void sanitize_command_index(size_t c);
-
-/* Argument handling (user input)
- */
-
-void set_cmd(int argc, char *argv[]);
-void set_cmd_args(int argc, char *argv[]);
-size_t conv_argv_part_num(const char *part_str);
-
-/* Prep files for reading
- */
-
-void open_gbe_file(void);
-int fd_verify_regular(int fd,
- const struct stat *expected,
- struct stat *out);
-int fd_verify_identity(int fd,
- const struct stat *expected,
- struct stat *out);
-int fd_verify_dir_identity(int fd,
- const struct stat *expected);
-int is_owner(struct stat *st);
-int lock_file(int fd, int flags);
-int same_file(int fd, struct stat *st_old, int check_size);
-void xopen(int *fd, const char *path, int flags, struct stat *st);
-
-/* Read GbE file and verify checksums
- */
-
-void copy_gbe(void);
-void read_file(void);
-void read_checksums(void);
-int good_checksum(size_t partnum);
-
-/* validate commands
- */
-
-void check_command_num(size_t c);
-unsigned char valid_command(size_t c);
-
-/* Helper functions for command: setmac
- */
-
-void cmd_helper_setmac(void);
-void parse_mac_string(void);
-void set_mac_byte(size_t mac_byte_pos);
-void set_mac_nib(size_t mac_str_pos,
- size_t mac_byte_pos, size_t mac_nib_pos);
-void write_mac_part(size_t partnum);
-
-/* string functions
- */
-
-int slen(const char *scmp, size_t maxlen,
- size_t *rval);
-int scmp(const char *a, const char *b,
- size_t maxlen, int *rval);
-
-/* numerical functions
- */
-
-unsigned short hextonum(char ch_s);
-size_t rlong(void);
-#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, size_t len);
-#endif
-#endif
-#else
-size_t fallback_rand_1989(void);
-size_t entropy_jitter(void);
-#endif
-
-/* Helper functions for command: dump
- */
-
-void cmd_helper_dump(void);
-void print_mac_from_nvm(size_t partnum);
-void hexdump(size_t partnum);
-
-/* Helper functions for command: swap
- */
-
-void cmd_helper_swap(void);
-
-/* Helper functions for command: copy
- */
-
-void cmd_helper_copy(void);
-
-/* Helper functions for commands:
- * cat, cat16 and cat128
- */
-
-void cmd_helper_cat(void);
-void cmd_helper_cat16(void);
-void cmd_helper_cat128(void);
-void cat(size_t nff);
-void cat_buf(unsigned char *b);
-
-/* Command verification/control
- */
-
-void check_cmd(void (*fn)(void), const char *name);
-void cmd_helper_err(void);
-
-/* Write GbE files to disk
- */
-
-void write_gbe_file(void);
-void set_checksum(size_t part);
-unsigned short calculated_checksum(size_t p);
-
-/* NVM read/write
- */
-
-unsigned short nvm_word(size_t pos16, size_t part);
-void set_nvm_word(size_t pos16,
- size_t part, unsigned short val16);
-void set_part_modified(size_t p);
-void check_nvm_bound(size_t pos16, size_t part);
-void check_bin(size_t a, const char *a_name);
-
-/* GbE file read/write
- */
-
-void rw_gbe_file_part(size_t p, int rw_type,
- const char *rw_type_str);
-void write_to_gbe_bin(void);
-int gbe_mv(void);
-void check_written_part(size_t p);
-void report_io_err_rw(void);
-unsigned char *gbe_mem_offset(size_t part, const char *f_op);
-off_t gbe_file_offset(size_t part, const char *f_op);
-off_t gbe_x_offset(size_t part, const char *f_op,
- const char *d_type, off_t nsize, off_t ncmp);
-ssize_t rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
- off_t off, int rw_type);
-
-/* Generic read/write
- */
-
-int fsync_dir(const char *path);
-ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len,
- off_t off, int rw_type, int loop_eagain, int loop_eintr,
- size_t max_retries, int off_reset);
-ssize_t prw(int fd, void *mem, size_t nrw,
- off_t off, int rw_type, int loop_eagain, int loop_eintr,
- int off_reset);
-int io_args(int fd, void *mem, size_t nrw,
- off_t off, int rw_type);
-int check_file(int fd, struct stat *st);
-ssize_t rw_over_nrw(ssize_t r, size_t nrw);
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
-off_t lseek_on_eintr(int fd, off_t off,
- int whence, int loop_eagain, int loop_eintr);
-#endif
-int try_err(int loop_err, int errval);
-
-/* Error handling and cleanup
- */
-
-void usage(void);
-void err_no_cleanup(int nvm_errval, const char *msg, ...);
-void err(int nvm_errval, const char *msg, ...);
-int exit_cleanup(void);
-const char *getnvmprogname(void);
-
-/* libc hardening
- */
-
-int new_tmpfile(int *fd, char **path);
-int new_tmpdir(int *fd, char **path);
-static int new_tmp_common(int *fd, char **path, int type);
-static int mkhtemp_try_create(int dirfd,
- struct stat *st_dir_initial,
- char *fname_copy,
- char *p,
- size_t xc,
- int *fd,
- struct stat *st,
- int type);
-int mkhtemp(int *fd, struct stat *st,
- char *template, int dirfd, const char *fname,
- struct stat *st_dir_initial, int type);
-int mkhtemp_fill_random(char *p, size_t xc);
-int world_writeable_and_sticky(const char *s,
- int sticky_allowed, int always_sticky);
-int same_dir(const char *a, const char *b);
-int tmpdir_policy(const char *path,
- int *allow_noworld_unsticky);
-char *env_tmpdir(int always_sticky);
-int secure_file(int *fd,
- struct stat *st,
- struct stat *expected,
- int bad_flags,
- int check_seek,
- int do_lock,
- mode_t mode);
-int close_on_eintr(int fd);
-int fsync_on_eintr(int fd);
-int fs_rename_at(int olddirfd, const char *old,
- int newdirfd, const char *new);
-int fs_open(const char *path, int flags);
-struct filesystem *rootfs(void);
-int fs_resolve_at(int dirfd, const char *path, int flags);
-int fs_next_component(const char **p,
- char *name, size_t namesz);
-int fs_open_component(int dirfd, const char *name,
- int flags, int is_last);
-int fs_dirname_basename(const char *path,
- char **dir, char **base, int allow_relative);
-int openat2p(int dirfd, const char *path,
- int flags, mode_t mode);
-int mkdirat_on_eintr(int dirfd,
- const char *pathname, mode_t mode);
-
-/* asserts */
-
-/* type asserts */
-typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1];
-typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1];
-typedef char static_assert_unsigned_char_is_1[
- (sizeof(unsigned char) == 1) ? 1 : -1];
-typedef char static_assert_unsigned_short_is_2[
- (sizeof(unsigned short) >= 2) ? 1 : -1];
-typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1];
-typedef char static_assert_unsigned_int_is_4[
- (sizeof(unsigned int) >= 4) ? 1 : -1];
-typedef char static_assert_unsigned_ssize_t_is_4[
- (sizeof(size_t) >= 4) ? 1 : -1];
-typedef char static_assert_ssize_t_ussize_t[
- (sizeof(size_t) == sizeof(ssize_t)) ? 1 : -1];
-typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1];
-typedef char static_assert_twos_complement[
- ((-1 & 3) == 3) ? 1 : -1
-];
-typedef char assert_unsigned_ssize_t_ptr[
- (sizeof(size_t) >= sizeof(void *)) ? 1 : -1
-];
-
-/*
- * We set _FILE_OFFSET_BITS 64, but we only handle
- * but we only need smaller files, so require 4-bytes.
- * Some operating systems ignore the define, hence assert:
- */
-typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1];
-
-/*
- * asserts (variables/defines sanity check)
- */
-typedef char assert_argc3[(ARGC_3==3)?1:-1];
-typedef char assert_argc4[(ARGC_4==4)?1:-1];
-typedef char assert_read[(IO_READ==0)?1:-1];
-typedef char assert_write[(IO_WRITE==1)?1:-1];
-typedef char assert_pread[(IO_PREAD==2)?1:-1];
-typedef char assert_pwrite[(IO_PWRITE==3)?1:-1];
-typedef char assert_pathlen[(PATH_LEN>=256)?1:-1];
-/* commands */
-typedef char assert_cmd_dump[(CMD_DUMP==0)?1:-1];
-typedef char assert_cmd_setmac[(CMD_SETMAC==1)?1:-1];
-typedef char assert_cmd_swap[(CMD_SWAP==2)?1:-1];
-typedef char assert_cmd_copy[(CMD_COPY==3)?1:-1];
-typedef char assert_cmd_cat[(CMD_CAT==4)?1:-1];
-typedef char assert_cmd_cat16[(CMD_CAT16==5)?1:-1];
-typedef char assert_cmd_cat128[(CMD_CAT128==6)?1:-1];
-/* bool */
-typedef char bool_arg_nopart[(ARG_NOPART==0)?1:-1];
-typedef char bool_arg_part[(ARG_PART==1)?1:-1];
-typedef char bool_skip_checksum_read[(SKIP_CHECKSUM_READ==0)?1:-1];
-typedef char bool_checksum_read[(CHECKSUM_READ==1)?1:-1];
-typedef char bool_skip_checksum_write[(SKIP_CHECKSUM_WRITE==0)?1:-1];
-typedef char bool_checksum_write[(CHECKSUM_WRITE==1)?1:-1];
-typedef char bool_loop_eintr[(LOOP_EINTR==1||LOOP_EINTR==0)?1:-1];
-typedef char bool_loop_eagain[(LOOP_EAGAIN==1||LOOP_EAGAIN==0)?1:-1];
-typedef char bool_no_loop_eintr[(NO_LOOP_EINTR==0)?1:-1];
-typedef char bool_no_loop_eagain[(NO_LOOP_EAGAIN==0)?1:-1];
-typedef char bool_off_err[(OFF_ERR==0)?1:-1];
-typedef char bool_off_reset[(OFF_RESET==0||OFF_RESET==1)?1:-1];
-
-#endif
-#endif
diff --git a/util/nvmutil/lib/checksum.c b/util/nvmutil/lib/checksum.c
deleted file mode 100644
index b417dc7e..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();
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- size_t _p;
- size_t _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, (size_t)f->part);
-
- err(ECANCELED, "%s: No valid checksum found in file",
- f->fname);
- }
-}
-
-int
-good_checksum(size_t 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(size_t p)
-{
- check_bin(p, "part number");
- set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
-}
-
-unsigned short
-calculated_checksum(size_t p)
-{
- size_t 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 3a863d23..00000000
--- a/util/nvmutil/lib/command.c
+++ /dev/null
@@ -1,563 +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();
-
- size_t c;
- size_t num_commands;
-
- num_commands = items(x->cmd);
-
- for (c = 0; c < num_commands; c++)
- sanitize_command_index(c);
-}
-
-void
-sanitize_command_index(size_t c)
-{
- struct xstate *x = xstatus();
- struct commands *cmd = &x->cmd[c];
-
- int _flag;
- size_t gbe_rw_size;
-
- size_t rval;
-
- check_command_num(c);
-
- if (cmd->argc < 3)
- err(EINVAL, "cmd index %lu: argc below 3, %d",
- (size_t)c, cmd->argc);
-
- if (cmd->str == NULL)
- err(EINVAL, "cmd index %lu: NULL str",
- (size_t)c);
-
- if (*cmd->str == '\0')
- err(EINVAL, "cmd index %lu: empty str",
- (size_t)c);
-
- if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
- err(errno, "Could not get command length");
-
- if (rval > MAX_CMD_LEN) {
- err(EINVAL, "cmd index %lu: str too long: %s",
- (size_t)c, cmd->str);
- }
-
- if (cmd->run == NULL)
- err(EINVAL, "cmd index %lu: cmd ptr null",
- (size_t)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",
- (size_t)gbe_rw_size);
- }
-
- if (gbe_rw_size > GBE_PART_SIZE)
- err(EINVAL, "rw_size larger than GbE part: %lu",
- (size_t)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();
- const char *cmd;
-
- int rval;
-
- size_t c;
-
- for (c = 0; c < items(x->cmd); c++) {
-
- cmd = x->cmd[c].str;
-
- if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
- err_no_cleanup(EINVAL,
- "could not compare command strings");
- if (rval != 0)
- continue; /* not the right command */
-
- /* valid command found */
- if (argc >= x->cmd[c].argc) {
- x->no_cmd = 0;
- x->i = c; /* set command */
-
- return;
- }
-
- err_no_cleanup(EINVAL,
- "Too few args on command '%s'", cmd);
- }
-
- x->no_cmd = 1;
-}
-
-void
-set_cmd_args(int argc, char *argv[])
-{
- struct xstate *x = xstatus();
- size_t 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]);
- }
-}
-
-size_t
-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 (size_t)(ch - '0');
-}
-
-void
-check_command_num(size_t c)
-{
- if (!valid_command(c))
- err(EINVAL, "Invalid run_cmd arg: %lu",
- (size_t)c);
-}
-
-unsigned char
-valid_command(size_t c)
-{
- struct xstate *x = xstatus();
- 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();
- struct macaddr *mac = &x->mac;
-
- size_t 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();
- struct macaddr *mac = &x->mac;
-
- size_t mac_byte;
-
- size_t rval;
-
- if (slen(x->mac.str, 18, &rval) < 0)
- err(EINVAL, "Could not determine MAC length");
-
- if (rval != 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(size_t mac_byte_pos)
-{
- struct xstate *x = xstatus();
- struct macaddr *mac = &x->mac;
-
- char separator;
-
- size_t mac_str_pos;
- size_t 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(size_t mac_str_pos,
- size_t mac_byte_pos, size_t mac_nib_pos)
-{
- struct xstate *x = xstatus();
- 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) {
- if (hex_num >= 17)
- err(EIO, "Randomisation failure");
- else
- 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(size_t partnum)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
- struct macaddr *mac = &x->mac;
-
- size_t 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: ",
- (size_t)partnum);
- print_mac_from_nvm(partnum);
-}
-
-void
-cmd_helper_dump(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- size_t 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),
- (size_t)p,
- calculated_checksum(p));
- }
-
- printf("MAC (part %lu): ",
- (size_t)p);
-
- print_mac_from_nvm(p);
-
- hexdump(p);
- }
-}
-
-void
-print_mac_from_nvm(size_t partnum)
-{
- size_t 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(size_t partnum)
-{
- size_t c;
- size_t row;
- unsigned short val16;
-
- for (row = 0; row < 8; row++) {
-
- printf("%08lx ",
- (size_t)((size_t)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();
- struct xfile *f = &x->f;
-
- check_cmd(cmd_helper_swap, "swap");
-
- memcpy(
- f->buf + (size_t)GBE_WORK_SIZE,
- f->buf,
- GBE_PART_SIZE);
-
- memcpy(
- f->buf,
- f->buf + (size_t)GBE_PART_SIZE,
- GBE_PART_SIZE);
-
- memcpy(
- f->buf + (size_t)GBE_PART_SIZE,
- f->buf + (size_t)GBE_WORK_SIZE,
- GBE_PART_SIZE);
-
- set_part_modified(0);
- set_part_modified(1);
-}
-
-void
-cmd_helper_copy(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- check_cmd(cmd_helper_copy, "copy");
-
- memcpy(
- f->buf + (size_t)((f->part ^ 1) * GBE_PART_SIZE),
- f->buf + (size_t)(f->part * GBE_PART_SIZE),
- GBE_PART_SIZE);
-
- set_part_modified(f->part ^ 1);
-}
-
-void
-cmd_helper_cat(void)
-{
- struct xstate *x = xstatus();
-
- check_cmd(cmd_helper_cat, "cat");
-
- x->cat = 0;
- cat(0);
-}
-
-void
-cmd_helper_cat16(void)
-{
- struct xstate *x = xstatus();
-
- check_cmd(cmd_helper_cat16, "cat16");
-
- x->cat = 1;
- cat(1);
-}
-
-void
-cmd_helper_cat128(void)
-{
- struct xstate *x = xstatus();
-
- check_cmd(cmd_helper_cat128, "cat128");
-
- x->cat = 15;
- cat(15);
-}
-
-void
-cat(size_t nff)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- size_t p;
- size_t ff;
-
- p = 0;
- ff = 0;
-
- if ((size_t)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 +
- (size_t)(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();
- size_t 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 ea2bcd0b..00000000
--- a/util/nvmutil/lib/file.c
+++ /dev/null
@@ -1,1136 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Pathless i/o, and some stuff you probably never saw.
- * Be nice to the demon.
- */
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* for openat2: */
-#ifdef __linux__
-#include <linux/openat2.h>
-#include <sys/syscall.h>
-#endif
-
-#include "../include/common.h"
-
-/* check that a file changed
- */
-
-int
-same_file(int fd, struct stat *st_old,
- int check_size)
-{
- struct stat st;
- int saved_errno = errno;
-
- /* TODO: null/-1 checks
- * like this can be
- * generalised
- */
- if (st_old == NULL) {
- errno = EFAULT;
- goto err_same_file;
- }
- if (fd < 0) {
- errno = EBADF;
- goto err_same_file;
- }
-
- if (fstat(fd, &st) == -1)
- goto err_same_file;
-
- if (fd_verify_regular(fd, st_old, &st) < 0)
- goto err_same_file;
-
- if (check_size &&
- st.st_size != st_old->st_size)
- goto err_same_file;
-
- errno = saved_errno;
- return 0;
-
-err_same_file:
-
- if (errno == saved_errno)
- errno = ESTALE;
-
- return -1;
-}
-
-/* open() but with abort traps
- */
-/* TODO: also support other things here than files.
- and then use, throughout the program.
- in particular, use of openat might help
- (split the path)
- (see: link attack mitigations throughout nvmutil)
-
- make it return, and handle the return value/errno
-
- (this could return e.g. EINTR)
-
- TODO: this function is not used by mkhtemp, nor will
- it probably be, it's currently used by nvmutil,
- for opening intel gbe nvm config files. i can
- probably remove it though and unify witth some
- of the verification code now used for mkhtemp
-
-TODO: and don't abort. return -1. and handle in the caller.
-
-minor obstacle: the mkhtemp code always requires absolute
-paths, whereas the gbe editor takes relative paths.
- */
-void
-xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
-{
- if ((*fd_ptr = open(path, flags)) < 0)
- err(errno, "%s", path);
-
- if (fstat(*fd_ptr, st) < 0)
- err(errno, "%s: stat", path);
-
- if (!S_ISREG(st->st_mode))
- err(errno, "%s: not a regular file", path);
-
- if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1)
- err(errno, "%s: file not seekable", path);
-}
-
-/* fsync() the directory of a file,
- * useful for atomic writes
- */
-
-int
-fsync_dir(const char *path)
-{
- int saved_errno = errno;
-
- size_t pathlen = 0;
- size_t maxlen = 0;
-
- char *dirbuf = NULL;
- int dirfd = -1;
-
- char *slash = NULL;
- struct stat st = {0};
-
- int close_errno;
-
-#if defined(PATH_LEN) && \
- (PATH_LEN) >= 256
- maxlen = PATH_LEN;
-#else
- maxlen = 4096;
-#endif
-
- if (path == NULL) {
- errno = EFAULT;
- goto err_fsync_dir;
- }
-
- if (slen(path, maxlen, &pathlen) < 0)
- goto err_fsync_dir;
-
- if (pathlen >= maxlen || pathlen < 0) {
- errno = EMSGSIZE;
- goto err_fsync_dir;
- }
-
- if (pathlen == 0)
- {
- errno = EINVAL;
- goto err_fsync_dir;
- }
-
- dirbuf = malloc(pathlen + 1);
- if (dirbuf == NULL) {
-
- errno = ENOMEM;
- goto err_fsync_dir;
- }
-
- memcpy(dirbuf, path, pathlen + 1);
- slash = strrchr(dirbuf, '/');
-
- if (slash != NULL) {
- *slash = '\0';
- if (*dirbuf == '\0') {
- dirbuf[0] = '/';
- dirbuf[1] = '\0';
- }
- } else {
- dirbuf[0] = '.';
- dirbuf[1] = '\0';
- }
-
- dirfd = fs_open(dirbuf,
- O_RDONLY | O_CLOEXEC | O_NOCTTY
-#ifdef O_DIRECTORY
- | O_DIRECTORY
-#endif
-#ifdef O_NOFOLLOW
- | O_NOFOLLOW
-#endif
-);
- if (dirfd < 0)
- goto err_fsync_dir;
-
- if (fstat(dirfd, &st) < 0)
- goto err_fsync_dir;
-
- if (!S_ISDIR(st.st_mode)) {
-
- errno = ENOTDIR;
- goto err_fsync_dir;
- }
-
- /* sync file on disk */
- if (fsync_on_eintr(dirfd) == -1)
- goto err_fsync_dir;
-
- if (close_on_eintr(dirfd) == -1) {
-
- dirfd = -1;
- goto err_fsync_dir;
- }
-
- if (dirbuf != NULL) {
-
- free(dirbuf);
- dirbuf = NULL;
- }
-
- dirbuf = NULL;
-
- errno = saved_errno;
- return 0;
-
-err_fsync_dir:
-
- if (errno == saved_errno)
- errno = EIO;
-
- if (dirbuf != NULL) {
-
- free(dirbuf);
- dirbuf = NULL;
- }
-
- if (dirfd >= 0) {
-
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
- dirfd = -1;
- }
-
- return -1;
-}
-
-/*
- * Safe I/O functions wrapping around
- * read(), write() and providing a portable
- * analog of both pread() and pwrite().
- * These functions are designed for maximum
- * robustness, checking NULL inputs, overflowed
- * outputs, and all kinds of errors that the
- * standard libc functions don't.
- *
- * Looping on EINTR and EAGAIN is supported.
- * EINTR/EAGAIN looping is done indefinitely.
- */
-
-/* rw_file_exact() - Read perfectly or die
- *
- * Read/write, and absolutely insist on an
- * absolute read; e.g. if 100 bytes are
- * requested, this MUST return 100.
- *
- * This function will never return zero.
- * It will only return below (error),
- * or above (success). On error, -1 is
- * returned and errno is set accordingly.
- *
- * Zero-byte returns are not allowed.
- * It will re-spin a finite number of
- * times upon zero-return, to recover,
- * otherwise it will return an error.
- */
-
-ssize_t
-rw_file_exact(int fd, unsigned char *mem, size_t nrw,
- off_t off, int rw_type, int loop_eagain,
- int loop_eintr, size_t max_retries,
- int off_reset)
-{
- ssize_t rval;
- ssize_t rc;
-
- size_t nrw_cur;
-
- off_t off_cur;
- void *mem_cur;
-
- size_t retries_on_zero;
-
- int saved_errno = errno;
-
- rval = 0;
-
- rc = 0;
- retries_on_zero = 0;
-
- if (io_args(fd, mem, nrw, off, rw_type) == -1)
- goto err_rw_file_exact;
-
- while (1) {
-
- /* Prevent theoretical overflow */
- if (rval >= 0 && (size_t)rval > (nrw - rc)) {
- errno = EOVERFLOW;
- goto err_rw_file_exact;
- }
-
- rc += rval;
- if ((size_t)rc >= nrw)
- break;
-
- mem_cur = (void *)(mem + (size_t)rc);
- nrw_cur = (size_t)(nrw - (size_t)rc);
-
- if (off < 0) {
- errno = EOVERFLOW;
- goto err_rw_file_exact;
- }
-
- off_cur = off + (off_t)rc;
-
- rval = prw(fd, mem_cur, nrw_cur, off_cur,
- rw_type, loop_eagain, loop_eintr,
- off_reset);
-
- if (rval < 0)
- goto err_rw_file_exact;
-
- if (rval == 0) {
- if (retries_on_zero++ < max_retries)
- continue;
-
- errno = EIO;
- goto err_rw_file_exact;
- }
-
- retries_on_zero = 0;
- }
-
- if ((size_t)rc != nrw) {
-
- errno = EIO;
- goto err_rw_file_exact;
- }
-
- rval = rw_over_nrw(rc, nrw);
- if (rval < 0)
- goto err_rw_file_exact;
-
- errno = saved_errno;
-
- return rval;
-
-err_rw_file_exact:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-/* prw() - portable read-write with more
- * safety checks than barebones libc
- *
- * portable pwrite/pread on request, or real
- * pwrite/pread libc functions can be used.
- * the portable (non-libc) pread/pwrite is not
- * thread-safe, because it does not prevent or
- * mitigate race conditions on file descriptors
- *
- * If you need real pwrite/pread, just compile
- * with flag: REAL_POS_IO=1
- *
- * A fallback is provided for regular read/write.
- * rw_type can be IO_READ (read), IO_WRITE (write),
- * IO_PREAD (pread) or IO_PWRITE
- *
- * loop_eagain does a retry loop on EAGAIN if set
- * loop_eintr does a retry loop on EINTR if set
- *
- * race conditions on non-libc pread/pwrite:
- * if a file offset changes, abort, to mitage.
- *
- * off_reset 1: reset the file offset *once* if
- * a change was detected, assuming
- * nothing else is touching it now
- * off_reset 0: never reset if changed
- */
-
-ssize_t
-prw(int fd, void *mem, size_t nrw,
- off_t off, int rw_type,
- int loop_eagain, int loop_eintr,
- int off_reset)
-{
- ssize_t rval;
- ssize_t r;
- int positional_rw;
- struct stat st;
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
- off_t verified;
- off_t off_orig;
- off_t off_last;
-#endif
- int saved_errno = errno;
-
- if (io_args(fd, mem, nrw, off, rw_type)
- == -1)
- goto err_prw;
-
- r = -1;
-
- /* do not use loop_eagain on
- * normal files
- */
-
- if (!loop_eagain) {
- /* check whether the file
- * changed
- */
-
- if (check_file(fd, &st) == -1)
- goto err_prw;
- }
-
- if (rw_type >= IO_PREAD)
- positional_rw = 1; /* pread/pwrite */
- else
- positional_rw = 0; /* read/write */
-
-try_rw_again:
-
- if (!positional_rw) {
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
-real_pread_pwrite:
-#endif
- if (rw_type == IO_WRITE)
- r = write(fd, mem, nrw);
- else if (rw_type == IO_READ)
- r = read(fd, mem, nrw);
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
- else if (rw_type == IO_PWRITE)
- r = pwrite(fd, mem, nrw, off);
- else if (rw_type == IO_PREAD)
- r = pread(fd, mem, nrw, off);
-#endif
-
- if (r == -1 && (errno == try_err(loop_eintr, EINTR)
- || errno == try_err(loop_eagain, EAGAIN)))
- goto try_rw_again;
-
- rval = rw_over_nrw(r, nrw);
- if (rval < 0)
- goto err_prw;
-
- errno = saved_errno;
-
- return rval;
- }
-
-#if defined(REAL_POS_IO) && \
- REAL_POS_IO > 0
- goto real_pread_pwrite;
-#else
- if ((off_orig = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr)) == (off_t)-1) {
- r = -1;
- } else if (lseek_on_eintr(fd, off, SEEK_SET,
- loop_eagain, loop_eintr) == (off_t)-1) {
- r = -1;
- } else {
- verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr);
-
- /* abort if the offset changed,
- * indicating race condition. if
- * off_reset enabled, reset *ONCE*
- */
-
- if (off_reset && off != verified)
- lseek_on_eintr(fd, off, SEEK_SET,
- loop_eagain, loop_eintr);
-
- do {
- /* check offset again, repeatedly.
- * even if off_reset is set, this
- * aborts if offsets change again
- */
-
- verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
- loop_eagain, loop_eintr);
-
- if (off != verified) {
-
- errno = EBUSY;
- goto err_prw;
- }
-
- if (rw_type == IO_PREAD)
- r = read(fd, mem, nrw);
- else if (rw_type == IO_PWRITE)
- r = write(fd, mem, nrw);
-
- if (rw_over_nrw(r, nrw) == -1)
- break;
-
- } while (r == -1 &&
- (errno == try_err(loop_eintr, EINTR) ||
- errno == try_err(loop_eagain, EAGAIN)));
- }
-
- saved_errno = errno;
-
- off_last = lseek_on_eintr(fd, off_orig, SEEK_SET,
- loop_eagain, loop_eintr);
-
- if (off_last != off_orig) {
- errno = saved_errno;
- goto err_prw;
- }
-
- errno = saved_errno;
-
- rval = rw_over_nrw(r, nrw);
- if (rval < 0)
- goto err_prw;
-
- errno = saved_errno;
-
- return rval;
-
-#endif
-
-err_prw:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-int
-io_args(int fd, void *mem, size_t nrw,
- off_t off, int rw_type)
-{
- int saved_errno = errno;
-
- /* obviously */
- if (mem == NULL) {
-
- errno = EFAULT;
- goto err_io_args;
- }
-
- /* uninitialised fd */
- if (fd < 0) {
-
- errno = EBADF;
- goto err_io_args;
- }
-
- /* negative offset */
- if (off < 0) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- /* prevent zero-byte rw */
- if (!nrw)
- goto err_io_args;
-
- /* prevent overflow */
- if (nrw > (size_t)SSIZE_MAX) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- /* prevent overflow */
- if (((size_t)off + nrw) < (size_t)off) {
-
- errno = ERANGE;
- goto err_io_args;
- }
-
- if (rw_type > IO_PWRITE) {
-
- errno = EINVAL;
- goto err_io_args;
- }
-
- errno = saved_errno;
-
- return 0;
-
-err_io_args:
-
- if (errno == saved_errno)
- errno = EINVAL;
-
- return -1;
-}
-
-int
-check_file(int fd, struct stat *st)
-{
- int saved_errno = errno;
-
- if (fd < 0) {
- errno = EBADF;
- goto err_is_file;
- }
-
- if (st == NULL) {
- errno = EFAULT;
- goto err_is_file;
- }
-
- if (fstat(fd, st) == -1)
- goto err_is_file;
-
- if (!S_ISREG(st->st_mode)) {
-
- errno = EBADF;
- goto err_is_file;
- }
-
- errno = saved_errno;
-
- return 0;
-
-err_is_file:
-
- if (errno == saved_errno)
- errno = EINVAL;
-
- return -1;
-}
-
-/* POSIX can say whatever it wants.
- * specification != implementation
- */
-
-ssize_t
-rw_over_nrw(ssize_t r, size_t nrw)
-{
- int saved_errno = errno;
-
- /* not a libc bug, but we
- * don't like the number zero
- */
- if (!nrw)
- goto err_rw_over_nrw;
-
- if (r == -1)
- return r;
-
- if ((size_t)
- r > SSIZE_MAX) {
-
- /* Theoretical buggy libc
- * check. Extremely academic.
- *
- * Specifications never
- * allow this return value
- * to exceed SSIZE_T, but
- * spec != implementation
- *
- * Check this after using
- * [p]read() or [p]write()
- *
- * NOTE: here, we assume
- * ssize_t integers are the
- * same size as SSIZE_T
- */
-
- errno = ERANGE;
- goto err_rw_over_nrw;
- }
-
- /* Theoretical buggy libc:
- * Should never return a number of
- * bytes above the requested length.
- */
- if ((size_t)r > nrw) {
-
- errno = ERANGE;
- goto err_rw_over_nrw;
- }
-
- errno = saved_errno;
-
- return r;
-
-err_rw_over_nrw:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
-off_t
-lseek_on_eintr(int fd, off_t off, int whence,
- int loop_eagain, int loop_eintr)
-{
- off_t old;
-
- old = -1;
-
- do {
- old = lseek(fd, off, whence);
- } while (old == (off_t)-1 && (
- errno == try_err(loop_eintr, EINTR) ||
- errno == try_err(loop_eintr, ETXTBSY) ||
- errno == try_err(loop_eagain, EAGAIN) ||
- errno == try_err(loop_eagain, EWOULDBLOCK)));
-
- return old;
-}
-#endif
-
-int
-try_err(int loop_err, int errval)
-{
- if (loop_err)
- return errval;
-
- return -1;
-}
-
-int
-close_on_eintr(int fd)
-{
- int r;
- int saved_errno = errno;
-
- do {
- r = close(fd);
- } while (r == -1 && (
- errno == EINTR || errno == EAGAIN ||
- errno == EWOULDBLOCK || errno == ETXTBSY));
-
- if (r >= 0)
- errno = saved_errno;
-
- return r;
-}
-
-int
-fsync_on_eintr(int fd)
-{
- int r;
- int saved_errno = errno;
-
- do {
- r = fsync(fd);
- } while (r == -1 && (errno == EINTR || errno == EAGAIN ||
- errno == ETXTBSY || errno == EWOULDBLOCK));
-
- if (r >= 0)
- errno = saved_errno;
-
- return r;
-}
-
-int
-fs_rename_at(int olddirfd, const char *old,
- int newdirfd, const char *new)
-{
- if (new == NULL || old == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- if (olddirfd < 0 || newdirfd < 0) {
-
- errno = EBADF;
- return -1;
- }
-
- return renameat(olddirfd, old, newdirfd, new);
-}
-
-/* secure open, based on
- * relative path to root
- *
- * always a fixed fd for /
- * see: rootfs()
- */
-int
-fs_open(const char *path, int flags)
-{
- struct filesystem *fs;
- const char *rel;
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
- if (path[0] != '/') {
- errno = EINVAL;
- return -1;
- }
-
- fs = rootfs();
- if (fs == NULL)
- return -1;
-
- rel = path + 1;
-
- return fs_resolve_at(fs->rootfd, rel, flags);
-}
-
-/* singleton function
- * that returns a fixed
- * descriptor of /
- *
- * used throughout, for
- * repeated integrity checks
- */
-struct filesystem *
-rootfs(void)
-{
- static struct filesystem global_fs;
- static int fs_initialised = 0;
-
- if (!fs_initialised) {
-
- global_fs.rootfd =
- open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
-
- if (global_fs.rootfd < 0)
- return NULL;
-
- fs_initialised = 1;
- }
-
- return &global_fs;
-}
-
-/* filesystem sandboxing.
- * (in userspace)
- */
-int
-fs_resolve_at(int dirfd, const char *path, int flags)
-{
- int nextfd = -1;
- int curfd;
- const char *p;
- char name[256];
- int saved_errno = errno;
- int r;
- int is_last;
-
- if (dirfd < 0 || path == NULL || *path == '\0') {
- errno = EINVAL;
- return -1;
- }
-
- p = path;
- curfd = dirfd; /* start here */
-
- for (;;) {
- r = fs_next_component(&p, name, sizeof(name));
- if (r < 0)
- goto err;
- if (r == 0)
- break;
-
- is_last = (*p == '\0');
-
- nextfd = fs_open_component(curfd, name, flags, is_last);
- if (nextfd < 0)
- goto err;
-
- /* close previous fd IF it is not the original input */
- if (curfd != dirfd) {
- (void) close_on_eintr(curfd);
- }
-
- curfd = nextfd;
- nextfd = -1;
- }
-
- errno = saved_errno;
- return curfd;
-
-err:
- saved_errno = errno;
-
- if (nextfd >= 0)
- (void) close_on_eintr(nextfd);
-
- /* close curfd only if it's not the original */
- if (curfd != dirfd && curfd >= 0)
- (void) close_on_eintr(curfd);
-
- errno = saved_errno;
- return -1;
-}
-
-int
-fs_next_component(const char **p,
- char *name, size_t namesz)
-{
- const char *s = *p;
- size_t len = 0;
-#if defined(PATH_LEN) && \
-(PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
-
- while (*s == '/')
- s++;
-
- if (*s == '\0') {
- *p = s;
- return 0;
- }
-
- while (s[len] != '/' && s[len] != '\0')
- len++;
-
- if (len == 0 || len >= namesz ||
- len >= maxlen) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- memcpy(name, s, len);
- name[len] = '\0';
-
- /* reject . and .. */
- if ((name[0] == '.' && name[1] == '\0') ||
- (name[0] == '.' && name[1] == '.' && name[2] == '\0')) {
- errno = EPERM;
- return -1;
- }
-
- *p = s + len;
- return 1;
-}
-
-int
-fs_open_component(int dirfd, const char *name,
- int flags, int is_last)
-{
- int fd;
- struct stat st;
-
- fd = openat2p(dirfd, name,
- (is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
- O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
-
- /* the patient always lies
- */
- if (!is_last) {
-
- if (fd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (fstat(fd, &st) < 0)
- return -1;
-
- if (!S_ISDIR(st.st_mode)) {
-
- (void) close_on_eintr(fd);
- errno = ENOTDIR;
- return -1;
- }
- }
-
- return fd;
-}
-
-int
-fs_dirname_basename(const char *path,
- char **dir, char **base,
- int allow_relative)
-{
- char *buf;
- char *slash;
- size_t len;
- int rval;
-#if defined(PATH_LEN) && \
-(PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
-
- if (path == NULL || dir == NULL || base == NULL)
- return -1;
-
- if (slen(path, maxlen, &len) < 0)
- return -1;
-
- buf = malloc(len + 1);
- if (buf == NULL)
- return -1;
-
- memcpy(buf, path, len + 1);
-
- /* strip trailing slashes */
- while (len > 1 && buf[len - 1] == '/')
- buf[--len] = '\0';
-
- slash = strrchr(buf, '/');
-
- if (slash) {
-
- *slash = '\0';
- *dir = buf;
- *base = slash + 1;
-
- if (**dir == '\0') {
- (*dir)[0] = '/';
- (*dir)[1] = '\0';
- }
- } else if (allow_relative) {
-
- *dir = strdup(".");
- *base = buf;
- } else {
- errno = EINVAL;
- free(buf);
- return -1;
- }
-
- return 0;
-}
-
-/* portable wrapper for use of openat2 on linux,
- * with fallback for others e.g. openbsd
- *
- * BONUS: arg checks
- * TODO: consider EINTR/EAGAIN retry loop
- */
-int
-openat2p(int dirfd, const char *path,
- int flags, mode_t mode)
-{
-#ifdef __linux__
- struct open_how how = {
- .flags = flags,
- .mode = mode,
- .resolve =
- RESOLVE_BENEATH |
- RESOLVE_NO_SYMLINKS |
- RESOLVE_NO_MAGICLINKS
- };
- int saved_errno = errno;
- int rval;
-#endif
-
- if (dirfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-retry:
- errno = 0;
-
-#ifdef __linux__
- /* more secure than regular openat,
- * but linux-only at the time of writing
- */
- rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
-#else
- /* less secure, but e.g. openbsd
- * doesn't have openat2 yet
- */
- rval = openat(dirfd, path, flags, mode);
-#endif
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
-
- if (rval >= 0)
- errno = saved_errno;
-
- return rval;
-}
-
-int
-mkdirat_on_eintr( /* <-- say that 10 times to please the demon */
- int dirfd,
- const char *path, mode_t mode)
-{
- int saved_errno = errno;
- int rval;
-
- if (dirfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (path == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-retry:
- errno = 0;
- rval = mkdirat(dirfd, path, mode);
-
- if (rval == -1 && (
- errno == EINTR ||
- errno == EAGAIN ||
- errno == EWOULDBLOCK ||
- errno == ETXTBSY))
- goto retry;
-
- if (rval >= 0)
- errno = saved_errno;
-
- return rval;
-}
diff --git a/util/nvmutil/lib/io.c b/util/nvmutil/lib/io.c
deleted file mode 100644
index 94bde87e..00000000
--- a/util/nvmutil/lib/io.c
+++ /dev/null
@@ -1,673 +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();
- 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 | O_NOCTTY, &f->gbe_st);
-
- if (f->gbe_st.st_nlink > 1)
- err(EINVAL,
- "%s: warning: file has multiple (%lu) hard links\n",
- f->fname, (size_t)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();
- struct xfile *f = &x->f;
-
- read_file();
-
- if (f->gbe_file_size == SIZE_8KB)
- return;
-
- memcpy(f->buf + (size_t)GBE_PART_SIZE,
- f->buf + (size_t)(f->gbe_file_size >> 1),
- (size_t)GBE_PART_SIZE);
-}
-
-void
-read_file(void)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- struct stat _st;
- ssize_t _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();
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- size_t 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(size_t p, int rw_type,
- const char *rw_type_str)
-{
- struct xstate *x = xstatus();
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- ssize_t rval;
-
- off_t file_offset;
-
- size_t 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, (size_t)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, (size_t)p);
-
- if ((size_t)rval != gbe_rw_size)
- err(EIO, "%s: partial %s: part %lu",
- f->fname, rw_type_str, (size_t)p);
-}
-
-void
-write_to_gbe_bin(void)
-{
- struct xstate *x = xstatus();
- 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) {
- f->tmp_fd = -1;
-
- fprintf(stderr, "FAIL: %s: close\n", f->tname);
- f->io_err_gbe_bin = 1;
- }
- f->tmp_fd = -1;
-
- if (close_on_eintr(f->gbe_fd) == -1) {
- f->gbe_fd = -1;
-
- fprintf(stderr, "FAIL: %s: close\n", f->fname);
- f->io_err_gbe_bin = 1;
- }
- f->gbe_fd = -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;
- }
-
- 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(size_t p)
-{
- struct xstate *x = xstatus();
- struct commands *cmd = &x->cmd[x->i];
- struct xfile *f = &x->f;
-
- ssize_t rval;
-
- size_t 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 ((size_t)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();
- struct xfile *f = &x->f;
-
- size_t 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, (size_t)p);
- if (f->rw_check_partial_read[p])
- fprintf(stderr,
- "%s: partial pread: p%lu (post-verification)\n",
- f->fname, (size_t)p);
- if (f->rw_check_bad_part[p])
- fprintf(stderr,
- "%s: pwrite: corrupt write on p%lu\n",
- f->fname, (size_t)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, (size_t)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",
- (size_t)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 = &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
- */
- if (new_tmpfile(&dest_fd, &f->fname) < 1)
- goto ret_gbe_mv;
- 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) {
- dest_fd = -1;
- goto ret_gbe_mv;
- }
- dest_fd = -1;
-
- 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;
- }
-
- if (dest_tmp != NULL) {
- free(dest_tmp);
- dest_tmp = NULL;
- }
-
- dest_tmp = NULL;
-
-ret_gbe_mv:
-
- /* TODO: this whole section is bloat.
- it can be generalised
- */
-
- if (f->gbe_fd > -1) {
- if (close_on_eintr(f->gbe_fd) < 0) {
- f->gbe_fd = -1;
- rval = -1;
- }
- f->gbe_fd = -1;
-
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- rval = -1;
- }
- }
-
- if (f->tmp_fd > -1) {
- if (close_on_eintr(f->tmp_fd) < 0) {
- f->tmp_fd = -1;
- 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(size_t p, const char *f_op)
-{
- struct xstate *x = xstatus();
- 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 + (size_t)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(size_t p, const char *f_op)
-{
- struct xstate *x = xstatus();
- 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(size_t p, const char *f_op, const char *d_type,
- off_t nsize, off_t ncmp)
-{
- struct xstate *x = xstatus();
- 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;
-}
-
-ssize_t
-rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
- off_t off, int rw_type)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- ssize_t 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 ((size_t)(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 > (size_t)(f->gbe_file_size - off))
- goto err_rw_gbe_file_exact;
-
- if (nrw > (size_t)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/mkhtemp.c b/util/nvmutil/lib/mkhtemp.c
deleted file mode 100644
index 2fcb894e..00000000
--- a/util/nvmutil/lib/mkhtemp.c
+++ /dev/null
@@ -1,1133 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Hardened mktemp (be nice to the demon).
- */
-
-#if defined(__linux__) && !defined(_GNU_SOURCE)
-/* for openat2 syscall on linux */
-#define _GNU_SOURCE 1
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* for openat2: */
-#ifdef __linux__
-#include <linux/openat2.h>
-#include <sys/syscall.h>
-#endif
-
-#include "../include/common.h"
-
-int
-new_tmpfile(int *fd, char **path)
-{
- return new_tmp_common(fd, path, MKHTEMP_FILE);
-}
-
-int
-new_tmpdir(int *fd, char **path)
-{
- return new_tmp_common(fd, path, MKHTEMP_DIR);
-}
-
-static int
-new_tmp_common(int *fd, char **path, int type)
-{
-#if defined(PATH_LEN) && \
- (PATH_LEN) >= 256
- size_t maxlen = PATH_LEN;
-#else
- size_t maxlen = 4096;
-#endif
- struct stat st;
-
- char suffix[] =
- "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
- char *tmpdir = NULL;
-
- int close_errno;
- size_t dirlen;
- size_t destlen;
- char *dest = NULL; /* final path (will be written into "path") */
- int saved_errno = errno;
- int dirfd = -1;
- const char *fname = NULL;
-
- struct stat st_dir_initial;
-
- if (path == NULL || fd == NULL) {
- errno = EFAULT;
- goto err;
- }
-
- /* don't mess with someone elses file */
- if (*fd >= 0) {
- errno = EEXIST;
- goto err;
- }
-
- /* regarding **path:
- * the pointer (to the pointer)
- * must nott be null, but we don't
- * care about the pointer it points
- * to. you should expect it to be
- * replaced upon successful return
- *
- * (on error, it will not be touched)
- */
-
-
- *fd = -1;
-
-#if defined(PERMIT_NON_STICKY_ALWAYS) && \
- ((PERMIT_NON_STICKY_ALWAYS) > 0)
- tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS);
-#else
- tmpdir = env_tmpdir(0);
-#endif
- if (tmpdir == NULL)
- goto err;
-
- if (slen(tmpdir, maxlen, &dirlen) < 0)
- goto err;
- if (*tmpdir == '\0')
- goto err;
- if (*tmpdir != '/')
- goto err;
-
- /* sizeof adds an extra byte, useful
- * because we also want '.' or '/'
- */
- destlen = dirlen + sizeof(suffix);
- if (destlen > maxlen - 1) {
- errno = EOVERFLOW;
- goto err;
- }
-
- dest = malloc(destlen + 1);
- if (dest == NULL) {
- errno = ENOMEM;
- goto err;
- }
-
- memcpy(dest, tmpdir, dirlen);
- *(dest + dirlen) = '/';
- memcpy(dest + dirlen + 1, suffix, sizeof(suffix) - 1);
- *(dest + destlen) = '\0';
-
- fname = dest + dirlen + 1;
-
- dirfd = fs_open(tmpdir,
- O_RDONLY | O_DIRECTORY);
- if (dirfd < 0)
- goto err;
-
- if (fstat(dirfd, &st_dir_initial) < 0)
- goto err;
-
- *fd = mkhtemp(fd, &st, dest, dirfd,
- fname, &st_dir_initial, type);
- if (*fd < 0)
- goto err;
-
- if (dirfd >= 0) {
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
- dirfd = -1;
- }
-
- errno = saved_errno;
- *path = dest;
-
- return 0;
-
-err:
-
- if (errno != saved_errno)
- saved_errno = errno;
- else
- saved_errno = errno = EIO;
-
- if (dest != NULL) {
- free(dest);
- dest = NULL;
- }
-
- if (dirfd >= 0) {
- close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = close_errno;
- dirfd = -1;
- }
-
- if (*fd >= 0) {
- close_errno = errno;
- (void) close_on_eintr(*fd);
- errno = close_errno;
- *fd = -1;
- }
-
- errno = saved_errno;
- return -1;
-}
-
-
-/* hardened TMPDIR parsing
- */
-
-char *
-env_tmpdir(int bypass_all_sticky_checks)
-{
- char *t;
- int allow_noworld_unsticky;
- int saved_errno = errno;
-
- t = getenv("TMPDIR");
-
- if (t != NULL && *t != '\0') {
-
- if (tmpdir_policy(t,
- &allow_noworld_unsticky) < 0) {
- errno = EPERM;
- return NULL; /* errno already set */
- }
-
- if (!world_writeable_and_sticky(t,
- allow_noworld_unsticky,
- bypass_all_sticky_checks)) {
- errno = EPERM;
- return NULL;
- }
-
- errno = saved_errno;
- return t;
- }
-
- allow_noworld_unsticky = 0;
-
- if (world_writeable_and_sticky("/tmp",
- allow_noworld_unsticky,
- bypass_all_sticky_checks)) {
-
- errno = saved_errno;
- return "/tmp";
- }
-
- if (world_writeable_and_sticky("/var/tmp",
- allow_noworld_unsticky,
- bypass_all_sticky_checks)) {
-
- errno = saved_errno;
- return "/var/tmp";
- }
-
- if (errno == saved_errno)
- errno = EPERM;
-
- return NULL;
-}
-
-int
-tmpdir_policy(const char *path,
- int *allow_noworld_unsticky)
-{
- int saved_errno = errno;
- int r;
-
- if (path == NULL ||
- allow_noworld_unsticky == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- *allow_noworld_unsticky = 1;
-
- r = same_dir(path, "/tmp");
- if (r < 0)
- goto err_tmpdir_policy;
- if (r > 0)
- *allow_noworld_unsticky = 0;
-
- r = same_dir(path, "/var/tmp");
- if (r < 0)
- goto err_tmpdir_policy;
- if (r > 0)
- *allow_noworld_unsticky = 0;
-
- errno = saved_errno;
- return 0;
-
-err_tmpdir_policy:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-int
-same_dir(const char *a, const char *b)
-{
- int fd_a = -1;
- int fd_b = -1;
-
- struct stat st_a;
- struct stat st_b;
-
- int saved_errno = errno;
- int rval_scmp;
-
-#if defined(PATH_LEN) && \
- (PATH_LEN) >= 256
- size_t maxlen = (PATH_LEN);
-#else
- size_t maxlen = 4096;
-#endif
-
- /* optimisation: if both dirs
- are the same, we don't need
- to check anything. sehr schnell:
- */
- if (scmp(a, b, maxlen, &rval_scmp) < 0)
- goto err_same_dir;
- /* bonus: scmp checks null for us */
- if (rval_scmp == 0)
- goto success_same_dir;
-
- fd_a = fs_open(a, O_RDONLY | O_DIRECTORY);
- if (fd_a < 0)
- goto err_same_dir;
-
- fd_b = fs_open(b, O_RDONLY | O_DIRECTORY);
- if (fd_b < 0)
- goto err_same_dir;
-
- if (fstat(fd_a, &st_a) < 0)
- goto err_same_dir;
-
- if (fstat(fd_b, &st_b) < 0)
- goto err_same_dir;
-
- if (st_a.st_dev == st_b.st_dev &&
- st_a.st_ino == st_b.st_ino) {
-
- (void) close_on_eintr(fd_a);
- (void) close_on_eintr(fd_b);
-
-success_same_dir:
-
- /* SUCCESS
- */
-
- errno = saved_errno;
- return 1;
- }
-
- (void) close_on_eintr(fd_a);
- (void) close_on_eintr(fd_b);
-
- /* FAILURE (logical)
- */
-
- errno = saved_errno;
- return 0;
-
-err_same_dir:
-
- /* FAILURE (probably syscall)
- */
-
- if (fd_a >= 0)
- (void) close_on_eintr(fd_a);
- if (fd_b >= 0)
- (void) close_on_eintr(fd_b);
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-/* bypass_all_sticky_checks: if set,
- disable stickiness checks (libc behaviour)
- (if not set: leah behaviour)
-
- allow_noworld_unsticky:
- allow non-sticky files if not world-writeable
- (still block non-sticky in standard TMPDIR)
-*/
-int
-world_writeable_and_sticky(
- const char *s,
- int allow_noworld_unsticky,
- int bypass_all_sticky_checks)
-{
- struct stat st;
- int dirfd = -1;
-
- int saved_errno = errno;
-
- if (s == NULL || *s == '\0') {
- errno = EINVAL;
- goto sticky_hell;
- }
-
- /* mitigate symlink attacks*
- */
- dirfd = fs_open(s, O_RDONLY | O_DIRECTORY);
- if (dirfd < 0)
- goto sticky_hell;
-
- if (fstat(dirfd, &st) < 0)
- goto sticky_hell;
-
- if (!S_ISDIR(st.st_mode)) {
- errno = ENOTDIR;
- goto sticky_hell;
- }
-
- /* must be fully executable
- * by everyone, or openat2
- * becomes unreliable**
- */
- if (!(st.st_mode & S_IXUSR) ||
- !(st.st_mode & S_IXGRP) ||
- !(st.st_mode & S_IXOTH)) {
-
- errno = EACCES;
- goto sticky_hell;
- }
-
- /* *normal-**ish mode (libc):
- */
-
- if (bypass_all_sticky_checks)
- goto sticky_heaven; /* normal == no security */
-
- /* unhinged leah mode:
- */
-
- if (st.st_mode & S_IWOTH) { /* world writeable */
-
- /* if world-writeable, only
- * allow sticky files
- */
- if (st.st_mode & S_ISVTX)
- goto sticky_heaven; /* sticky */
-
- errno = EPERM;
- goto sticky_hell; /* not sticky */
- }
-
- /* non-world-writeable, so
- * stickiness is do-not-care
- */
- if (allow_noworld_unsticky)
- goto sticky_heaven; /* sticky! */
-
- goto sticky_hell; /* heaven visa denied */
-
-sticky_heaven:
-/* i like the one in hamburg better */
-
- if (dirfd >= 0)
- (void) close_on_eintr(dirfd);
-
- errno = saved_errno;
-
- return 1;
-
-sticky_hell:
-
- if (errno == saved_errno)
- errno = EPERM;
-
- saved_errno = errno;
-
- if (dirfd >= 0)
- (void) close_on_eintr(dirfd);
-
- errno = saved_errno;
-
- return 0;
-}
-
-/* mk(h)temp - hardened mktemp.
- * like mkstemp, but (MUCH) harder.
- *
- * designed to resist TOCTOU attacsk
- * e.g. directory race / symlink attack
- *
- * extremely strict and even implements
- * some limited userspace-level sandboxing,
- * similar to openbsd unveil (which you
- * can also use with this in your program)
- *
- * supports both files and directories.
- * file: type = MKHTEMP_FILE (0)
- * dir: type = MKHTEMP_DIR (1)
- *
- * DESIGN NOTES:
- *
- * caller is expected to handle
- * cleanup e.g. free(), on *st,
- * *template, *fname (all of the
- * pointers). ditto fd cleanup.
- *
- * some limited cleanup is
- * performed here, e.g. directory/file
- * cleanup on error in mkhtemp_try_create
- *
- * we only check if these are not NULL,
- * and the caller is expected to take
- * care; without too many conditions,
- * these functions are more flexible,
- * but some precauttions are taken:
- *
- * when used via the function new_tmpfile
- * or new_tmpdir, thtis is extremely strict,
- * much stricter than previous mktemp
- * variants. for example, it is much
- * stricter about stickiness on world
- * writeable directories, and it enforces
- * file ownership under hardened mode
- * (only lets you touch your own files/dirs)
- */
-int
-mkhtemp(int *fd,
- struct stat *st,
- char *template,
- int dirfd,
- const char *fname,
- struct stat *st_dir_initial,
- int type)
-{
- size_t len = 0;
- size_t xc = 0;
- size_t fname_len = 0;
-
- char *fname_copy = NULL;
- char *p;
-
- size_t retries;
-
- int close_errno;
- int saved_errno = errno;
-
-#if defined(PATH_LEN) && \
- (PATH_LEN) >= 256
- size_t max_len = PATH_LEN;
-#else
- size_t max_len = 4096;
-#endif
- int r;
- char *end;
-
- if (fd == NULL ||
- template == NULL ||
- fname == NULL ||
- st_dir_initial == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- /* we do not mess with an
- open descriptor.
- */
- if (*fd >= 0) {
- errno = EEXIST; /* leave their file alone */
- return -1;
- }
-
- if (dirfd < 0) {
- errno = EBADF;
- return -1;
- }
-
- if (slen(template, max_len, &len) < 0)
- return -1;
-
- if (len >= max_len) {
- errno = EMSGSIZE;
- return -1;
- }
-
- if (slen(fname, max_len, &fname_len) < 0)
- return -1;
-
- if (fname_len == 0) {
- errno = EINVAL;
- return -1;
- }
-
- if (strrchr(fname, '/') != NULL) {
- errno = EINVAL;
- return -1;
- }
-
- /* count trailing X */
- end = template + len;
- while (end > template && *--end == 'X')
- xc++;
-
- if (xc < 12 || xc > len) {
- errno = EINVAL;
- return -1;
- }
-
- if (fname_len > len) {
- errno = EOVERFLOW;
- return -1;
- }
-
- if (memcmp(fname,
- template + len - fname_len,
- fname_len) != 0) {
- errno = EINVAL;
- return -1;
- }
-
- fname_copy = malloc(fname_len + 1);
- if (fname_copy == NULL) {
- errno = ENOMEM;
- goto err;
- }
-
- /* fname_copy = suffix region only; p points to trailing XXXXXX */
- memcpy(fname_copy,
- template + len - fname_len,
- fname_len + 1);
- p = fname_copy + fname_len - xc;
-
- for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
-
- r = mkhtemp_try_create(
- dirfd,
- st_dir_initial,
- fname_copy,
- p,
- xc,
- fd,
- st,
- type);
-
- if (r == 0) {
- if (retries >= MKHTEMP_SPIN_THRESHOLD) {
- /* usleep can return EINTR */
- close_errno = errno;
- usleep((useconds_t)rlong() & 0x3FF);
- errno = close_errno;
- }
- continue;
- }
- if (r < 0)
- goto err;
-
- /* success: copy final name back */
- memcpy(template + len - fname_len,
- fname_copy, fname_len);
-
- errno = saved_errno;
- goto success;
- }
-
- errno = EEXIST;
-
-err:
- if (*fd >= 0) {
- close_errno = errno;
- (void)close_on_eintr(*fd);
- errno = close_errno;
- *fd = -1;
- }
-
-success:
-
- if (fname_copy != NULL)
- free(fname_copy);
-
- return (*fd >= 0) ? *fd : -1;
-}
-
-static int
-mkhtemp_try_create(int dirfd,
- struct stat *st_dir_initial,
- char *fname_copy,
- char *p,
- size_t xc,
- int *fd,
- struct stat *st,
- int type)
-{
- struct stat st_open;
- int saved_errno = errno;
- int close_errno;
- int rval = -1;
-
- int file_created = 0;
- int dir_created = 0;
-
- if (fd == NULL || st == NULL || p == NULL || fname_copy == NULL ||
- st_dir_initial == NULL) {
- errno = EFAULT;
- goto err;
- } else if (*fd >= 0) { /* do not mess with someone else's file */
- errno = EEXIST;
- goto err;
- }
-
- if (mkhtemp_fill_random(p, xc) < 0)
- goto err;
-
- if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
- goto err;
-
- if (type == MKHTEMP_FILE) {
- *fd = openat2p(dirfd, fname_copy,
- O_RDWR | O_CREAT | O_EXCL |
- O_NOFOLLOW | O_CLOEXEC | O_NOCTTY,
- 0600);
-
- /* O_CREAT and O_EXCL guarantees
- * creation upon success
- */
- if (*fd >= 0)
- file_created = 1;
-
- } else { /* dir: MKHTEMP_DIR */
-
- if (mkdirat_on_eintr(dirfd, fname_copy, 0700) < 0)
- goto err;
-
- /* ^ NOTE: opening the directory here
- will never set errno=EEXIST,
- since we're not creating it */
-
- dir_created = 1;
-
- /* do it again (mitigate directory race) */
- if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
- goto err;
-
- *fd = openat2p(dirfd, fname_copy,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
- if (*fd < 0)
- goto err;
-
- if (fstat(*fd, &st_open) < 0)
- goto err;
-
- if (!S_ISDIR(st_open.st_mode)) {
- errno = ENOTDIR;
- goto err;
- }
-
- /* NOTE: could check nlink count here,
- * but it's not very reliable here. skipped.
- */
-
- if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
- goto err;
-
- }
-
- /* NOTE: openat2p and mkdirat_on_eintr
- * already handled EINTR/EAGAIN looping
- */
-
- if (*fd < 0) {
- if (errno == EEXIST) {
-
- rval = 0;
- goto out;
- }
- goto err;
- }
-
- if (fstat(*fd, &st_open) < 0)
- goto err;
-
- if (type == MKHTEMP_FILE) {
-
- if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
- goto err;
-
- if (secure_file(fd, st, &st_open,
- O_APPEND, 1, 1, 0600) < 0) /* WARNING: only once */
- goto err;
-
- } else { /* dir: MKHTEMP_DIR */
-
- if (fd_verify_identity(*fd, &st_open, st_dir_initial) < 0)
- goto err;
-
- if (!S_ISDIR(st_open.st_mode)) {
- errno = ENOTDIR;
- goto err;
- }
-
- if (is_owner(&st_open) < 0)
- goto err;
-
- /* group/world writeable */
- if (st_open.st_mode & (S_IWGRP | S_IWOTH)) {
- errno = EPERM;
- goto err;
- }
- }
-
- errno = saved_errno;
- rval = 1;
- goto out;
-
-err:
- close_errno = errno;
-
- if (fd != NULL && *fd >= 0) {
- (void) close_on_eintr(*fd);
- *fd = -1;
- }
-
- if (file_created)
- (void) unlinkat(dirfd, fname_copy, 0);
-
- if (dir_created)
- (void) unlinkat(dirfd, fname_copy, AT_REMOVEDIR);
-
- errno = close_errno;
- rval = -1;
-out:
- return rval;
-}
-
-int
-mkhtemp_fill_random(char *p, size_t xc)
-{
- size_t chx = 0;
- int rand_failures = 0;
-
- size_t r;
-
- int saved_rand_error = 0;
- static char ch[] =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
- /* clamp rand to prevent modulo bias
- * (reduced risk of entropy leak)
- */
- size_t limit = ((size_t)-1) - (((size_t)-1) % (sizeof(ch) - 1));
-
- int saved_errno = errno;
-
- if (p == NULL) {
- errno = EFAULT;
- goto err_mkhtemp_fill_random;
- }
-
- for (chx = 0; chx < xc; chx++) {
-
- do {
- saved_rand_error = errno;
- rand_failures = 0;
-retry_rand:
- errno = 0;
-
- /* on bsd: uses arc4random
- on linux: uses getrandom
- on OLD linux: /dev/urandom
- on old/other unix: /dev/urandom
- */
- r = rlong();
-
- if (errno > 0) {
- if (++rand_failures <= 8)
- goto retry_rand;
-
- goto err_mkhtemp_fill_random;
- }
-
- rand_failures = 0;
- errno = saved_rand_error;
-
- } while (r >= limit);
-
- p[chx] = ch[r % (sizeof(ch) - 1)];
- }
-
- errno = saved_errno;
- return 0;
-
-err_mkhtemp_fill_random:
-
- if (errno == saved_errno)
- errno = ECANCELED;
-
- return -1;
-}
-
-/* WARNING: **ONCE** per file.
- *
- * !!! DO NOT RUN TWICE PER FILE. BEWARE OF THE DEMON !!!
- * watch out for spikes!
- */
-int secure_file(int *fd,
- struct stat *st,
- struct stat *expected,
- int bad_flags,
- int check_seek,
- int do_lock,
- mode_t mode)
-{
- int flags;
- struct stat st_now;
- int saved_errno = errno;
- /* you have been warned */
- if (fd == NULL) {
- errno = EFAULT;
- goto err_demons;
- }
- if (*fd < 0) {
- errno = EBADF;
- goto err_demons;
- }
-
- if (st == NULL) {
- errno = EFAULT;
- goto err_demons;
- }
-
- flags = fcntl(*fd, F_GETFL);
-
- if (flags == -1)
- goto err_demons;
-
- if (bad_flags > 0) {
-
- /* e.g. O_APPEND breaks pwrite/pread
- * by allowing offsets to be ignored */
- if (flags & bad_flags) {
- errno = EPERM;
- goto err_demons;
- }
- }
-
- if (expected != NULL) {
- if (fd_verify_regular(*fd, expected, st) < 0)
- goto err_demons;
- } else {
- if (fstat(*fd, &st_now) == -1)
- goto err_demons;
-
- if (!S_ISREG(st_now.st_mode)) {
- errno = EBADF;
- goto err_demons;
- }
-
- *st = st_now;
- }
-
- if (check_seek) {
-
- /* check if it's seekable */
- if (lseek(*fd, 0, SEEK_CUR) == (off_t)-1)
- goto err_demons;
- }
-
- /* don't release the demon
- */
- if (st->st_nlink != 1) { /***********/
- /* ( >:3 ) */
- errno = ELOOP; /* /| |\ */ /* don't let him out */
- goto err_demons; /* / \ */
- /***********/
- }
-
- if (st->st_uid != geteuid() && /* someone else's file */
- geteuid() != 0) { /* override for root */
-
- errno = EPERM;
- goto err_demons;
- }
- if (is_owner(st) < 0)
- goto err_demons;
-
- /* world-writeable or group-ownership.
- * if these are set, then others could
- * modify the file (not secure)
- */
- if (st->st_mode & (S_IWGRP | S_IWOTH)) {
- errno = EPERM;
- goto err_demons;
- }
-
- if (do_lock) {
- if (lock_file(*fd, flags) == -1)
- goto err_demons;
-
- if (expected != NULL) {
- if (fd_verify_identity(*fd, expected, &st_now) < 0)
- goto err_demons;
- }
- }
-
- if (fchmod(*fd, mode) == -1)
- goto err_demons;
-
- errno = saved_errno;
- return 0;
-
-err_demons:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
-
-int
-fd_verify_regular(int fd,
- const struct stat *expected,
- struct stat *out)
-{
- if (fd_verify_identity(fd, expected, out) < 0)
- return -1;
-
- if (!S_ISREG(out->st_mode)) {
- errno = EBADF;
- return -1;
- }
-
- return 0; /* regular file */
-}
-
-int
-fd_verify_identity(int fd,
- const struct stat *expected,
- struct stat *out)
-{
- struct stat st_now;
- int saved_errno = errno;
-
- if (fd < 0 || expected == NULL) {
- errno = EFAULT;
- return -1;
- }
-
- if (fstat(fd, &st_now) < 0)
- return -1;
-
- if (st_now.st_dev != expected->st_dev ||
- st_now.st_ino != expected->st_ino) {
- errno = ESTALE;
- return -1;
- }
-
- if (out != NULL)
- *out = st_now;
-
- errno = saved_errno;
- return 0;
-}
-
-int
-fd_verify_dir_identity(int fd,
- const struct stat *expected)
-{
- struct stat st_now;
- int saved_errno = errno;
-
- if (fd < 0 || expected == NULL) {
- errno = EFAULT;
- return -1;
- }
-
- if (fstat(fd, &st_now) < 0)
- return -1;
-
- if (st_now.st_dev != expected->st_dev ||
- st_now.st_ino != expected->st_ino) {
- errno = ESTALE;
- return -1;
- }
-
- if (!S_ISDIR(st_now.st_mode)) {
- errno = ENOTDIR;
- return -1;
- }
-
- errno = saved_errno;
- return 0;
-}
-
-int
-is_owner(struct stat *st)
-{
- if (st == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
-#if ALLOW_ROOT_OVERRIDE
- if (st->st_uid != geteuid() && /* someone else's file */
- geteuid() != 0) { /* override for root */
-#else
- if (st->st_uid != geteuid()) { /* someone else's file */
-#endif /* and no root override */
- errno = EPERM;
- return -1;
- }
-
- return 0;
-}
-
-int
-lock_file(int fd, int flags)
-{
- struct flock fl;
- int saved_errno = errno;
-
- if (fd < 0) {
- errno = EBADF;
- goto err_lock_file;
- }
-
- if (flags < 0) {
- errno = EINVAL;
- goto err_lock_file;
- }
-
- 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)
- goto err_lock_file;
-
- saved_errno = errno;
- return 0;
-
-err_lock_file:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return -1;
-}
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c
deleted file mode 100644
index 343350b0..00000000
--- a/util/nvmutil/lib/num.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
- *
- * Numerical functions.
- */
-
-/*
-TODO: properly handle errno in this file
- */
-
-#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"
-
-/* TODO:
- * make this and errno handling more
- * flexible
-
- in particular:
- hextonum could be modified to
- write into a buffer instead,
- with the converted numbers,
- of an arbitrary length
- */
-unsigned short
-hextonum(char ch_s)
-{
- int saved_errno = errno;
-
- /* rlong() can return error,
- but preserves errno if no
- error. we need to detect
- this because it handles
- /dev/urandom sometimes
-
- therefore, if it's zero
- at start, we know if there
- was an err at the end, by
- return value zero, if errno
- was set; this is technically
- valid, since zero is also
- a valid random number!
-
- it's an edge case that i had
- to fix. i'll rewrite the code
- better later. for now, it
- should be ok.
- */
- errno = 0;
-
- unsigned char ch;
- size_t rval;
-
- ch = (unsigned char)ch_s;
-
- if ((unsigned int)(ch - '0') <= 9) {
-
- rval = ch - '0';
- goto hextonum_success;
- }
-
- ch |= 0x20;
-
- if ((unsigned int)(ch - 'a') <= 5) {
-
- rval = ch - 'a' + 10;
- goto hextonum_success;
- }
-
- if (ch == '?' || ch == 'x') {
-
- rval = rlong();
- if (errno > 0)
- goto err_hextonum;
-
- goto hextonum_success;
- }
-
- goto err_hextonum;
-
-hextonum_success:
-
- errno = saved_errno;
- return (unsigned short)rval & 0xf;
-
-err_hextonum:
-
- if (errno == saved_errno)
- errno = EINVAL;
- else
- return 17; /* 17 indicates getrandom/urandom fail */
-
- return 16; /* invalid character */
-
- /* caller just checks >15. */
-}
-
-/* Random numbers
- */
-
-/* when calling this: save errno
- * first, then set errno to zero.
- * on error, this function will
- * set errno and possibly return
- *
- * rlong also preserves errno
- * and leaves it unchanged on
- * success, so if you do it
- * right, you can detect error.
- * this is because it uses
- * /dev/urandom which can err.
- * ditto getrandom (EINTR),
- * theoretically.
- */
-
-size_t
-rlong(void)
-{
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
- defined(__FreeBSD__) || \
- defined(__NetBSD__) || defined(__APPLE__)
-
- int saved_errno = errno;
- size_t rval;
-
- arc4random_buf(&rval, sizeof(size_t));
-
- errno = saved_errno;
- return rval;
-#else
- static int fd = -1;
- static ssize_t nr = -1;
- static size_t 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
- size_t rval;
- ssize_t new_nr;
-
- int retries = 0;
- int max_retries = 100;
-
- int saved_errno = errno;
-
-#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
- */
-
- if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0) {
- errno = saved_errno;
- 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 err_rlong;
-
- if (nr < 0 || nr < (ssize_t)sizeof(size_t)) {
-
- if (fd < 0) {
-
- fd = open("/dev/urandom",
- O_RDONLY | O_BINARY | O_NOFOLLOW |
- O_CLOEXEC | O_NOCTTY);
-
-#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 | O_NOCTTY);
-#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 < (ssize_t)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(size_t));
-
- nr -= (ssize_t)sizeof(size_t);
- off += sizeof(size_t);
-
- errno = saved_errno;
-
- return rval;
-
-err_rlong:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return 0;
-
-#endif
-#else /* FALLBACK_RAND_1989 */
- /* your computer is from a museum
- */
- size_t mix = 0;
- int nr = 0;
- int saved_errno = errno;
-
- /* 100 times, for entropy
- */
- for (nr = 0; nr < 100; nr++)
- mix ^= fallback_rand_1989();
-
- errno = saved_errno;
- return mix;
-#endif
-}
-
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-int /* yes, linux is a fallback */
-fallback_rand_getrandom(void *buf, size_t len)
-{
- size_t off = 0;
- ssize_t rval = -1;
- int saved_errno = errno;
-
- if (!len) {
- errno = EINVAL;
- return -1;
- }
-
- if (buf == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-
- while (off < len) {
-
-#if defined(HAVE_GETRANDOM)
- rval = (ssize_t)getrandom((char *)buf + off, len - off, 0);
-#elif defined(HAVE_GETRANDOM_SYSCALL)
- rval = (ssize_t)syscall(SYS_getrandom,
- (char *)buf + off, len - off, 0);
-#endif
-
- if (rval < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- errno = EIO;
- return -1; /* unsupported by kernel */
- }
-
- if (rval == 0) {
- errno = EIO;
- return -1;
- }
-
- off += (size_t)rval;
- }
-
- errno = saved_errno;
- return 0;
-
-#else
- (void)buf;
- (void)len;
-
- errno = EIO;
- return -1;
-#endif
-}
-#endif
-#endif
-#else
-size_t
-fallback_rand_1989(void)
-{
- static size_t mix = 0;
- static size_t counter = 0;
-
- struct timeval tv;
-
- /* nobody should use this
- * (not crypto-safe)
- */
-
- gettimeofday(&tv, NULL);
-
- mix ^= (size_t)tv.tv_sec
- ^ (size_t)tv.tv_usec
- ^ (size_t)getpid()
- ^ (size_t)&mix
- ^ counter++
- ^ entropy_jitter();
-
- /*
- * Stack addresses can vary between
- * calls, thus increasing entropy.
- */
- mix ^= (size_t)&mix;
- mix ^= (size_t)&tv;
- mix ^= (size_t)&counter;
-
- return mix;
-}
-
-size_t
-entropy_jitter(void)
-{
- size_t mix;
-
- struct timeval a, b;
- ssize_t 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 = (ssize_t)(b.tv_usec - a.tv_usec);
- if (mix_diff < 0)
- mix_diff = -mix_diff;
-
- mix ^= (size_t)(mix_diff);
-
- mix ^= (size_t)&mix;
-
- }
-
- return mix;
-}
-#endif
-
-void
-check_bin(size_t a, const char *a_name)
-{
- if (a > 1)
- err(EINVAL, "%s must be 0 or 1, but is %lu",
- a_name, (size_t)a);
-}
diff --git a/util/nvmutil/lib/state.c b/util/nvmutil/lib/state.c
deleted file mode 100644
index 19d5cd8c..00000000
--- a/util/nvmutil/lib/state.c
+++ /dev/null
@@ -1,279 +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 *
-xstart(int argc, char *argv[])
-{
- static int first_run = 1;
- static int pre_init = 0;
-
- 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
-
- };
-
- if (!first_run) {
- if (pre_init)
- err_no_cleanup(ECANCELED,
- "Outside access to state during init");
-
- first_run = 0;
-
- return &us;
- }
-
- if (argc < 3)
- err_no_cleanup(EINVAL, "xstart: Too few arguments");
- if (argv == NULL)
- err_no_cleanup(EINVAL, "xstart: NULL argv");
-
- first_run = 0;
- pre_init = 1;
-
- us.f.buf = us.f.real_buf;
-
- us.argv0 = argv[0];
- us.f.fname = argv[1];
-
- if (new_tmpfile(&us.f.tmp_fd, &us.f.tname) < 0)
- err_no_cleanup(errno, "xstart: cannot create tmpfile");
-
- /* parse user command */
-/* TODO: CHECK ACCESSES VIA xstatus() */
- set_cmd(argc, argv);
- set_cmd_args(argc, argv);
-
- if (us.f.tname == NULL)
- err_no_cleanup(errno, "x->f.tname null");
- if (*us.f.tname == '\0')
- err_no_cleanup(errno, "x->f.tname empty");
-
- if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
- err_no_cleanup(errno, "%s: stat", us.f.tname);
-
- 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));
-
- pre_init = 0;
-
- return &us;
-}
-
-struct xstate *
-xstatus(void)
-{
- struct xstate *x = xstart(0, NULL);
-
- if (x == NULL)
- err_no_cleanup(EACCES, "NULL pointer to xstate");
-
- sanitize_command_list();
-
- return x;
-}
-
-/* early init functions that
- should not access state
- WARNING:
- does not do cleanup. only
- call this during pre-init
- */
-void
-err_no_cleanup(int nvm_errval, const char *msg, ...)
-{
- va_list args;
-
- if (errno == 0)
- errno = nvm_errval;
- if (!errno)
- errno = ECANCELED;
-
- fprintf(stderr, "nvmutil: ");
-
- va_start(args, msg);
- vfprintf(stderr, msg, args);
- va_end(args);
-
- fprintf(stderr, ": %s\n", strerror(errno));
-
- exit(EXIT_FAILURE);
-}
-
-void
-err(int nvm_errval, const char *msg, ...)
-{
- struct xstate *x = xstatus();
-
- 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();
-
- 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();
- 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) {
- f->gbe_fd = -1;
- close_err = 1;
- }
- f->gbe_fd = -1;
- }
-
- if (f->tmp_fd > -1) {
- if (close_on_eintr(f->tmp_fd) == -1) {
- f->tmp_fd = -1;
- close_err = 1;
- }
- f->tmp_fd = -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 ca58fb1c..00000000
--- a/util/nvmutil/lib/string.c
+++ /dev/null
@@ -1,114 +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 <string.h>
-#include <unistd.h>
-
-#include "../include/common.h"
-
-/* scmp() - strict string comparison
- *
- * strict string comparison
- * similar to strncmp, but null and
- * unterminated inputs do not produce
- * a return value; on error, errno is
- * set and -1 is returned.
- *
- * the real return value is stored in
- * the 4th argument by pointer.
- *
- * the value at rval pointer is set,
- * only upon success. callers should
- * check the return value accordingly.
- */
-
-int
-scmp(const char *a,
- const char *b,
- size_t maxlen,
- int *rval)
-{
- size_t ch;
- unsigned char ac;
- unsigned char bc;
-
- if (a == NULL ||
- b == NULL ||
- rval == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- for (ch = 0; ch < maxlen; ch++) {
-
- ac = (unsigned char)a[ch];
- bc = (unsigned char)b[ch];
-
- if (ac != bc) {
- *rval = ac - bc;
- return 0;
- }
-
- if (ac == '\0') {
- *rval = 0;
- return 0;
- }
- }
-
- /* block unterminated strings */
- errno = EFAULT;
- return -1;
-}
-
-/* slen() - strict strict length
- *
- * strict string length calculation
- * similar to strnlen, but null and
- * unterminated inputs do not produce
- * a return value; on error, errno is
- * set and -1 is returned.
- *
- * the real return value is stored in
- * the 3rd argument by pointer.
- *
- * the value at rval pointer is set,
- * only upon success. callers should
- * check the return value accordingly.
- */
-
-int
-slen(const char *s,
- size_t maxlen,
- size_t *rval)
-{
- size_t ch;
-
- if (s == NULL ||
- rval == NULL) {
-
- errno = EFAULT;
- return -1;
- }
-
- for (ch = 0;
- ch < maxlen && s[ch] != '\0';
- ch++);
-
- if (ch == maxlen) {
- /* unterminated */
- errno = EFAULT;
- return -1;
- }
-
- *rval = ch;
- return 0;
-}
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 f84dae6a..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(size_t pos16, size_t p)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- size_t 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(size_t pos16, size_t p, unsigned short val16)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- size_t 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(size_t p)
-{
- struct xstate *x = xstatus();
- struct xfile *f = &x->f;
-
- check_bin(p, "part number");
- f->part_modified[p] = 1;
-}
-
-void
-check_nvm_bound(size_t c, size_t 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",
- (size_t)c);
-}
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
deleted file mode 100644
index 266654e8..00000000
--- a/util/nvmutil/nvmutil.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* SPDX-License-Identifier: MIT
- * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
- *
- * This tool lets you modify Intel GbE NVM (Gigabit Ethernet
- * Non-Volatile Memory) images, e.g. change the MAC address.
- * These images configure your Intel Gigabit Ethernet adapter.
- */
-
-#ifdef __OpenBSD__
-/* for pledge/unveil test:
- */
-#include <sys/param.h>
-#endif
-
-#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 "include/common.h"
-
-int
-main(int argc, char *argv[])
-{
- struct xstate *x;
-
- struct commands *cmd;
- struct xfile *f;
-
- size_t c;
-
- int rval;
- char *test = NULL;
- int fd = -1;
- rval = new_tmpdir(&fd, &test);
- if (rval < 0)
- err_no_cleanup(errno, "TESTERR: ");
-
- printf("TEST: %s\n", test);
- exit(1);
-
-/* 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_no_cleanup(errno, "pledge plus unveil, main");
- if (unveil("/dev/null", "r") == -1)
- err_no_cleanup(errno, "unveil r: /dev/null");
-#elif (OpenBSD) >= 509
- if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
- err_no_cleanup(errno, "pledge, main");
-#endif
-#endif
-
-#ifndef S_ISREG
- err_no_cleanup(ECANCELED,
- "Can't determine file types (S_ISREG undefined)");
-#endif
-#if ((CHAR_BIT) != 8)
- err_no_cleanup(ECANCELED, "Unsupported char size");
-#endif
-
- x = xstart(argc, argv);
-
- if (x == NULL)
- err_no_cleanup(ECANCELED, "NULL state on init");
-
- cmd = &x->cmd[x->i];
- f = &x->f;
-
-/* https://man.openbsd.org/pledge.2
- https://man.openbsd.org/unveil.2 */
-#if defined(__OpenBSD__) && defined(OpenBSD)
-#if (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, "unveil rwc: %s", 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)");
-
-#elif (OpenBSD) >= 509
- if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
- err(errno, "pledge");
-#endif
-#endif
-
- if (cmd->run == NULL)
- err(errno, "Command not set");
-
- open_gbe_file();
-
- copy_gbe();
- read_checksums();
-
- cmd->run();
-
- for (c = 0; c < items(x->cmd); c++)
- x->cmd[c].run = cmd_helper_err;
-
- if ((cmd->flags & O_ACCMODE) == O_RDWR)
- write_to_gbe_bin();
-
- if (exit_cleanup() == -1)
- err(EIO, "%s: close", f->fname);
-
- if (f->io_err_gbe_bin)
- err(EIO, "%s: error writing final file");
-
- if (f->tname != NULL) {
- free(f->tname);
- f->tname = NULL;
- }
-
- return EXIT_SUCCESS;
-}