diff options
author | Alex Kiernan <alex.kiernan@gmail.com> | 2018-03-09 15:13:02 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-03-19 23:14:19 +0300 |
commit | dbc34323796bfc37116a7a28e93bcc5bea5fb136 (patch) | |
tree | 3370a29bbb59a2db130bd84ee5b3f136867bcc76 /tools/env/fw_env.c | |
parent | 899b5338fa695e4bb399b01861624f658e09174f (diff) | |
download | u-boot-dbc34323796bfc37116a7a28e93bcc5bea5fb136.tar.xz |
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/env/fw_env.c')
-rw-r--r-- | tools/env/fw_env.c | 83 |
1 files changed, 80 insertions, 3 deletions
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 600fe5dce0..77eac3d6c1 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; |