]> 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 be338a5ba909dcbada9bc746457e7fa3936e513b..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>
@@ -25,6 +25,7 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #ifdef MTD_OLD
 # include <stdint.h>
@@ -34,6 +35,9 @@
 # include <mtd/mtd-user.h>
 #endif
 
+#include <mtd/ubi-user.h>
+
+#include "fw_env_private.h"
 #include "fw_env.h"
 
 struct env_opts default_opts = {
@@ -57,16 +61,17 @@ struct envdev_s {
        ulong erase_size;               /* device erase size */
        ulong env_sectors;              /* number of environment sectors */
        uint8_t mtd_type;               /* type of the MTD device */
+       int is_ubi;                     /* set if we use UBI volume */
 };
 
-static struct envdev_s envdevices[2] =
-{
+static struct envdev_s envdevices[2] = {
        {
                .mtd_type = MTD_ABSENT,
        }, {
                .mtd_type = MTD_ABSENT,
        },
 };
+
 static int dev_current;
 
 #define DEVNAME(i)    envdevices[(i)].devname
@@ -75,6 +80,7 @@ static int dev_current;
 #define DEVESIZE(i)   envdevices[(i)].erase_size
 #define ENVSECTORS(i) envdevices[(i)].env_sectors
 #define DEVTYPE(i)    envdevices[(i)].mtd_type
+#define IS_UBI(i)     envdevices[(i)].is_ubi
 
 #define CUR_ENVSIZE ENVSIZE(dev_current)
 
@@ -82,14 +88,14 @@ static unsigned long usable_envsize;
 #define ENV_SIZE      usable_envsize
 
 struct env_image_single {
-       uint32_t        crc;    /* CRC32 over data bytes    */
-       char            data[];
+       uint32_t crc;           /* CRC32 over data bytes    */
+       char data[];
 };
 
 struct env_image_redundant {
-       uint32_t        crc;    /* CRC32 over data bytes    */
-       unsigned char   flags;  /* active or obsolete */
-       char            data[];
+       uint32_t crc;           /* CRC32 over data bytes    */
+       unsigned char flags;    /* active or obsolete */
+       char data[];
 };
 
 enum flag_scheme {
@@ -99,20 +105,18 @@ enum flag_scheme {
 };
 
 struct environment {
-       void                    *image;
-       uint32_t                *crc;
-       unsigned char           *flags;
-       char                    *data;
-       enum flag_scheme        flag_scheme;
+       void *image;
+       uint32_t *crc;
+       unsigned char *flags;
+       char *data;
+       enum flag_scheme flag_scheme;
 };
 
 static struct environment environment = {
        .flag_scheme = FLAG_NONE,
 };
 
-static int env_aes_cbc_crypt(char *data, const int enc, uint8_t *key);
-
-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 */
@@ -121,18 +125,239 @@ static unsigned char obsolete_flag = 0;
 #define DEFAULT_ENV_INSTANCE_STATIC
 #include <env_default.h>
 
-static int flash_io (int mode);
-static char *envmatch (char * s1, char * s2);
+#define UBI_DEV_START "/dev/ubi"
+#define UBI_SYSFS "/sys/class/ubi"
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
+
+static int is_ubi_devname(const char *devname)
+{
+       return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1);
+}
+
+static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name,
+                                      const char *volname)
+{
+       char path[256];
+       FILE *file;
+       char *name;
+       int ret;
+
+       strcpy(path, UBI_SYSFS "/");
+       strcat(path, volume_sysfs_name);
+       strcat(path, "/name");
+
+       file = fopen(path, "r");
+       if (!file)
+               return -1;
+
+       ret = fscanf(file, "%ms", &name);
+       fclose(file);
+       if (ret <= 0 || !name) {
+               fprintf(stderr,
+                       "Failed to read from file %s, ret = %d, name = %s\n",
+                       path, ret, name);
+               return -1;
+       }
+
+       if (!strcmp(name, volname)) {
+               free(name);
+               return 0;
+       }
+       free(name);
+
+       return -1;
+}
+
+static int ubi_get_volnum_by_name(int devnum, const char *volname)
+{
+       DIR *sysfs_ubi;
+       struct dirent *dirent;
+       int ret;
+       int tmp_devnum;
+       int volnum;
+
+       sysfs_ubi = opendir(UBI_SYSFS);
+       if (!sysfs_ubi)
+               return -1;
+
+#ifdef DEBUG
+       fprintf(stderr, "Looking for volume name \"%s\"\n", volname);
+#endif
+
+       while (1) {
+               dirent = readdir(sysfs_ubi);
+               if (!dirent)
+                       return -1;
+
+               ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT,
+                            &tmp_devnum, &volnum);
+               if (ret == 2 && devnum == tmp_devnum) {
+                       if (ubi_check_volume_sysfs_name(dirent->d_name,
+                                                       volname) == 0)
+                               return volnum;
+               }
+       }
+
+       return -1;
+}
+
+static int ubi_get_devnum_by_devname(const char *devname)
+{
+       int devnum;
+       int ret;
+
+       ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum);
+       if (ret != 1)
+               return -1;
+
+       return devnum;
+}
+
+static const char *ubi_get_volume_devname(const char *devname,
+                                         const char *volname)
+{
+       char *volume_devname;
+       int volnum;
+       int devnum;
+       int ret;
+
+       devnum = ubi_get_devnum_by_devname(devname);
+       if (devnum < 0)
+               return NULL;
+
+       volnum = ubi_get_volnum_by_name(devnum, volname);
+       if (volnum < 0)
+               return NULL;
+
+       ret = asprintf(&volume_devname, "%s_%d", devname, volnum);
+       if (ret < 0)
+               return NULL;
+
+#ifdef DEBUG
+       fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n",
+               devname, volname, volume_devname);
+#endif
+
+       return volume_devname;
+}
+
+static void ubi_check_dev(unsigned int dev_id)
+{
+       char *devname = (char *)DEVNAME(dev_id);
+       char *pname;
+       const char *volname = NULL;
+       const char *volume_devname;
+
+       if (!is_ubi_devname(DEVNAME(dev_id)))
+               return;
+
+       IS_UBI(dev_id) = 1;
+
+       for (pname = devname; *pname != '\0'; pname++) {
+               if (*pname == ':') {
+                       *pname = '\0';
+                       volname = pname + 1;
+                       break;
+               }
+       }
+
+       if (volname) {
+               /* Let's find real volume device name */
+               volume_devname = ubi_get_volume_devname(devname, volname);
+               if (!volume_devname) {
+                       fprintf(stderr, "Didn't found ubi volume \"%s\"\n",
+                               volname);
+                       return;
+               }
+
+               free(devname);
+               DEVNAME(dev_id) = volume_devname;
+       }
+}
+
+static int ubi_update_start(int fd, int64_t bytes)
+{
+       if (ioctl(fd, UBI_IOCVOLUP, &bytes))
+               return -1;
+       return 0;
+}
+
+static int ubi_read(int fd, void *buf, size_t count)
+{
+       ssize_t ret;
+
+       while (count > 0) {
+               ret = read(fd, buf, count);
+               if (ret > 0) {
+                       count -= ret;
+                       buf += ret;
+
+                       continue;
+               }
+
+               if (ret == 0) {
+                       /*
+                        * Happens in case of too short volume data size. If we
+                        * return error status we will fail it will be treated
+                        * as UBI device error.
+                        *
+                        * Leave catching this error to CRC check.
+                        */
+                       fprintf(stderr, "Warning: end of data on ubi volume\n");
+                       return 0;
+               } else if (errno == EBADF) {
+                       /*
+                        * Happens in case of corrupted volume. The same as
+                        * above, we cannot return error now, as we will still
+                        * be able to successfully write environment later.
+                        */
+                       fprintf(stderr, "Warning: corrupted volume?\n");
+                       return 0;
+               } else if (errno == EINTR) {
+                       continue;
+               }
+
+               fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n",
+                       (unsigned int)count, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ubi_write(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+
+       while (count > 0) {
+               ret = write(fd, buf, count);
+               if (ret <= 0) {
+                       if (ret < 0 && errno == EINTR)
+                               continue;
+
+                       fprintf(stderr, "Cannot write %u bytes to ubi volume\n",
+                               (unsigned int)count);
+                       return -1;
+               }
+
+               count -= ret;
+               buf += ret;
+       }
+
+       return 0;
+}
+
+static int flash_io(int mode);
 static int parse_config(struct env_opts *opts);
 
 #if defined(CONFIG_FILE)
-static int get_config (char *);
+static int get_config(char *);
 #endif
 
 static char *skip_chars(char *s)
 {
        for (; *s != '\0'; s++) {
-               if (isblank(*s))
+               if (isblank(*s) || *s == '=')
                        return s;
        }
        return NULL;
@@ -148,10 +373,28 @@ static char *skip_blanks(char *s)
 }
 
 /*
+ * s1 is either a simple 'name', or a 'name=value' pair.
+ * s2 is a 'name=value' pair.
+ * If the names match, return the value of s2, else NULL.
+ */
+static char *envmatch(char *s1, char *s2)
+{
+       if (s1 == NULL || s2 == NULL)
+               return NULL;
+
+       while (*s1 == *s2++)
+               if (*s1++ == '=')
+                       return s2;
+       if (*s1 == '\0' && *(s2 - 1) == '=')
+               return s2;
+       return NULL;
+}
+
+/**
  * Search the environment for a variable.
  * Return the value, if found, or NULL, if not found.
  */
-char *fw_getenv (char *name)
+char *fw_getenv(char *name)
 {
        char *env, *nxt;
 
@@ -160,12 +403,12 @@ char *fw_getenv (char *name)
 
                for (nxt = env; *nxt; ++nxt) {
                        if (nxt >= &environment.data[ENV_SIZE]) {
-                               fprintf (stderr, "## Error: "
+                               fprintf(stderr, "## Error: "
                                        "environment not terminated\n");
                                return NULL;
                        }
                }
-               val = envmatch (name, env);
+               val = envmatch(name, env);
                if (!val)
                        continue;
                return val;
@@ -199,119 +442,72 @@ char *fw_getdefenv(char *name)
        return NULL;
 }
 
-int parse_aes_key(char *key, uint8_t *bin_key)
-{
-       char tmp[5] = { '0', 'x', 0, 0, 0 };
-       unsigned long ul;
-       int i;
-
-       if (strnlen(key, 64) != 32) {
-               fprintf(stderr,
-                       "## Error: '-a' option requires 16-byte AES key\n");
-               return -1;
-       }
-
-       for (i = 0; i < 16; i++) {
-               tmp[2] = key[0];
-               tmp[3] = key[1];
-               errno = 0;
-               ul = strtoul(tmp, NULL, 16);
-               if (errno) {
-                       fprintf(stderr,
-                               "## Error: '-a' option requires valid AES key\n");
-                       return -1;
-               }
-               bin_key[i] = ul & 0xff;
-               key += 2;
-       }
-       return 0;
-}
-
 /*
  * Print the current definition of one, or more, or all
  * environment variables
  */
 int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts)
 {
-       char *env, *nxt;
        int i, rc = 0;
 
+       if (value_only && argc != 1) {
+               fprintf(stderr,
+                       "## Error: `-n'/`--noheader' option requires exactly one argument\n");
+               return -1;
+       }
+
        if (!opts)
                opts = &default_opts;
 
        if (fw_env_open(opts))
                return -1;
 
-       if (argc == 0) {                /* Print all env variables  */
+       if (argc == 0) {        /* Print all env variables  */
+               char *env, *nxt;
                for (env = environment.data; *env; env = nxt + 1) {
                        for (nxt = env; *nxt; ++nxt) {
                                if (nxt >= &environment.data[ENV_SIZE]) {
-                                       fprintf (stderr, "## Error: "
+                                       fprintf(stderr, "## Error: "
                                                "environment not terminated\n");
                                        return -1;
                                }
                        }
 
-                       printf ("%s\n", env);
+                       printf("%s\n", env);
                }
+               fw_env_close(opts);
                return 0;
        }
 
-       if (value_only && argc != 1) {
-               fprintf(stderr,
-                       "## Error: `-n' option requires exactly one argument\n");
-               return -1;
-       }
-
-       for (i = 0; i < argc; ++i) {    /* print single env variables   */
+       for (i = 0; i < argc; ++i) {    /* print a subset of env variables */
                char *name = argv[i];
                char *val = NULL;
 
-               for (env = environment.data; *env; env = nxt + 1) {
-
-                       for (nxt = env; *nxt; ++nxt) {
-                               if (nxt >= &environment.data[ENV_SIZE]) {
-                                       fprintf (stderr, "## Error: "
-                                               "environment not terminated\n");
-                                       return -1;
-                               }
-                       }
-                       val = envmatch (name, env);
-                       if (val) {
-                               if (!value_only) {
-                                       fputs (name, stdout);
-                                       putc ('=', stdout);
-                               }
-                               puts (val);
-                               break;
-                       }
-               }
+               val = fw_getenv(name);
                if (!val) {
-                       fprintf (stderr, "## Error: \"%s\" not defined\n", name);
+                       fprintf(stderr, "## Error: \"%s\" not defined\n", name);
                        rc = -1;
+                       continue;
                }
+
+               if (value_only) {
+                       puts(val);
+                       break;
+               }
+
+               printf("%s=%s\n", name, val);
        }
 
+       fw_env_close(opts);
+
        return rc;
 }
 
-int fw_env_close(struct env_opts *opts)
+int fw_env_flush(struct env_opts *opts)
 {
-       int ret;
-
        if (!opts)
                opts = &default_opts;
 
-       if (opts->aes_flag) {
-               ret = env_aes_cbc_crypt(environment.data, 1,
-                                       opts->aes_key);
-               if (ret) {
-                       fprintf(stderr,
-                               "Error: can't encrypt env for flash\n");
-                       return ret;
-               }
-       }
-
        /*
         * Update CRC
         */
@@ -319,15 +515,13 @@ int fw_env_close(struct env_opts *opts)
 
        /* write environment back to flash */
        if (flash_io(O_RDWR)) {
-               fprintf(stderr,
-                       "Error: can't write fw_env to flash\n");
-                       return -1;
+               fprintf(stderr, "Error: can't write fw_env to flash\n");
+               return -1;
        }
 
        return 0;
 }
 
-
 /*
  * Set/Clear a single variable in the environment.
  * This is called in sequence to update the environment
@@ -352,7 +546,8 @@ int fw_env_write(char *name, char *value)
                                return -1;
                        }
                }
-               if ((oldval = envmatch (name, env)) != NULL)
+               oldval = envmatch(name, env);
+               if (oldval)
                        break;
        }
 
@@ -375,7 +570,7 @@ int fw_env_write(char *name, char *value)
                        errno = EROFS;
                        return -1;
                } else if (env_flags_validate_varaccess(name,
-                   ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) {
+                          ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) {
                        const char *defval = fw_getdefenv(name);
 
                        if (defval == NULL)
@@ -419,21 +614,21 @@ int fw_env_write(char *name, char *value)
        /*
         * Append new definition at the end
         */
-       for (env = environment.data; *env || *(env + 1); ++env);
+       for (env = environment.data; *env || *(env + 1); ++env)
+               ;
        if (env > environment.data)
                ++env;
        /*
         * Overflow when:
         * "name" + "=" + "val" +"\0\0"  > CUR_ENVSIZE - (env-environment)
         */
-       len = strlen (name) + 2;
+       len = strlen(name) + 2;
        /* add '=' for first arg, ' ' for all others */
        len += strlen(value) + 1;
 
        if (len > (&environment.data[ENV_SIZE] - env)) {
-               fprintf (stderr,
-                       "Error: environment overflow, \"%s\" deleted\n",
-                       name);
+               fprintf(stderr,
+                       "Error: environment overflow, \"%s\" deleted\n", name);
                return -1;
        }
 
@@ -457,13 +652,15 @@ int fw_env_write(char *name, char *value)
  *         modified or deleted
  *
  */
-int fw_setenv(int argc, char *argv[], struct env_opts *opts)
+int fw_env_set(int argc, char *argv[], struct env_opts *opts)
 {
        int i;
        size_t len;
        char *name, **valv;
+       char *oldval;
        char *value = NULL;
        int valc;
+       int ret;
 
        if (!opts)
                opts = &default_opts;
@@ -483,8 +680,10 @@ int fw_setenv(int argc, char *argv[], struct env_opts *opts)
        valv = argv + 1;
        valc = argc - 1;
 
-       if (env_flags_validate_env_set_params(name, valv, valc) < 0)
-               return 1;
+       if (env_flags_validate_env_set_params(name, valv, valc) < 0) {
+               fw_env_close(opts);
+               return -1;
+       }
 
        len = 0;
        for (i = 0; i < valc; ++i) {
@@ -493,11 +692,13 @@ int fw_setenv(int argc, char *argv[], struct env_opts *opts)
 
                if (value)
                        value[len - 1] = ' ';
+               oldval = value;
                value = realloc(value, len + val_len + 1);
                if (!value) {
                        fprintf(stderr,
                                "Cannot malloc %zu bytes: %s\n",
                                len, strerror(errno));
+                       free(oldval);
                        return -1;
                }
 
@@ -510,7 +711,10 @@ int fw_setenv(int argc, char *argv[], struct env_opts *opts)
 
        free(value);
 
-       return fw_env_close(opts);
+       ret = fw_env_flush(opts);
+       fw_env_close(opts);
+
+       return ret;
 }
 
 /*
@@ -533,7 +737,8 @@ int fw_setenv(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;
@@ -554,41 +759,39 @@ int fw_parse_script(char *fname, struct env_opts *opts)
                fp = fopen(fname, "r");
                if (fp == NULL) {
                        fprintf(stderr, "I cannot open %s for reading\n",
-                                fname);
+                               fname);
                        return -1;
                }
        }
 
-       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;
 
@@ -602,7 +805,6 @@ int fw_parse_script(char *fname, struct env_opts *opts)
                        else
                                val = NULL;
                }
-
 #ifdef DEBUG
                fprintf(stderr, "Setting %s : %s\n",
                        name, val ? val : " removed");
@@ -619,43 +821,58 @@ int fw_parse_script(char *fname, struct env_opts *opts)
                 */
                if (fw_env_write(name, val)) {
                        fprintf(stderr,
-                       "fw_env_write returns with error : %s\n",
+                               "fw_env_write returns with error : %s\n",
                                strerror(errno));
                        ret = -1;
                        break;
                }
 
        }
+       free(line);
 
        /* Close file if not stdin */
        if (strcmp(fname, "-") != 0)
                fclose(fp);
 
-       ret |= fw_env_close(opts);
+       ret |= fw_env_flush(opts);
+
+       fw_env_close(opts);
 
        return ret;
 }
 
+/**
+ * environment_end() - compute offset of first byte right after environment
+ * @dev - index of enviroment buffer
+ * Return:
+ *  device offset of first byte right after environment
+ */
+off_t environment_end(int dev)
+{
+       /* environment is block aligned */
+       return DEVOFFSET(dev) + ENVSECTORS(dev) * DEVESIZE(dev);
+}
+
 /*
  * Test for bad block on NAND, just returns 0 on NOR, on NAND:
  * 0   - block is good
  * > 0 - block is bad
  * < 0 - failed to test
  */
-static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart)
+static int flash_bad_block(int fd, uint8_t mtd_type, loff_t blockstart)
 {
        if (mtd_type == MTD_NANDFLASH) {
-               int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart);
+               int badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart);
 
                if (badblock < 0) {
-                       perror ("Cannot read bad block mark");
+                       perror("Cannot read bad block mark");
                        return badblock;
                }
 
                if (badblock) {
 #ifdef DEBUG
-                       fprintf (stderr, "Bad block at 0x%llx, "
-                                "skipping\n", *blockstart);
+                       fprintf(stderr, "Bad block at 0x%llx, skipping\n",
+                               (unsigned long long)blockstart);
 #endif
                        return badblock;
                }
@@ -669,83 +886,74 @@ static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart)
  * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from
  * the DEVOFFSET (dev) block. On NOR the loop is only run once.
  */
-static int flash_read_buf (int dev, int fd, void *buf, size_t count,
-                          off_t offset, uint8_t mtd_type)
+static int flash_read_buf(int dev, int fd, void *buf, size_t count,
+                         off_t offset)
 {
        size_t blocklen;        /* erase / write length - one block on NAND,
                                   0 on NOR */
        size_t processed = 0;   /* progress counter */
        size_t readlen = count; /* current read length */
-       off_t top_of_range;     /* end of the last block we may use */
        off_t block_seek;       /* offset inside the current block to the start
                                   of the data */
        loff_t blockstart;      /* running start of the current block -
                                   MEMGETBADBLOCK needs 64 bits */
        int rc;
 
-       blockstart = (offset / DEVESIZE (dev)) * DEVESIZE (dev);
+       blockstart = (offset / DEVESIZE(dev)) * DEVESIZE(dev);
 
        /* Offset inside a block */
        block_seek = offset - blockstart;
 
-       if (mtd_type == MTD_NANDFLASH) {
+       if (DEVTYPE(dev) == MTD_NANDFLASH) {
                /*
                 * NAND: calculate which blocks we are reading. We have
                 * to read one block at a time to skip bad blocks.
                 */
-               blocklen = DEVESIZE (dev);
-
-               /*
-                * To calculate the top of the range, we have to use the
-                * global DEVOFFSET (dev), which can be different from offset
-                */
-               top_of_range = ((DEVOFFSET(dev) / blocklen) +
-                               ENVSECTORS (dev)) * blocklen;
+               blocklen = DEVESIZE(dev);
 
                /* Limit to one block for the first read */
                if (readlen > blocklen - block_seek)
                        readlen = blocklen - block_seek;
        } else {
                blocklen = 0;
-               top_of_range = offset + count;
        }
 
        /* This only runs once on NOR flash */
        while (processed < count) {
-               rc = flash_bad_block (fd, mtd_type, &blockstart);
-               if (rc < 0)             /* block test failed */
+               rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
+               if (rc < 0)     /* block test failed */
                        return -1;
 
-               if (blockstart + block_seek + readlen > top_of_range) {
+               if (blockstart + block_seek + readlen > environment_end(dev)) {
                        /* End of range is reached */
-                       fprintf (stderr,
-                                "Too few good blocks within range\n");
+                       fprintf(stderr, "Too few good blocks within range\n");
                        return -1;
                }
 
-               if (rc) {               /* block is bad */
+               if (rc) {       /* block is bad */
                        blockstart += blocklen;
                        continue;
                }
 
                /*
                 * If a block is bad, we retry in the next block at the same
-                * offset - see common/env_nand.c::writeenv()
+                * offset - see env/nand.c::writeenv()
                 */
-               lseek (fd, blockstart + block_seek, SEEK_SET);
+               lseek(fd, blockstart + block_seek, SEEK_SET);
 
-               rc = read (fd, buf + processed, readlen);
+               rc = read(fd, buf + processed, readlen);
                if (rc != readlen) {
-                       fprintf (stderr, "Read error on %s: %s\n",
-                                DEVNAME (dev), strerror (errno));
+                       fprintf(stderr, "Read error on %s: %s\n",
+                               DEVNAME(dev), strerror(errno));
                        return -1;
                }
 #ifdef DEBUG
                fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n",
-                        rc, blockstart + block_seek, DEVNAME(dev));
+                       rc, (unsigned long long)blockstart + block_seek,
+                       DEVNAME(dev));
 #endif
                processed += readlen;
-               readlen = min (blocklen, count - processed);
+               readlen = min(blocklen, count - processed);
                block_seek = 0;
                blockstart += blocklen;
        }
@@ -754,12 +962,12 @@ static int flash_read_buf (int dev, int fd, void *buf, size_t count,
 }
 
 /*
- * Write count bytes at offset, but stay within ENVSECTORS (dev) sectors of
+ * Write count bytes from begin of environment, but stay within
+ * ENVSECTORS(dev) sectors of
  * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we
  * erase and write the whole data at once.
  */
-static int flash_write_buf (int dev, int fd, void *buf, size_t count,
-                           off_t offset, uint8_t mtd_type)
+static int flash_write_buf(int dev, int fd, void *buf, size_t count)
 {
        void *data;
        struct erase_info_user erase;
@@ -775,7 +983,6 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
                                   below offset */
        off_t block_seek;       /* offset inside the erase block to the start
                                   of the data */
-       off_t top_of_range;     /* end of the last block we may use */
        loff_t blockstart;      /* running start of the current block -
                                   MEMGETBADBLOCK needs 64 bits */
        int rc;
@@ -783,27 +990,24 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
        /*
         * For mtd devices only offset and size of the environment do matter
         */
-       if (mtd_type == MTD_ABSENT) {
+       if (DEVTYPE(dev) == MTD_ABSENT) {
                blocklen = count;
-               top_of_range = offset + count;
                erase_len = blocklen;
-               blockstart = offset;
+               blockstart = DEVOFFSET(dev);
                block_seek = 0;
                write_total = blocklen;
        } else {
                blocklen = DEVESIZE(dev);
 
-               top_of_range = ((DEVOFFSET(dev) / blocklen) +
-                                       ENVSECTORS(dev)) * blocklen;
-
-               erase_offset = (offset / blocklen) * blocklen;
+               erase_offset = DEVOFFSET(dev);
 
                /* Maximum area we may use */
-               erase_len = top_of_range - erase_offset;
+               erase_len = environment_end(dev) - erase_offset;
 
                blockstart = erase_offset;
+
                /* Offset inside a block */
-               block_seek = offset - erase_offset;
+               block_seek = DEVOFFSET(dev) - erase_offset;
 
                /*
                 * Data size we actually write: from the start of the block
@@ -811,7 +1015,7 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
                 * to the end of the block
                 */
                write_total = ((block_seek + count + blocklen - 1) /
-                                                       blocklen) * blocklen;
+                              blocklen) * blocklen;
        }
 
        /*
@@ -820,16 +1024,15 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
         * block back again.
         */
        if (write_total > count) {
-               data = malloc (erase_len);
+               data = malloc(erase_len);
                if (!data) {
-                       fprintf (stderr,
-                                "Cannot malloc %zu bytes: %s\n",
-                                erase_len, strerror (errno));
+                       fprintf(stderr,
+                               "Cannot malloc %zu bytes: %s\n",
+                               erase_len, strerror(errno));
                        return -1;
                }
 
-               rc = flash_read_buf (dev, fd, data, write_total, erase_offset,
-                                    mtd_type);
+               rc = flash_read_buf(dev, fd, data, write_total, erase_offset);
                if (write_total != rc)
                        return -1;
 
@@ -840,13 +1043,14 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
                if (block_seek + count != write_total) {
                        if (block_seek != 0)
                                fprintf(stderr, " and ");
-                       fprintf(stderr, "0x%lx - 0x%x",
-                               block_seek + count, write_total - 1);
+                       fprintf(stderr, "0x%lx - 0x%lx",
+                               (unsigned long)block_seek + count,
+                               (unsigned long)write_total - 1);
                }
                fprintf(stderr, "\n");
 #endif
                /* Overwrite the old environment */
-               memcpy (data + block_seek, buf, count);
+               memcpy(data + block_seek, buf, count);
        } else {
                /*
                 * We get here, iff offset is block-aligned and count is a
@@ -855,7 +1059,7 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
                data = buf;
        }
 
-       if (mtd_type == MTD_NANDFLASH) {
+       if (DEVTYPE(dev) == MTD_NANDFLASH) {
                /*
                 * NAND: calculate which blocks we are writing. We have
                 * to write one block at a time to skip bad blocks.
@@ -869,25 +1073,25 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
 
        /* This only runs once on NOR flash and SPI-dataflash */
        while (processed < write_total) {
-               rc = flash_bad_block (fd, mtd_type, &blockstart);
-               if (rc < 0)             /* block test failed */
+               rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
+               if (rc < 0)     /* block test failed */
                        return rc;
 
-               if (blockstart + erasesize > top_of_range) {
-                       fprintf (stderr, "End of range reached, aborting\n");
+               if (blockstart + erasesize > environment_end(dev)) {
+                       fprintf(stderr, "End of range reached, aborting\n");
                        return -1;
                }
 
-               if (rc) {               /* block is bad */
+               if (rc) {       /* block is bad */
                        blockstart += blocklen;
                        continue;
                }
 
-               if (mtd_type != MTD_ABSENT) {
+               if (DEVTYPE(dev) != MTD_ABSENT) {
                        erase.start = blockstart;
                        ioctl(fd, MEMUNLOCK, &erase);
                        /* These do not need an explicit erase cycle */
-                       if (mtd_type != MTD_DATAFLASH)
+                       if (DEVTYPE(dev) != MTD_DATAFLASH)
                                if (ioctl(fd, MEMERASE, &erase) != 0) {
                                        fprintf(stderr,
                                                "MTD erase error on %s: %s\n",
@@ -896,33 +1100,33 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
                                }
                }
 
-               if (lseek (fd, blockstart, SEEK_SET) == -1) {
-                       fprintf (stderr,
-                                "Seek error on %s: %s\n",
-                                DEVNAME (dev), strerror (errno));
+               if (lseek(fd, blockstart, SEEK_SET) == -1) {
+                       fprintf(stderr,
+                               "Seek error on %s: %s\n",
+                               DEVNAME(dev), strerror(errno));
                        return -1;
                }
-
 #ifdef DEBUG
-               fprintf(stderr, "Write 0x%x bytes at 0x%llx\n", erasesize,
-                       blockstart);
+               fprintf(stderr, "Write 0x%llx bytes at 0x%llx\n",
+                       (unsigned long long)erasesize,
+                       (unsigned long long)blockstart);
 #endif
-               if (write (fd, data + processed, erasesize) != erasesize) {
-                       fprintf (stderr, "Write error on %s: %s\n",
-                                DEVNAME (dev), strerror (errno));
+               if (write(fd, data + processed, erasesize) != erasesize) {
+                       fprintf(stderr, "Write error on %s: %s\n",
+                               DEVNAME(dev), strerror(errno));
                        return -1;
                }
 
-               if (mtd_type != MTD_ABSENT)
+               if (DEVTYPE(dev) != MTD_ABSENT)
                        ioctl(fd, MEMLOCK, &erase);
 
-               processed  += erasesize;
+               processed += erasesize;
                block_seek = 0;
                blockstart += erasesize;
        }
 
        if (write_total > count)
-               free (data);
+               free(data);
 
        return processed;
 }
@@ -930,52 +1134,30 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
 /*
  * Set obsolete flag at offset - NOR flash only
  */
-static int flash_flag_obsolete (int dev, int fd, off_t offset)
+static int flash_flag_obsolete(int dev, int fd, off_t offset)
 {
        int rc;
        struct erase_info_user erase;
 
-       erase.start  = DEVOFFSET (dev);
-       erase.length = DEVESIZE (dev);
+       erase.start = DEVOFFSET(dev);
+       erase.length = DEVESIZE(dev);
        /* This relies on the fact, that obsolete_flag == 0 */
-       rc = lseek (fd, offset, SEEK_SET);
+       rc = lseek(fd, offset, SEEK_SET);
        if (rc < 0) {
-               fprintf (stderr, "Cannot seek to set the flag on %s \n",
-                        DEVNAME (dev));
+               fprintf(stderr, "Cannot seek to set the flag on %s\n",
+                       DEVNAME(dev));
                return rc;
        }
-       ioctl (fd, MEMUNLOCK, &erase);
-       rc = write (fd, &obsolete_flag, sizeof (obsolete_flag));
-       ioctl (fd, MEMLOCK, &erase);
+       ioctl(fd, MEMUNLOCK, &erase);
+       rc = write(fd, &obsolete_flag, sizeof(obsolete_flag));
+       ioctl(fd, MEMLOCK, &erase);
        if (rc < 0)
-               perror ("Could not set obsolete flag");
+               perror("Could not set obsolete flag");
 
        return rc;
 }
 
-/* Encrypt or decrypt the environment before writing or reading it. */
-static int env_aes_cbc_crypt(char *payload, const int enc, uint8_t *key)
-{
-       uint8_t *data = (uint8_t *)payload;
-       const int len = usable_envsize;
-       uint8_t key_exp[AES_EXPAND_KEY_LENGTH];
-       uint32_t aes_blocks;
-
-       /* First we expand the key. */
-       aes_expand_key(key, key_exp);
-
-       /* Calculate the number of AES blocks to encrypt. */
-       aes_blocks = DIV_ROUND_UP(len, AES_KEY_LENGTH);
-
-       if (enc)
-               aes_cbc_encrypt_blocks(key_exp, data, data, aes_blocks);
-       else
-               aes_cbc_decrypt_blocks(key_exp, data, data, aes_blocks);
-
-       return 0;
-}
-
-static int flash_write (int fd_current, int fd_target, int dev_target)
+static int flash_write(int fd_current, int fd_target, int dev_target)
 {
        int rc;
 
@@ -989,124 +1171,211 @@ static int flash_write (int fd_current, int fd_target, int dev_target)
                *environment.flags = active_flag;
                break;
        default:
-               fprintf (stderr, "Unimplemented flash scheme %u \n",
-                        environment.flag_scheme);
+               fprintf(stderr, "Unimplemented flash scheme %u\n",
+                       environment.flag_scheme);
                return -1;
        }
 
 #ifdef DEBUG
        fprintf(stderr, "Writing new environment at 0x%llx on %s\n",
-               DEVOFFSET (dev_target), DEVNAME (dev_target));
+               DEVOFFSET(dev_target), DEVNAME(dev_target));
 #endif
 
+       if (IS_UBI(dev_target)) {
+               if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0)
+                       return 0;
+               return ubi_write(fd_target, environment.image, CUR_ENVSIZE);
+       }
+
        rc = flash_write_buf(dev_target, fd_target, environment.image,
-                             CUR_ENVSIZE, DEVOFFSET(dev_target),
-                             DEVTYPE(dev_target));
+                            CUR_ENVSIZE);
        if (rc < 0)
                return rc;
 
        if (environment.flag_scheme == FLAG_BOOLEAN) {
                /* Have to set obsolete flag */
-               off_t offset = DEVOFFSET (dev_current) +
-                       offsetof (struct env_image_redundant, flags);
+               off_t offset = DEVOFFSET(dev_current) +
+                   offsetof(struct env_image_redundant, flags);
 #ifdef DEBUG
                fprintf(stderr,
                        "Setting obsolete flag in environment at 0x%llx on %s\n",
-                       DEVOFFSET (dev_current), DEVNAME (dev_current));
+                       DEVOFFSET(dev_current), DEVNAME(dev_current));
 #endif
-               flash_flag_obsolete (dev_current, fd_current, offset);
+               flash_flag_obsolete(dev_current, fd_current, offset);
        }
 
        return 0;
 }
 
-static int flash_read (int fd)
+static int flash_read(int fd)
 {
        int rc;
 
+       if (IS_UBI(dev_current)) {
+               DEVTYPE(dev_current) = MTD_ABSENT;
+
+               return ubi_read(fd, environment.image, CUR_ENVSIZE);
+       }
+
        rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE,
-                           DEVOFFSET(dev_current), DEVTYPE(dev_current));
+                           DEVOFFSET(dev_current));
        if (rc != CUR_ENVSIZE)
                return -1;
 
        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;
+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);
+
+       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 (close(fd_target)) {
+                       fprintf(stderr,
+                               "I/O error on %s: %s\n",
+                               DEVNAME(dev_target), strerror(errno));
+                       rc = -1;
                }
 
-               rc = flash_write (fd_current, fd_target, dev_target);
+               if (target_temp) {
+                       int dir_fd;
 
-               if (HaveRedundEnv) {
-                       if (close (fd_target)) {
-                               fprintf (stderr,
-                                       "I/O error on %s: %s\n",
-                                       DEVNAME (dev_target),
-                                       strerror (errno));
+                       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));
                }
-       } else {
-               rc = flash_read (fd_current);
        }
+ exit:
+       return rc;
+}
 
-exit:
-       if (close (fd_current)) {
-               fprintf (stderr,
-                        "I/O error on %s: %s\n",
-                        DEVNAME (dev_current), strerror (errno));
+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;
        }
 
-       return rc;
-}
-
-/*
- * s1 is either a simple 'name', or a 'name=value' pair.
- * s2 is a 'name=value' pair.
- * If the names match, return the value of s2, else NULL.
- */
+       if (mode == O_RDWR) {
+               rc = flash_io_write(fd_current);
+       } else {
+               rc = flash_read(fd_current);
+       }
 
-static char *envmatch (char * s1, char * s2)
-{
-       if (s1 == NULL || s2 == NULL)
-               return NULL;
+       if (close(fd_current)) {
+               fprintf(stderr,
+                       "I/O error on %s: %s\n",
+                       DEVNAME(dev_current), strerror(errno));
+               return -1;
+       }
 
-       while (*s1 == *s2++)
-               if (*s1++ == '=')
-                       return s2;
-       if (*s1 == '\0' && *(s2 - 1) == '=')
-               return s2;
-       return NULL;
+       return rc;
 }
 
 /*
@@ -1116,11 +1385,11 @@ int fw_env_open(struct env_opts *opts)
 {
        int crc0, crc0_ok;
        unsigned char flag0;
-       void *addr0;
+       void *addr0 = NULL;
 
        int crc1, crc1_ok;
        unsigned char flag1;
-       void *addr1;
+       void *addr1 = NULL;
 
        int ret;
 
@@ -1130,51 +1399,55 @@ int fw_env_open(struct env_opts *opts)
        if (!opts)
                opts = &default_opts;
 
-       if (parse_config(opts))         /* should fill envdevices */
-               return -1;
+       if (parse_config(opts)) /* should fill envdevices */
+               return -EINVAL;
 
        addr0 = calloc(1, CUR_ENVSIZE);
        if (addr0 == NULL) {
                fprintf(stderr,
                        "Not enough memory for environment (%ld bytes)\n",
                        CUR_ENVSIZE);
-               return -1;
+               ret = -ENOMEM;
+               goto open_cleanup;
        }
 
        /* 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;
-               environment.data        = redundant->data;
+               environment.crc = &redundant->crc;
+               environment.flags = &redundant->flags;
+               environment.data = redundant->data;
        } else {
                single = addr0;
-               environment.crc         = &single->crc;
-               environment.flags       = NULL;
-               environment.data        = single->data;
+               environment.crc = &single->crc;
+               environment.flags = NULL;
+               environment.data = single->data;
        }
 
        dev_current = 0;
-       if (flash_io (O_RDONLY))
-               return -1;
 
-       crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
-
-       if (opts->aes_flag) {
-               ret = env_aes_cbc_crypt(environment.data, 0,
-                                       opts->aes_key);
-               if (ret)
-                       return ret;
+       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_ok = (crc0 == *environment.crc);
-       if (!HaveRedundEnv) {
+       if (!have_redund_env) {
                if (!crc0_ok) {
-                       fprintf (stderr,
+                       fprintf(stderr,
                                "Warning: Bad CRC, using default environment\n");
-                       memcpy(environment.data, default_environment, sizeof default_environment);
+                       memcpy(environment.data, default_environment,
+                              sizeof(default_environment));
                }
        } else {
                flag0 = *environment.flags;
@@ -1185,7 +1458,8 @@ int fw_env_open(struct env_opts *opts)
                        fprintf(stderr,
                                "Not enough memory for environment (%ld bytes)\n",
                                CUR_ENVSIZE);
-                       return -1;
+                       ret = -ENOMEM;
+                       goto open_cleanup;
                }
                redundant = addr1;
 
@@ -1194,8 +1468,12 @@ int fw_env_open(struct env_opts *opts)
                 * other pointers in environment still point inside addr0
                 */
                environment.image = addr1;
-               if (flash_io (O_RDONLY))
-                       return -1;
+               if (flash_io(O_RDONLY)) {
+                       crc1_ok = 0;
+               } else {
+                       crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
+                       crc1_ok = (crc1 == redundant->crc);
+               }
 
                /* Check flag scheme compatibility */
                if (DEVTYPE(dev_current) == MTD_NORFLASH &&
@@ -1211,23 +1489,15 @@ int fw_env_open(struct env_opts *opts)
                           DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
                        environment.flag_scheme = FLAG_INCREMENTAL;
                } else if (DEVTYPE(dev_current) == MTD_ABSENT &&
-                          DEVTYPE(!dev_current) == MTD_ABSENT) {
+                          DEVTYPE(!dev_current) == MTD_ABSENT &&
+                          IS_UBI(dev_current) == IS_UBI(!dev_current)) {
                        environment.flag_scheme = FLAG_INCREMENTAL;
                } else {
-                       fprintf (stderr, "Incompatible flash types!\n");
-                       return -1;
-               }
-
-               crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE);
-
-               if (opts->aes_flag) {
-                       ret = env_aes_cbc_crypt(redundant->data, 0,
-                                               opts->aes_key);
-                       if (ret)
-                               return ret;
+                       fprintf(stderr, "Incompatible flash types!\n");
+                       ret = -EINVAL;
+                       goto open_cleanup;
                }
 
-               crc1_ok = (crc1 == redundant->crc);
                flag1 = redundant->flags;
 
                if (crc0_ok && !crc1_ok) {
@@ -1235,10 +1505,10 @@ int fw_env_open(struct env_opts *opts)
                } else if (!crc0_ok && crc1_ok) {
                        dev_current = 1;
                } else if (!crc0_ok && !crc1_ok) {
-                       fprintf (stderr,
+                       fprintf(stderr,
                                "Warning: Bad CRC, using default environment\n");
-                       memcpy (environment.data, default_environment,
-                               sizeof default_environment);
+                       memcpy(environment.data, default_environment,
+                              sizeof(default_environment));
                        dev_current = 0;
                } else {
                        switch (environment.flag_scheme) {
@@ -1265,12 +1535,12 @@ int fw_env_open(struct env_opts *opts)
                                else if ((flag1 == 255 && flag0 == 0) ||
                                         flag0 >= flag1)
                                        dev_current = 0;
-                               else /* flag1 > flag0 */
+                               else    /* flag1 > flag0 */
                                        dev_current = 1;
                                break;
                        default:
-                               fprintf (stderr, "Unknown flag scheme %u \n",
-                                        environment.flag_scheme);
+                               fprintf(stderr, "Unknown flag scheme %u\n",
+                                       environment.flag_scheme);
                                return -1;
                        }
                }
@@ -1281,44 +1551,75 @@ int fw_env_open(struct env_opts *opts)
                 * flags before writing out
                 */
                if (dev_current) {
-                       environment.image       = addr1;
-                       environment.crc         = &redundant->crc;
-                       environment.flags       = &redundant->flags;
-                       environment.data        = redundant->data;
-                       free (addr0);
+                       environment.image = addr1;
+                       environment.crc = &redundant->crc;
+                       environment.flags = &redundant->flags;
+                       environment.data = redundant->data;
+                       free(addr0);
                } else {
-                       environment.image       = addr0;
+                       environment.image = addr0;
                        /* Other pointers are already set */
-                       free (addr1);
+                       free(addr1);
                }
 #ifdef DEBUG
                fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current));
 #endif
        }
        return 0;
+
+ open_cleanup:
+       if (addr0)
+               free(addr0);
+
+       if (addr1)
+               free(addr0);
+
+       return ret;
+}
+
+/*
+ * Simply free allocated buffer with environment
+ */
+int fw_env_close(struct env_opts *opts)
+{
+       if (environment.image)
+               free(environment.image);
+
+       environment.image = NULL;
+
+       return 0;
 }
 
 static int check_device_config(int dev)
 {
        struct stat st;
+       int32_t lnum = 0;
        int fd, rc = 0;
 
+       /* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
+       ubi_check_dev(dev);
+
        fd = open(DEVNAME(dev), O_RDONLY);
        if (fd < 0) {
                fprintf(stderr,
-                       "Cannot open %s: %s\n",
-                       DEVNAME(dev), strerror(errno));
+                       "Cannot open %s: %s\n", DEVNAME(dev), strerror(errno));
                return -1;
        }
 
        rc = fstat(fd, &st);
        if (rc < 0) {
-               fprintf(stderr, "Cannot stat the file %s\n",
-                       DEVNAME(dev));
+               fprintf(stderr, "Cannot stat the file %s\n", DEVNAME(dev));
                goto err;
        }
 
-       if (S_ISCHR(st.st_mode)) {
+       if (IS_UBI(dev)) {
+               rc = ioctl(fd, UBI_IOCEBISMAP, &lnum);
+               if (rc < 0) {
+                       fprintf(stderr, "Cannot get UBI information for %s\n",
+                               DEVNAME(dev));
+                       goto err;
+               }
+       } else if (S_ISCHR(st.st_mode)) {
                struct mtd_info_user mtdinfo;
                rc = ioctl(fd, MEMGETINFO, &mtdinfo);
                if (rc < 0) {
@@ -1335,9 +1636,15 @@ static int check_device_config(int dev)
                        goto err;
                }
                DEVTYPE(dev) = mtdinfo.type;
+               if (DEVESIZE(dev) == 0)
+                       /* Assume the erase size is the same as the env-size */
+                       DEVESIZE(dev) = ENVSIZE(dev);
        } else {
                uint64_t size;
                DEVTYPE(dev) = MTD_ABSENT;
+               if (DEVESIZE(dev) == 0)
+                       /* Assume the erase size to be 512 bytes */
+                       DEVESIZE(dev) = 0x200;
 
                /*
                 * Check for negative offsets, treat it as backwards offset
@@ -1346,20 +1653,40 @@ static int check_device_config(int dev)
                if (DEVOFFSET(dev) < 0) {
                        rc = ioctl(fd, BLKGETSIZE64, &size);
                        if (rc < 0) {
-                               fprintf(stderr, "Could not get block device size on %s\n",
+                               fprintf(stderr,
+                                       "Could not get block device size on %s\n",
                                        DEVNAME(dev));
                                goto err;
                        }
 
                        DEVOFFSET(dev) = DEVOFFSET(dev) + size;
 #ifdef DEBUG
-                       fprintf(stderr, "Calculated device offset 0x%llx on %s\n",
+                       fprintf(stderr,
+                               "Calculated device offset 0x%llx on %s\n",
                                DEVOFFSET(dev), DEVNAME(dev));
 #endif
                }
        }
 
-err:
+       if (ENVSECTORS(dev) == 0)
+               /* Assume enough sectors to cover the environment */
+               ENVSECTORS(dev) = DIV_ROUND_UP(ENVSIZE(dev), DEVESIZE(dev));
+
+       if (DEVOFFSET(dev) % DEVESIZE(dev) != 0) {
+               fprintf(stderr,
+                       "Environment does not start on (erase) block boundary\n");
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (ENVSIZE(dev) > ENVSECTORS(dev) * DEVESIZE(dev)) {
+               fprintf(stderr,
+                       "Environment does not fit into available sectors\n");
+               errno = EINVAL;
+               return -1;
+       }
+
+ err:
        close(fd);
        return rc;
 }
@@ -1379,108 +1706,100 @@ static int parse_config(struct env_opts *opts)
                return -1;
        }
 #else
-       DEVNAME (0) = DEVICE1_NAME;
-       DEVOFFSET (0) = DEVICE1_OFFSET;
-       ENVSIZE (0) = ENV1_SIZE;
-       /* Default values are: erase-size=env-size */
-       DEVESIZE (0) = ENVSIZE (0);
-       /* #sectors=env-size/erase-size (rounded up) */
-       ENVSECTORS (0) = (ENVSIZE(0) + DEVESIZE(0) - 1) / DEVESIZE(0);
+       DEVNAME(0) = DEVICE1_NAME;
+       DEVOFFSET(0) = DEVICE1_OFFSET;
+       ENVSIZE(0) = ENV1_SIZE;
+
+       /* Set defaults for DEVESIZE, ENVSECTORS later once we
+        * know DEVTYPE
+        */
 #ifdef DEVICE1_ESIZE
-       DEVESIZE (0) = DEVICE1_ESIZE;
+       DEVESIZE(0) = DEVICE1_ESIZE;
 #endif
 #ifdef DEVICE1_ENVSECTORS
-       ENVSECTORS (0) = DEVICE1_ENVSECTORS;
+       ENVSECTORS(0) = DEVICE1_ENVSECTORS;
 #endif
 
 #ifdef HAVE_REDUND
-       DEVNAME (1) = DEVICE2_NAME;
-       DEVOFFSET (1) = DEVICE2_OFFSET;
-       ENVSIZE (1) = ENV2_SIZE;
-       /* Default values are: erase-size=env-size */
-       DEVESIZE (1) = ENVSIZE (1);
-       /* #sectors=env-size/erase-size (rounded up) */
-       ENVSECTORS (1) = (ENVSIZE(1) + DEVESIZE(1) - 1) / DEVESIZE(1);
+       DEVNAME(1) = DEVICE2_NAME;
+       DEVOFFSET(1) = DEVICE2_OFFSET;
+       ENVSIZE(1) = ENV2_SIZE;
+
+       /* Set defaults for DEVESIZE, ENVSECTORS later once we
+        * know DEVTYPE
+        */
 #ifdef DEVICE2_ESIZE
-       DEVESIZE (1) = DEVICE2_ESIZE;
+       DEVESIZE(1) = DEVICE2_ESIZE;
 #endif
 #ifdef DEVICE2_ENVSECTORS
-       ENVSECTORS (1) = 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;
 
                if (ENVSIZE(0) != ENVSIZE(1)) {
-                       ENVSIZE(0) = ENVSIZE(1) = min(ENVSIZE(0), ENVSIZE(1));
                        fprintf(stderr,
-                               "Redundant environments have inequal size, set to 0x%08lx\n",
-                               ENVSIZE(1));
+                               "Redundant environments have unequal size");
+                       return -1;
                }
        }
 
        usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
-       if (HaveRedundEnv)
+       if (have_redund_env)
                usable_envsize -= sizeof(char);
 
-       if (opts->aes_flag)
-               usable_envsize &= ~(AES_KEY_LENGTH - 1);
-
        return 0;
 }
 
 #if defined(CONFIG_FILE)
-static int get_config (char *fname)
+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");
+       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));
+                           &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
 
                if (rc < 3)
                        continue;
 
                DEVNAME(i) = devname;
 
-               if (rc < 4)
-                       /* Assume the erase size is the same as the env-size */
-                       DEVESIZE(i) = ENVSIZE(i);
-
-               if (rc < 5)
-                       /* Assume enough env sectors to cover the environment */
-                       ENVSECTORS (i) = (ENVSIZE(i) + DEVESIZE(i) - 1) / DEVESIZE(i);
+               /* Set defaults for DEVESIZE, ENVSECTORS later once we
+                * know DEVTYPE
+                */
 
                i++;
        }
-       fclose (fp);
+       free(line);
+       fclose(fp);
 
-       HaveRedundEnv = i - 1;
-       if (!i) {                       /* No valid entries found */
+       have_redund_env = i - 1;
+       if (!i) {               /* No valid entries found */
                errno = EINVAL;
                return -1;
        } else