aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAlex Kiernan2018-03-09 12:13:02 +0000
committerTom Rini2018-03-19 16:14:19 -0400
commitdbc34323796bfc37116a7a28e93bcc5bea5fb136 (patch)
tree3370a29bbb59a2db130bd84ee5b3f136867bcc76 /tools
parent899b5338fa695e4bb399b01861624f658e09174f (diff)
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>
Diffstat (limited to 'tools')
-rw-r--r--tools/env/fw_env.c83
1 files changed, 80 insertions, 3 deletions
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index 600fe5dce00..77eac3d6c17 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -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;