X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=tools%2Fenv%2Ffw_env.c;h=299e0c9608bb2bc3b9e0298929726c14a337f96c;hb=af1b7286d8b2712cff5779d8a1565afed9d9d8e6;hp=fba4c8c6654f0640ddd472d37d11dbf3d7504ac7;hpb=0f507779ca00d90cdd4bcc8252630370339b7ea6;p=oweals%2Fu-boot.git diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index fba4c8c665..299e0c9608 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -8,10 +8,15 @@ * SPDX-License-Identifier: GPL-2.0+ */ +#define _GNU_SOURCE + +#include #include #include #include +#include #include +#include #include #include #include @@ -29,14 +34,17 @@ # include #endif +#include "fw_env_private.h" #include "fw_env.h" -#include +struct env_opts default_opts = { +#ifdef CONFIG_FILE + .config_file = CONFIG_FILE +#endif +}; #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#define WHITESPACE(c) ((c == '\t') || (c == ' ')) - #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ @@ -44,8 +52,8 @@ _min1 < _min2 ? _min1 : _min2; }) struct envdev_s { - char devname[16]; /* Device name */ - ulong devoff; /* Device offset */ + const char *devname; /* Device name */ + long long devoff; /* Device offset */ ulong env_size; /* environment size */ ulong erase_size; /* device erase size */ ulong env_sectors; /* number of environment sectors */ @@ -71,7 +79,8 @@ static int dev_current; #define CUR_ENVSIZE ENVSIZE(dev_current) -#define ENV_SIZE getenvsize() +static unsigned long usable_envsize; +#define ENV_SIZE usable_envsize struct env_image_single { uint32_t crc; /* CRC32 over data bytes */ @@ -102,10 +111,7 @@ static struct environment environment = { .flag_scheme = FLAG_NONE, }; -/* Is AES encryption used? */ -static int aes_flag; -static uint8_t aes_key[AES_KEY_LENGTH] = { 0 }; -static int env_aes_cbc_crypt(char *data, const int enc); +static int env_aes_cbc_crypt(char *data, const int enc, uint8_t *key); static int HaveRedundEnv = 0; @@ -117,42 +123,49 @@ static unsigned char obsolete_flag = 0; #include static int flash_io (int mode); -static char *envmatch (char * s1, char * s2); -static int parse_config (void); +static int parse_config(struct env_opts *opts); #if defined(CONFIG_FILE) static int get_config (char *); #endif -static inline ulong getenvsize (void) -{ - ulong rc = CUR_ENVSIZE - sizeof(long); - - if (HaveRedundEnv) - rc -= sizeof (char); - - if (aes_flag) - rc &= ~(AES_KEY_LENGTH - 1); - return rc; +static char *skip_chars(char *s) +{ + for (; *s != '\0'; s++) { + if (isblank(*s)) + return s; + } + return NULL; } -static char *fw_string_blank(char *s, int noblank) +static char *skip_blanks(char *s) { - int i; - int len = strlen(s); - - for (i = 0; i < len; i++, s++) { - if ((noblank && !WHITESPACE(*s)) || - (!noblank && WHITESPACE(*s))) - break; + for (; *s != '\0'; s++) { + if (!isblank(*s)) + return s; } - if (i == len) + return NULL; +} + +/* + * 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; - return s; + 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. */ @@ -204,7 +217,7 @@ char *fw_getdefenv(char *name) return NULL; } -static int parse_aes_key(char *key) +int parse_aes_key(char *key, uint8_t *bin_key) { char tmp[5] = { '0', 'x', 0, 0, 0 }; unsigned long ul; @@ -226,11 +239,9 @@ static int parse_aes_key(char *key) "## Error: '-a' option requires valid AES key\n"); return -1; } - aes_key[i] = ul & 0xff; + bin_key[i] = ul & 0xff; key += 2; } - aes_flag = 1; - return 0; } @@ -238,29 +249,24 @@ static int parse_aes_key(char *key) * Print the current definition of one, or more, or all * environment variables */ -int fw_printenv (int argc, char *argv[]) +int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts) { - char *env, *nxt; - int i, n_flag; - int rc = 0; + int i, rc = 0; - if (argc >= 2 && strcmp(argv[1], "-a") == 0) { - if (argc < 3) { - fprintf(stderr, - "## Error: '-a' option requires AES key\n"); - return -1; - } - rc = parse_aes_key(argv[2]); - if (rc) - return rc; - argv += 2; - argc -= 2; + if (value_only && argc != 1) { + fprintf(stderr, + "## Error: `-n' option requires exactly one argument\n"); + return -1; } - if (fw_env_open()) + if (!opts) + opts = &default_opts; + + if (fw_env_open(opts)) return -1; - if (argc == 1) { /* 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]) { @@ -272,59 +278,44 @@ int fw_printenv (int argc, char *argv[]) printf ("%s\n", env); } + fw_env_close(opts); return 0; } - if (strcmp (argv[1], "-n") == 0) { - n_flag = 1; - ++argv; - --argc; - if (argc != 2) { - fprintf (stderr, "## Error: " - "`-n' option requires exactly one argument\n"); - return -1; - } - } else { - n_flag = 0; - } - - for (i = 1; 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 (!n_flag) { - fputs (name, stdout); - putc ('=', stdout); - } - puts (val); - break; - } - } + val = fw_getenv(name); if (!val) { 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(void) +int fw_env_flush(struct env_opts *opts) { int ret; - if (aes_flag) { - ret = env_aes_cbc_crypt(environment.data, 1); + + 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"); @@ -477,49 +468,41 @@ int fw_env_write(char *name, char *value) * modified or deleted * */ -int fw_setenv(int argc, char *argv[]) +int fw_setenv(int argc, char *argv[], struct env_opts *opts) { - int i, rc; + int i; size_t len; - char *name; + char *name, **valv; char *value = NULL; + int valc; + int ret; - if (argc < 2) { - errno = EINVAL; - return -1; - } - - if (strcmp(argv[1], "-a") == 0) { - if (argc < 3) { - fprintf(stderr, - "## Error: '-a' option requires AES key\n"); - return -1; - } - rc = parse_aes_key(argv[2]); - if (rc) - return rc; - argv += 2; - argc -= 2; - } + if (!opts) + opts = &default_opts; - if (argc < 2) { + if (argc < 1) { + fprintf(stderr, "## Error: variable name missing\n"); errno = EINVAL; return -1; } - if (fw_env_open()) { + if (fw_env_open(opts)) { fprintf(stderr, "Error: environment not initialized\n"); return -1; } - name = argv[1]; + name = argv[0]; + valv = argv + 1; + valc = argc - 1; - if (env_flags_validate_env_set_params(argc, argv) < 0) - return 1; + if (env_flags_validate_env_set_params(name, valv, valc) < 0) { + fw_env_close(opts); + return -1; + } len = 0; - for (i = 2; i < argc; ++i) { - char *val = argv[i]; + for (i = 0; i < valc; ++i) { + char *val = valv[i]; size_t val_len = strlen(val); if (value) @@ -541,7 +524,10 @@ int fw_setenv(int argc, char *argv[]) free(value); - return fw_env_close(); + ret = fw_env_flush(opts); + fw_env_close(opts); + + return ret; } /* @@ -561,7 +547,7 @@ int fw_setenv(int argc, char *argv[]) * 0 - OK * -1 - Error */ -int fw_parse_script(char *fname) +int fw_parse_script(char *fname, struct env_opts *opts) { FILE *fp; char dump[1024]; /* Maximum line length in the file */ @@ -571,7 +557,10 @@ int fw_parse_script(char *fname) int len; int ret = 0; - if (fw_env_open()) { + if (!opts) + opts = &default_opts; + + if (fw_env_open(opts)) { fprintf(stderr, "Error: environment not initialized\n"); return -1; } @@ -604,31 +593,29 @@ int fw_parse_script(char *fname) } /* Drop ending line feed / carriage return */ - while (len > 0 && (dump[len - 1] == '\n' || - dump[len - 1] == '\r')) { - dump[len - 1] = '\0'; - len--; - } + dump[--len] = '\0'; + if (len && dump[len - 1] == '\r') + dump[--len] = '\0'; /* Skip comment or empty lines */ - if ((len == 0) || dump[0] == '#') + if (len == 0 || dump[0] == '#') continue; /* * Search for variable's name, * remove leading whitespaces */ - name = fw_string_blank(dump, 1); + name = skip_blanks(dump); if (!name) continue; /* The first white space is the end of variable name */ - val = fw_string_blank(name, 0); + val = skip_chars(name); len = strlen(name); if (val) { *val++ = '\0'; if ((val - name) < len) - val = fw_string_blank(val, 1); + val = skip_blanks(val); else val = NULL; } @@ -661,10 +648,23 @@ int fw_parse_script(char *fname) if (strcmp(fname, "-") != 0) fclose(fp); - ret |= fw_env_close(); + ret |= fw_env_flush(opts); + + fw_env_close(opts); return ret; +} +/** + * environment_end() - compute offset of first byte right after environemnt + * @dev - index of enviroment buffer + * Return: + * device offset of first byte right after environemnt + */ +off_t environment_end(int dev) +{ + /* environment is block aligned */ + return DEVOFFSET(dev) + ENVSECTORS(dev) * DEVESIZE(dev); } /* @@ -673,10 +673,10 @@ int fw_parse_script(char *fname) * > 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"); @@ -685,8 +685,8 @@ static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) 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; } @@ -701,13 +701,12 @@ static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) * 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) + 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 - @@ -719,35 +718,27 @@ static int flash_read_buf (int dev, int fd, void *buf, size_t count, /* 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; - /* 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); + 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"); @@ -773,7 +764,8 @@ static int flash_read_buf (int dev, int fd, void *buf, size_t count, } #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); @@ -785,12 +777,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; @@ -806,7 +798,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; @@ -814,27 +805,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 @@ -859,8 +847,7 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count, 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; @@ -871,8 +858,9 @@ 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 @@ -886,7 +874,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. @@ -900,11 +888,11 @@ 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); + rc = flash_bad_block(fd, DEVTYPE(dev), blockstart); if (rc < 0) /* block test failed */ return rc; - if (blockstart + erasesize > top_of_range) { + if (blockstart + erasesize > environment_end(dev)) { fprintf (stderr, "End of range reached, aborting\n"); return -1; } @@ -914,11 +902,11 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count, 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", @@ -935,8 +923,9 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count, } #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", @@ -944,7 +933,7 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count, return -1; } - if (mtd_type != MTD_ABSENT) + if (DEVTYPE(dev) != MTD_ABSENT) ioctl(fd, MEMLOCK, &erase); processed += erasesize; @@ -985,15 +974,15 @@ static int flash_flag_obsolete (int dev, int fd, off_t offset) } /* Encrypt or decrypt the environment before writing or reading it. */ -static int env_aes_cbc_crypt(char *payload, const int enc) +static int env_aes_cbc_crypt(char *payload, const int enc, uint8_t *key) { uint8_t *data = (uint8_t *)payload; - const int len = getenvsize(); + 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(aes_key, key_exp); + aes_expand_key(key, key_exp); /* Calculate the number of AES blocks to encrypt. */ aes_blocks = DIV_ROUND_UP(len, AES_KEY_LENGTH); @@ -1026,13 +1015,12 @@ static int flash_write (int fd_current, int fd_target, int dev_target) } #ifdef DEBUG - fprintf(stderr, "Writing new environment at 0x%lx on %s\n", + fprintf(stderr, "Writing new environment at 0x%llx on %s\n", DEVOFFSET (dev_target), DEVNAME (dev_target)); #endif 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; @@ -1042,7 +1030,7 @@ static int flash_write (int fd_current, int fd_target, int dev_target) offsetof (struct env_image_redundant, flags); #ifdef DEBUG fprintf(stderr, - "Setting obsolete flag in environment at 0x%lx on %s\n", + "Setting obsolete flag in environment at 0x%llx on %s\n", DEVOFFSET (dev_current), DEVNAME (dev_current)); #endif flash_flag_obsolete (dev_current, fd_current, offset); @@ -1053,41 +1041,10 @@ static int flash_write (int fd_current, int fd_target, int dev_target) static int flash_read (int fd) { - struct mtd_info_user mtdinfo; - struct stat st; int rc; - rc = fstat(fd, &st); - if (rc < 0) { - fprintf(stderr, "Cannot stat the file %s\n", - DEVNAME(dev_current)); - return -1; - } - - if (S_ISCHR(st.st_mode)) { - rc = ioctl(fd, MEMGETINFO, &mtdinfo); - if (rc < 0) { - fprintf(stderr, "Cannot get MTD information for %s\n", - DEVNAME(dev_current)); - return -1; - } - if (mtdinfo.type != MTD_NORFLASH && - mtdinfo.type != MTD_NANDFLASH && - mtdinfo.type != MTD_DATAFLASH && - mtdinfo.type != MTD_UBIVOLUME) { - fprintf (stderr, "Unsupported flash type %u on %s\n", - mtdinfo.type, DEVNAME(dev_current)); - return -1; - } - } else { - memset(&mtdinfo, 0, sizeof(mtdinfo)); - mtdinfo.type = MTD_ABSENT; - } - - DEVTYPE(dev_current) = mtdinfo.type; - rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, - DEVOFFSET (dev_current), mtdinfo.type); + DEVOFFSET(dev_current)); if (rc != CUR_ENVSIZE) return -1; @@ -1152,52 +1109,37 @@ exit: 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. - */ - -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; -} - /* * Prevent confusion if running from erased flash memory */ -int fw_env_open(void) +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; struct env_image_single *single; struct env_image_redundant *redundant; - if (parse_config ()) /* should fill envdevices */ - return -1; + if (!opts) + opts = &default_opts; + + 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 */ @@ -1216,15 +1158,18 @@ int fw_env_open(void) } dev_current = 0; - if (flash_io (O_RDONLY)) - return -1; + if (flash_io(O_RDONLY)) { + ret = -EIO; + goto open_cleanup; + } crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); - if (aes_flag) { - ret = env_aes_cbc_crypt(environment.data, 0); + if (opts->aes_flag) { + ret = env_aes_cbc_crypt(environment.data, 0, + opts->aes_key); if (ret) - return ret; + goto open_cleanup; } crc0_ok = (crc0 == *environment.crc); @@ -1243,7 +1188,8 @@ int fw_env_open(void) fprintf(stderr, "Not enough memory for environment (%ld bytes)\n", CUR_ENVSIZE); - return -1; + ret = -ENOMEM; + goto open_cleanup; } redundant = addr1; @@ -1252,8 +1198,10 @@ int fw_env_open(void) * other pointers in environment still point inside addr0 */ environment.image = addr1; - if (flash_io (O_RDONLY)) - return -1; + if (flash_io(O_RDONLY)) { + ret = -EIO; + goto open_cleanup; + } /* Check flag scheme compatibility */ if (DEVTYPE(dev_current) == MTD_NORFLASH && @@ -1273,15 +1221,17 @@ int fw_env_open(void) environment.flag_scheme = FLAG_INCREMENTAL; } else { fprintf (stderr, "Incompatible flash types!\n"); - return -1; + ret = -EINVAL; + goto open_cleanup; } crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); - if (aes_flag) { - ret = env_aes_cbc_crypt(redundant->data, 0); + if (opts->aes_flag) { + ret = env_aes_cbc_crypt(redundant->data, 0, + opts->aes_key); if (ret) - return ret; + goto open_cleanup; } crc1_ok = (crc1 == redundant->crc); @@ -1353,28 +1303,140 @@ int fw_env_open(void) #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 parse_config () +static int check_device_config(int dev) { struct stat st; + int fd, rc = 0; + + fd = open(DEVNAME(dev), O_RDONLY); + if (fd < 0) { + fprintf(stderr, + "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)); + goto err; + } + + if (S_ISCHR(st.st_mode)) { + struct mtd_info_user mtdinfo; + rc = ioctl(fd, MEMGETINFO, &mtdinfo); + if (rc < 0) { + fprintf(stderr, "Cannot get MTD information for %s\n", + DEVNAME(dev)); + goto err; + } + if (mtdinfo.type != MTD_NORFLASH && + mtdinfo.type != MTD_NANDFLASH && + mtdinfo.type != MTD_DATAFLASH && + mtdinfo.type != MTD_UBIVOLUME) { + fprintf(stderr, "Unsupported flash type %u on %s\n", + mtdinfo.type, DEVNAME(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 + * from the end of the block device + */ + if (DEVOFFSET(dev) < 0) { + rc = ioctl(fd, BLKGETSIZE64, &size); + if (rc < 0) { + 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", + DEVOFFSET(dev), DEVNAME(dev)); +#endif + } + } + + 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; +} + +static int parse_config(struct env_opts *opts) +{ + int rc; + + if (!opts) + opts = &default_opts; #if defined(CONFIG_FILE) /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ - if (get_config (CONFIG_FILE)) { - fprintf (stderr, - "Cannot parse config file: %s\n", strerror (errno)); + if (get_config(opts->config_file)) { + fprintf(stderr, "Cannot parse config file '%s': %m\n", + opts->config_file); return -1; } #else - strcpy (DEVNAME (0), DEVICE1_NAME); + 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); + + /* Set defaults for DEVESIZE, ENVSECTORS later once we + * know DEVTYPE + */ #ifdef DEVICE1_ESIZE DEVESIZE (0) = DEVICE1_ESIZE; #endif @@ -1383,13 +1445,13 @@ static int parse_config () #endif #ifdef HAVE_REDUND - strcpy (DEVNAME (1), DEVICE2_NAME); + 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); + + /* Set defaults for DEVESIZE, ENVSECTORS later once we + * know DEVTYPE + */ #ifdef DEVICE2_ESIZE DEVESIZE (1) = DEVICE2_ESIZE; #endif @@ -1399,19 +1461,29 @@ static int parse_config () HaveRedundEnv = 1; #endif #endif - if (stat (DEVNAME (0), &st)) { - fprintf (stderr, - "Cannot access MTD device %s: %s\n", - DEVNAME (0), strerror (errno)); - return -1; - } + rc = check_device_config(0); + if (rc < 0) + return rc; - if (HaveRedundEnv && stat (DEVNAME (1), &st)) { - fprintf (stderr, - "Cannot access MTD device %s: %s\n", - DEVNAME (1), strerror (errno)); - return -1; + if (HaveRedundEnv) { + rc = check_device_config(1); + if (rc < 0) + return rc; + + if (ENVSIZE(0) != ENVSIZE(1)) { + fprintf(stderr, + "Redundant environments have unequal size"); + return -1; + } } + + usable_envsize = CUR_ENVSIZE - sizeof(uint32_t); + if (HaveRedundEnv) + usable_envsize -= sizeof(char); + + if (opts->aes_flag) + usable_envsize &= ~(AES_KEY_LENGTH - 1); + return 0; } @@ -1422,6 +1494,7 @@ static int get_config (char *fname) int i = 0; int rc; char dump[128]; + char *devname; fp = fopen (fname, "r"); if (fp == NULL) @@ -1432,23 +1505,21 @@ static int get_config (char *fname) if (dump[0] == '#') continue; - rc = sscanf (dump, "%s %lx %lx %lx %lx", - DEVNAME (i), - &DEVOFFSET (i), - &ENVSIZE (i), - &DEVESIZE (i), - &ENVSECTORS (i)); + rc = sscanf(dump, "%ms %lli %lx %lx %lx", + &devname, + &DEVOFFSET(i), + &ENVSIZE(i), + &DEVESIZE(i), + &ENVSECTORS(i)); if (rc < 3) continue; - if (rc < 4) - /* Assume the erase size is the same as the env-size */ - DEVESIZE(i) = ENVSIZE(i); + DEVNAME(i) = devname; - 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++; }