
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 ---
tools/env/fw_env.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-)
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 2df3504..b814c4e 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> @@ -1229,9 +1230,46 @@ 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/uboot.tmp", *dname); + if (rc == -1) + goto err; + + rc = open(temp_name, O_RDWR | O_CREAT | O_TRUNC, 0700); + if (rc == -1) { + /* fall back to in place write */ + 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 (HaveRedundEnv) { /* switch to next partition for writing */ @@ -1247,8 +1285,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); @@ -1260,7 +1307,7 @@ static int flash_io_write (int fd_current) DEVNAME (dev_current), strerror (errno)); }
- if (HaveRedundEnv) { + if (fd_current != fd_target) { if (fsync(fd_target) && !(errno == EINVAL || errno == EROFS)) { fprintf (stderr, @@ -1275,6 +1322,34 @@ static int flash_io_write (int fd_current) 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;