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 <[email protected]>
---

 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;
-- 
2.7.4

_______________________________________________
U-Boot mailing list
[email protected]
https://lists.denx.de/listinfo/u-boot

Reply via email to