+// 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
#include <errno.h>
#include <env_flags.h>
#include <fcntl.h>
+#include <libgen.h>
#include <linux/fs.h>
#include <linux/stringify.h>
#include <ctype.h>
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;
}
}
- 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;
}
}
+ free(line);
/* Close file if not stdin */
if (strcmp(fname, "-") != 0)
}
/**
- * environment_end() - compute offset of first byte right after environemnt
+ * environment_end() - compute offset of first byte right after environment
* @dev - index of enviroment buffer
* Return:
- * device offset of first byte right after environemnt
+ * device offset of first byte right after environment
*/
off_t environment_end(int dev)
{
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 (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), mode);
- if (fd_target < 0) {
- fprintf(stderr,
- "Can't open %s: %s\n",
- DEVNAME(dev_target), strerror(errno));
- rc = -1;
- goto exit;
- }
- } else {
- dev_target = dev_current;
- fd_target = fd_current;
+err:
+ if (dup_name)
+ free(dup_name);
+
+ return rc;
+}
+
+static int flash_io_write(int fd_current)
+{
+ int fd_target = -1, rc, dev_target;
+ const char *dname, *target_temp = NULL;
+
+ if (have_redund_env) {
+ /* switch to next partition for writing */
+ dev_target = !dev_current;
+ /* dev_target: fd_target, erase_target */
+ fd_target = open(DEVNAME(dev_target), O_RDWR);
+ if (fd_target < 0) {
+ fprintf(stderr,
+ "Can't open %s: %s\n",
+ DEVNAME(dev_target), strerror(errno));
+ rc = -1;
+ goto exit;
}
+ } else {
+ struct stat sb;
+
+ if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) {
+ /* if any part of flash_open_tempfile() fails we fall
+ * back to in-place writes
+ */
+ fd_target = flash_open_tempfile(&dname, &target_temp);
+ }
+ dev_target = dev_current;
+ if (fd_target == -1)
+ fd_target = fd_current;
+ }
- rc = flash_write(fd_current, fd_target, dev_target);
+ rc = flash_write(fd_current, fd_target, dev_target);
- if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+ if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+ fprintf(stderr,
+ "fsync failed on %s: %s\n",
+ DEVNAME(dev_current), strerror(errno));
+ }
+
+ if (fd_current != fd_target) {
+ if (fsync(fd_target) &&
+ !(errno == EINVAL || errno == EROFS)) {
fprintf(stderr,
"fsync failed on %s: %s\n",
DEVNAME(dev_current), strerror(errno));
}
- if (have_redund_env) {
- if (fsync(fd_target) &&
- !(errno == EINVAL || errno == EROFS)) {
+ if (close(fd_target)) {
+ fprintf(stderr,
+ "I/O error on %s: %s\n",
+ DEVNAME(dev_target), strerror(errno));
+ rc = -1;
+ }
+
+ if (target_temp) {
+ int dir_fd;
+
+ dir_fd = open(dname, O_DIRECTORY | O_RDONLY);
+ if (dir_fd == -1)
fprintf(stderr,
- "fsync failed on %s: %s\n",
- DEVNAME(dev_current), strerror(errno));
- }
+ "Can't open %s: %s\n",
+ dname, strerror(errno));
- if (close(fd_target)) {
+ if (rename(target_temp, DEVNAME(dev_target))) {
fprintf(stderr,
- "I/O error on %s: %s\n",
- DEVNAME(dev_target), strerror(errno));
+ "rename failed %s => %s: %s\n",
+ target_temp, DEVNAME(dev_target),
+ strerror(errno));
rc = -1;
}
+
+ if (dir_fd != -1 && fsync(dir_fd))
+ fprintf(stderr,
+ "fsync failed on %s: %s\n",
+ dname, strerror(errno));
+
+ if (dir_fd != -1 && close(dir_fd))
+ fprintf(stderr,
+ "I/O error on %s: %s\n",
+ dname, strerror(errno));
}
+ }
+ exit:
+ return rc;
+}
+
+static int flash_io(int mode)
+{
+ int fd_current, rc;
+
+ /* dev_current: fd_current, erase_current */
+ fd_current = open(DEVNAME(dev_current), mode);
+ if (fd_current < 0) {
+ fprintf(stderr,
+ "Can't open %s: %s\n",
+ DEVNAME(dev_current), strerror(errno));
+ return -1;
+ }
+
+ if (mode == O_RDWR) {
+ rc = flash_io_write(fd_current);
} else {
rc = flash_read(fd_current);
}
- exit:
if (close(fd_current)) {
fprintf(stderr,
"I/O error on %s: %s\n",
FILE *fp;
int i = 0;
int rc;
- char dump[128];
+ char *line = NULL;
+ size_t linesize = 0;
char *devname;
fp = fopen(fname, "r");
if (fp == NULL)
return -1;
- while (i < 2 && fgets(dump, sizeof(dump), fp)) {
- /* Skip incomplete conversions and comment strings */
- if (dump[0] == '#')
+ while (i < 2 && getline(&line, &linesize, fp) != -1) {
+ /* Skip comment strings */
+ if (line[0] == '#')
continue;
- rc = sscanf(dump, "%ms %lli %lx %lx %lx",
+ rc = sscanf(line, "%ms %lli %lx %lx %lx",
&devname,
&DEVOFFSET(i),
&ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
i++;
}
+ free(line);
fclose(fp);
have_redund_env = i - 1;