]> git.sur5r.net Git - u-boot/blobdiff - tools/env/fw_env.c
fw_printenv: Don't bail out directly after one env read error
[u-boot] / tools / env / fw_env.c
index 9d5ccfc8545380a9a9cfefa4fe7827c41a8173c0..3a5ad026f01732442cefd05e772724ac8298ccd3 100644 (file)
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2000-2010
  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  *
  * (C) Copyright 2008
  * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #define _GNU_SOURCE
@@ -14,6 +13,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>
@@ -116,7 +116,7 @@ static struct environment environment = {
        .flag_scheme = FLAG_NONE,
 };
 
-static int HaveRedundEnv = 0;
+static int have_redund_env;
 
 static unsigned char active_flag = 1;
 /* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
@@ -737,7 +737,8 @@ int fw_env_set(int argc, char *argv[], struct env_opts *opts)
 int fw_parse_script(char *fname, struct env_opts *opts)
 {
        FILE *fp;
-       char dump[1024];        /* Maximum line length in the file */
+       char *line = NULL;
+       size_t linesize = 0;
        char *name;
        char *val;
        int lineno = 0;
@@ -763,36 +764,34 @@ int fw_parse_script(char *fname, struct env_opts *opts)
                }
        }
 
-       while (fgets(dump, sizeof(dump), fp)) {
+       while ((len = getline(&line, &linesize, fp)) != -1) {
                lineno++;
-               len = strlen(dump);
 
                /*
-                * Read a whole line from the file. If the line is too long
-                * or is not terminated, reports an error and exit.
+                * Read a whole line from the file. If the line is not
+                * terminated, reports an error and exit.
                 */
-               if (dump[len - 1] != '\n') {
+               if (line[len - 1] != '\n') {
                        fprintf(stderr,
-                               "Line %d not corrected terminated or too long\n",
+                               "Line %d not correctly terminated\n",
                                lineno);
                        ret = -1;
                        break;
                }
 
                /* Drop ending line feed / carriage return */
-               dump[--len] = '\0';
-               if (len && dump[len - 1] == '\r')
-                       dump[--len] = '\0';
+               line[--len] = '\0';
+               if (len && line[len - 1] == '\r')
+                       line[--len] = '\0';
 
                /* Skip comment or empty lines */
-               if (len == 0 || dump[0] == '#')
+               if (len == 0 || line[0] == '#')
                        continue;
 
                /*
-                * Search for variable's name,
-                * remove leading whitespaces
+                * Search for variable's name remove leading whitespaces
                 */
-               name = skip_blanks(dump);
+               name = skip_blanks(line);
                if (!name)
                        continue;
 
@@ -829,6 +828,7 @@ int fw_parse_script(char *fname, struct env_opts *opts)
                }
 
        }
+       free(line);
 
        /* Close file if not stdin */
        if (strcmp(fname, "-") != 0)
@@ -842,10 +842,10 @@ int fw_parse_script(char *fname, struct env_opts *opts)
 }
 
 /**
- * environment_end() - compute offset of first byte right after environemnt
+ * environment_end() - compute offset of first byte right after environment
  * @dev - index of enviroment buffer
  * Return:
- *  device offset of first byte right after environemnt
+ *  device offset of first byte right after environment
  */
 off_t environment_end(int dev)
 {
@@ -1225,65 +1225,149 @@ static int flash_read(int fd)
        return 0;
 }
 
-static int flash_io(int mode)
+static int flash_open_tempfile(const char **dname, const char **target_temp)
 {
-       int fd_current, fd_target, rc, dev_target;
+       char *dup_name = strdup(DEVNAME(dev_current));
+       char *temp_name = NULL;
+       int rc = -1;
 
-       /* dev_current: fd_current, erase_current */
-       fd_current = open(DEVNAME(dev_current), mode);
-       if (fd_current < 0) {
-               fprintf(stderr,
-                       "Can't open %s: %s\n",
-                       DEVNAME(dev_current), strerror(errno));
+       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;
        }
 
-       if (mode == O_RDWR) {
-               if (HaveRedundEnv) {
-                       /* switch to next partition for writing */
-                       dev_target = !dev_current;
-                       /* dev_target: fd_target, erase_target */
-                       fd_target = open(DEVNAME(dev_target), mode);
-                       if (fd_target < 0) {
-                               fprintf(stderr,
-                                       "Can't open %s: %s\n",
-                                       DEVNAME(dev_target), strerror(errno));
-                               rc = -1;
-                               goto exit;
-                       }
-               } else {
-                       dev_target = dev_current;
-                       fd_target = fd_current;
+err:
+       if (dup_name)
+               free(dup_name);
+
+       return rc;
+}
+
+static int flash_io_write(int fd_current)
+{
+       int fd_target = -1, rc, dev_target;
+       const char *dname, *target_temp = NULL;
+
+       if (have_redund_env) {
+               /* switch to next partition for writing */
+               dev_target = !dev_current;
+               /* dev_target: fd_target, erase_target */
+               fd_target = open(DEVNAME(dev_target), O_RDWR);
+               if (fd_target < 0) {
+                       fprintf(stderr,
+                               "Can't open %s: %s\n",
+                               DEVNAME(dev_target), strerror(errno));
+                       rc = -1;
+                       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;
+               if (fd_target == -1)
+                       fd_target = fd_current;
+       }
 
-               rc = flash_write(fd_current, fd_target, dev_target);
+       rc = flash_write(fd_current, fd_target, dev_target);
 
-               if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+       if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+               fprintf(stderr,
+                       "fsync failed on %s: %s\n",
+                       DEVNAME(dev_current), strerror(errno));
+       }
+
+       if (fd_current != fd_target) {
+               if (fsync(fd_target) &&
+                   !(errno == EINVAL || errno == EROFS)) {
                        fprintf(stderr,
                                "fsync failed on %s: %s\n",
                                DEVNAME(dev_current), strerror(errno));
                }
 
-               if (HaveRedundEnv) {
-                       if (fsync(fd_target) &&
-                           !(errno == EINVAL || errno == EROFS)) {
+               if (close(fd_target)) {
+                       fprintf(stderr,
+                               "I/O error on %s: %s\n",
+                               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,
-                                       "fsync failed on %s: %s\n",
-                                       DEVNAME(dev_current), strerror(errno));
-                       }
+                                       "Can't open %s: %s\n",
+                                       dname, strerror(errno));
 
-                       if (close(fd_target)) {
+                       if (rename(target_temp, DEVNAME(dev_target))) {
                                fprintf(stderr,
-                                       "I/O error on %s: %s\n",
-                                       DEVNAME(dev_target), strerror(errno));
+                                       "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;
+}
+
+static int flash_io(int mode)
+{
+       int fd_current, rc;
+
+       /* dev_current: fd_current, erase_current */
+       fd_current = open(DEVNAME(dev_current), mode);
+       if (fd_current < 0) {
+               fprintf(stderr,
+                       "Can't open %s: %s\n",
+                       DEVNAME(dev_current), strerror(errno));
+               return -1;
+       }
+
+       if (mode == O_RDWR) {
+               rc = flash_io_write(fd_current);
        } else {
                rc = flash_read(fd_current);
        }
 
- exit:
        if (close(fd_current)) {
                fprintf(stderr,
                        "I/O error on %s: %s\n",
@@ -1330,7 +1414,7 @@ int fw_env_open(struct env_opts *opts)
        /* read environment from FLASH to local buffer */
        environment.image = addr0;
 
-       if (HaveRedundEnv) {
+       if (have_redund_env) {
                redundant = addr0;
                environment.crc = &redundant->crc;
                environment.flags = &redundant->flags;
@@ -1343,15 +1427,22 @@ int fw_env_open(struct env_opts *opts)
        }
 
        dev_current = 0;
-       if (flash_io(O_RDONLY)) {
+
+       if (!flash_io(O_RDONLY)) {
+               crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
+               crc0_ok = (crc0 == *environment.crc);
+       } else if (have_redund_env) {
+               /*
+                * to give the redundant env a chance, maybe it's good:
+                * mark env crc0 invalid then test below if crc1 is ok
+                */
+               crc0_ok = 0;
+       } else {
                ret = -EIO;
                goto open_cleanup;
        }
 
-       crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
-
-       crc0_ok = (crc0 == *environment.crc);
-       if (!HaveRedundEnv) {
+       if (!have_redund_env) {
                if (!crc0_ok) {
                        fprintf(stderr,
                                "Warning: Bad CRC, using default environment\n");
@@ -1378,8 +1469,10 @@ int fw_env_open(struct env_opts *opts)
                 */
                environment.image = addr1;
                if (flash_io(O_RDONLY)) {
-                       ret = -EIO;
-                       goto open_cleanup;
+                       crc1_ok = 0;
+               } else {
+                       crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
+                       crc1_ok = (crc1 == redundant->crc);
                }
 
                /* Check flag scheme compatibility */
@@ -1405,9 +1498,6 @@ int fw_env_open(struct env_opts *opts)
                        goto open_cleanup;
                }
 
-               crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
-
-               crc1_ok = (crc1 == redundant->crc);
                flag1 = redundant->flags;
 
                if (crc0_ok && !crc1_ok) {
@@ -1644,14 +1734,14 @@ static int parse_config(struct env_opts *opts)
 #ifdef DEVICE2_ENVSECTORS
        ENVSECTORS(1) = DEVICE2_ENVSECTORS;
 #endif
-       HaveRedundEnv = 1;
+       have_redund_env = 1;
 #endif
 #endif
        rc = check_device_config(0);
        if (rc < 0)
                return rc;
 
-       if (HaveRedundEnv) {
+       if (have_redund_env) {
                rc = check_device_config(1);
                if (rc < 0)
                        return rc;
@@ -1664,7 +1754,7 @@ static int parse_config(struct env_opts *opts)
        }
 
        usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
-       if (HaveRedundEnv)
+       if (have_redund_env)
                usable_envsize -= sizeof(char);
 
        return 0;
@@ -1676,19 +1766,20 @@ static int get_config(char *fname)
        FILE *fp;
        int i = 0;
        int rc;
-       char dump[128];
+       char *line = NULL;
+       size_t linesize = 0;
        char *devname;
 
        fp = fopen(fname, "r");
        if (fp == NULL)
                return -1;
 
-       while (i < 2 && fgets(dump, sizeof(dump), fp)) {
-               /* Skip incomplete conversions and comment strings */
-               if (dump[0] == '#')
+       while (i < 2 && getline(&line, &linesize, fp) != -1) {
+               /* Skip comment strings */
+               if (line[0] == '#')
                        continue;
 
-               rc = sscanf(dump, "%ms %lli %lx %lx %lx",
+               rc = sscanf(line, "%ms %lli %lx %lx %lx",
                            &devname,
                            &DEVOFFSET(i),
                            &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
@@ -1704,9 +1795,10 @@ static int get_config(char *fname)
 
                i++;
        }
+       free(line);
        fclose(fp);
 
-       HaveRedundEnv = i - 1;
+       have_redund_env = i - 1;
        if (!i) {               /* No valid entries found */
                errno = EINVAL;
                return -1;