summaryrefslogtreecommitdiff
path: root/util/libreboot-utils/lib/state.c
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-24 00:28:15 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-24 01:25:53 +0000
commitf2544d094ba88e1cfbb7993ad67444852cfd5efd (patch)
tree252172594a1284e59e88c73c22f0201508809022 /util/libreboot-utils/lib/state.c
parentafcd535816b45fd7b0e0d07c1a8580f6f462f5e4 (diff)
util/mkhtemp: new utility (hardened mktemp)
part of the same code library as nvmutil. as part of this, i renamed util/nvmutil to util/libreboot-utils/ because it is now a multi-utility codebase. this is more efficient, since i also wish to use mkhtemp (function) in nvmutil. Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/libreboot-utils/lib/state.c')
-rw-r--r--util/libreboot-utils/lib/state.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/util/libreboot-utils/lib/state.c b/util/libreboot-utils/lib/state.c
new file mode 100644
index 00000000..d06a8869
--- /dev/null
+++ b/util/libreboot-utils/lib/state.c
@@ -0,0 +1,252 @@
+/* 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;
+}
+
+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;
+}