tools: env: Implement atomic replace for filesystem

If the U-Boot environment is stored in a regular file and redundant
operation isn't set, then write to a temporary file and perform an
atomic rename.

Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
This commit is contained in:
Alex Kiernan 2018-03-09 12:13:02 +00:00 committed by Tom Rini
parent 899b5338fa
commit dbc3432379

83
tools/env/fw_env.c vendored
View file

@ -14,6 +14,7 @@
#include <errno.h>
#include <env_flags.h>
#include <fcntl.h>
#include <libgen.h>
#include <linux/fs.h>
#include <linux/stringify.h>
#include <ctype.h>
@ -1225,9 +1226,48 @@ static int flash_read(int fd)
return 0;
}
static int flash_open_tempfile(const char **dname, const char **target_temp)
{
char *dup_name = strdup(DEVNAME(dev_current));
char *temp_name = NULL;
int rc = -1;
if (!dup_name)
return -1;
*dname = dirname(dup_name);
if (!*dname)
goto err;
rc = asprintf(&temp_name, "%s/XXXXXX", *dname);
if (rc == -1)
goto err;
rc = mkstemp(temp_name);
if (rc == -1) {
/* fall back to in place write */
fprintf(stderr,
"Can't create %s: %s\n", temp_name, strerror(errno));
free(temp_name);
} else {
*target_temp = temp_name;
/* deliberately leak dup_name as dname /might/ point into
* it and we need it for our caller
*/
dup_name = NULL;
}
err:
if (dup_name)
free(dup_name);
return rc;
}
static int flash_io_write(int fd_current)
{
int fd_target, rc, dev_target;
int fd_target = -1, rc, dev_target;
const char *dname, *target_temp = NULL;
if (have_redund_env) {
/* switch to next partition for writing */
@ -1242,8 +1282,17 @@ static int flash_io_write(int fd_current)
goto exit;
}
} else {
struct stat sb;
if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) {
/* if any part of flash_open_tempfile() fails we fall
* back to in-place writes
*/
fd_target = flash_open_tempfile(&dname, &target_temp);
}
dev_target = dev_current;
fd_target = fd_current;
if (fd_target == -1)
fd_target = fd_current;
}
rc = flash_write(fd_current, fd_target, dev_target);
@ -1254,7 +1303,7 @@ static int flash_io_write(int fd_current)
DEVNAME(dev_current), strerror(errno));
}
if (have_redund_env) {
if (fd_current != fd_target) {
if (fsync(fd_target) &&
!(errno == EINVAL || errno == EROFS)) {
fprintf(stderr,
@ -1268,6 +1317,34 @@ static int flash_io_write(int fd_current)
DEVNAME(dev_target), strerror(errno));
rc = -1;
}
if (target_temp) {
int dir_fd;
dir_fd = open(dname, O_DIRECTORY | O_RDONLY);
if (dir_fd == -1)
fprintf(stderr,
"Can't open %s: %s\n",
dname, strerror(errno));
if (rename(target_temp, DEVNAME(dev_target))) {
fprintf(stderr,
"rename failed %s => %s: %s\n",
target_temp, DEVNAME(dev_target),
strerror(errno));
rc = -1;
}
if (dir_fd != -1 && fsync(dir_fd))
fprintf(stderr,
"fsync failed on %s: %s\n",
dname, strerror(errno));
if (dir_fd != -1 && close(dir_fd))
fprintf(stderr,
"I/O error on %s: %s\n",
dname, strerror(errno));
}
}
exit:
return rc;