util/nvmutil: never do cross-filesystem moves

make a local TMPDIR instead, where gbe.bin is.
this avoids the EXDEV errno, so we don't have
to handle it, and it's just better performant
for everyone.

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-24 18:46:36 +00:00
parent 6593e76c6a
commit c1befbcd3e
5 changed files with 47 additions and 135 deletions

View File

@@ -497,11 +497,9 @@ const char *getnvmprogname(void);
/* libc hardening /* libc hardening
*/ */
int new_tmpfile_at(int dirfd, struct stat *st_dir, int new_tmpfile(int *fd, char **path, char *tmpdir);
int *fd, char **name); int new_tmpdir(int *fd, char **path, char *tmpdir);
int new_tmpfile(int *fd, char **path); int new_tmp_common(int *fd, char **path, int type, char *tmpdir);
int new_tmpdir(int *fd, char **path);
int new_tmp_common(int *fd, char **path, int type);
int mkhtemp_try_create(int dirfd, int mkhtemp_try_create(int dirfd,
struct stat *st_dir_initial, struct stat *st_dir_initial,
char *fname_copy, char *fname_copy,
@@ -519,7 +517,8 @@ int world_writeable_and_sticky(const char *s,
int same_dir(const char *a, const char *b); int same_dir(const char *a, const char *b);
int tmpdir_policy(const char *path, int tmpdir_policy(const char *path,
int *allow_noworld_unsticky); int *allow_noworld_unsticky);
char *env_tmpdir(int always_sticky, char **tmpdir); char *env_tmpdir(int always_sticky, char **tmpdir,
char *override_tmpdir);
int secure_file(int *fd, int secure_file(int *fd,
struct stat *st, struct stat *st,
struct stat *expected, struct stat *expected,

View File

@@ -458,70 +458,6 @@ gbe_mv(void)
err(errno, "TODO: cross-filesystem atomic writes currently broken"); err(errno, "TODO: cross-filesystem atomic writes currently broken");
/*
* cross-filesystem: copy into target dir tmp
*/
if (fs_dirname_basename(f->fname,
&dir, &base, 0) < 0)
goto ret_gbe_mv;
dirfd = fs_open(dir,
O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
goto ret_gbe_mv;
if (fstat(dirfd, &st_dir) < 0)
goto ret_gbe_mv;
if (new_tmpfile_at(dirfd, &st_dir,
&dest_fd, &dest_name) < 0)
goto ret_gbe_mv;
/* copy: tmp to local tmp */
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;
/* atomic replace */
if (fs_rename_at(dirfd, dest_name,
dirfd, base) == -1)
goto ret_gbe_mv;
if (fsync_dir(f->fname) < 0) {
f->io_err_gbe_bin = 1;
goto ret_gbe_mv;
}
free_if_null(&dest_name);
free_if_null(&dir);
tmp_gbe_bin_exists = 0;
ret_gbe_mv: ret_gbe_mv:
/* TODO: this whole section is bloat. /* TODO: this whole section is bloat.

View File

@@ -27,70 +27,21 @@
#include "../include/common.h" #include "../include/common.h"
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
int int
new_tmpfile(int *fd, char **path) new_tmpfile(int *fd, char **path, char *tmpdir)
{ {
return new_tmp_common(fd, path, MKHTEMP_FILE); return new_tmp_common(fd, path, MKHTEMP_FILE, tmpdir);
} }
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
int int
new_tmpdir(int *fd, char **path) new_tmpdir(int *fd, char **path, char *tmpdir)
{ {
return new_tmp_common(fd, path, MKHTEMP_DIR); return new_tmp_common(fd, path, MKHTEMP_DIR, tmpdir);
}
/* not used by mkhtemp util, but by nvmutil.
* it leaves an st variable set after return,
* so that verification can be done on a file
* being used, before writing it elsewhere
*/
int
new_tmpfile_at(int dirfd, struct stat *st_dir,
int *fd, char **name)
{
struct stat st;
char suffix[] = "tmp.XXXXXXXXXX";
size_t len;
char *buf = NULL;
char *fname;
int saved_errno = errno;
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
size_t maxlen = PATH_LEN;
#else
size_t maxlen = 4096;
#endif
if (if_err(fd == NULL || name == NULL || st_dir == NULL, EFAULT) ||
if_err(*fd >= 0, EEXIST) ||
if_err(dirfd < 0, EBADF) ||
if_err_sys(slen(suffix, maxlen, &len) < 0) ||
if_err_sys((malloc(len + 1)) == NULL))
return -1;
memcpy(buf, suffix, len + 1);
fname = buf;
*fd = mkhtemp(fd, &st,
buf, dirfd, fname,
st_dir, MKHTEMP_FILE);
if (*fd < 0)
goto err;
*name = buf;
errno = saved_errno;
return 0;
err:
free_if_null(&buf);
close_no_err(fd);
return -1;
} }
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
/* WARNING: /* WARNING:
* on error, *path (at **path) may be * on error, *path (at **path) may be
NULL, or if the error pertains to NULL, or if the error pertains to
@@ -107,7 +58,8 @@ err:
* default to /tmp or /var/tmp * default to /tmp or /var/tmp
*/ */
int int
new_tmp_common(int *fd, char **path, int type) new_tmp_common(int *fd, char **path, int type,
char *tmpdir)
{ {
#if defined(PATH_LEN) && \ #if defined(PATH_LEN) && \
(PATH_LEN) >= 256 (PATH_LEN) >= 256
@@ -118,7 +70,6 @@ new_tmp_common(int *fd, char **path, int type)
struct stat st; struct stat st;
char suffix[] = "tmp.XXXXXXXXXX"; char suffix[] = "tmp.XXXXXXXXXX";
char *tmpdir = NULL;
size_t dirlen; size_t dirlen;
size_t destlen; size_t destlen;
@@ -152,15 +103,25 @@ new_tmp_common(int *fd, char **path, int type)
* (on error, it will not be touched) * (on error, it will not be touched)
*/ */
*fd = -1; *fd = -1;
if (tmpdir == NULL) { /* no user override */
#if defined(PERMIT_NON_STICKY_ALWAYS) && \
((PERMIT_NON_STICKY_ALWAYS) > 0)
tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir, NULL);
#else
tmpdir = env_tmpdir(0, &fail_dir, NULL);
#endif
} else {
#if defined(PERMIT_NON_STICKY_ALWAYS) && \ #if defined(PERMIT_NON_STICKY_ALWAYS) && \
((PERMIT_NON_STICKY_ALWAYS) > 0) ((PERMIT_NON_STICKY_ALWAYS) > 0)
tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir); tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir,
tmpdir);
#else #else
tmpdir = env_tmpdir(0, &fail_dir); tmpdir = env_tmpdir(0, &fail_dir, tmpdir);
#endif #endif
}
if (tmpdir == NULL) if (tmpdir == NULL)
goto err; goto err;
@@ -241,7 +202,8 @@ err:
*/ */
char * char *
env_tmpdir(int bypass_all_sticky_checks, char **tmpdir) env_tmpdir(int bypass_all_sticky_checks, char **tmpdir,
char *override_tmpdir)
{ {
char *t; char *t;
int allow_noworld_unsticky; int allow_noworld_unsticky;
@@ -250,7 +212,11 @@ env_tmpdir(int bypass_all_sticky_checks, char **tmpdir)
char tmp[] = "/tmp"; char tmp[] = "/tmp";
char vartmp[] = "/var/tmp"; char vartmp[] = "/var/tmp";
t = getenv("TMPDIR"); /* tmpdir is a user override, if set */
if (override_tmpdir == NULL)
t = getenv("TMPDIR");
else
t = override_tmpdir;
if (t != NULL && *t != '\0') { if (t != NULL && *t != '\0') {

View File

@@ -26,6 +26,9 @@ struct xstate *
xstart(int argc, char *argv[]) xstart(int argc, char *argv[])
{ {
static int first_run = 1; static int first_run = 1;
static char *dir = NULL;
static char *base = NULL;
char *realdir = NULL;
static struct xstate us = { static struct xstate us = {
{ {
@@ -107,7 +110,15 @@ xstart(int argc, char *argv[])
us.f.tmp_fd = -1; us.f.tmp_fd = -1;
us.f.tname = NULL; us.f.tname = NULL;
if (new_tmpfile(&us.f.tmp_fd, &us.f.tname) < 0) if ((realdir = realpath(us.f.fname, NULL)) == NULL)
err_no_cleanup(errno, "xstart: can't get realpath of %s",
us.f.fname);
if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
err_no_cleanup(errno, "xstart: don't know CWD of %s",
us.f.fname);
if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir) < 0)
err_no_cleanup(errno, "%s", us.f.tname); err_no_cleanup(errno, "%s", us.f.tname);
if (us.f.tname == NULL) if (us.f.tname == NULL)

View File

@@ -106,7 +106,7 @@ main(int argc, char *argv[])
} }
} }
if (new_tmp_common(&fd, &s, type) < 0) if (new_tmp_common(&fd, &s, type, NULL) < 0)
err_no_cleanup(errno, "%s", s); err_no_cleanup(errno, "%s", s);
#if defined(__OpenBSD__) && defined(OpenBSD) #if defined(__OpenBSD__) && defined(OpenBSD)