+// 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>
.flag_scheme = FLAG_NONE,
};
-static int HaveRedundEnv = 0;
+static int have_redund_env;
static unsigned char active_flag = 1;
/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
return 0;
}
-static int flash_io(int mode)
+static int flash_open_tempfile(const char **dname, const char **target_temp)
{
- int fd_current, fd_target, rc, dev_target;
+ char *dup_name = strdup(DEVNAME(dev_current));
+ char *temp_name = NULL;
+ int rc = -1;
- /* dev_current: fd_current, erase_current */
- fd_current = open(DEVNAME(dev_current), mode);
- if (fd_current < 0) {
- fprintf(stderr,
- "Can't open %s: %s\n",
- DEVNAME(dev_current), strerror(errno));
+ if (!dup_name)
return -1;
+
+ *dname = dirname(dup_name);
+ if (!*dname)
+ goto err;
+
+ rc = asprintf(&temp_name, "%s/XXXXXX", *dname);
+ if (rc == -1)
+ goto err;
+
+ rc = mkstemp(temp_name);
+ if (rc == -1) {
+ /* fall back to in place write */
+ fprintf(stderr,
+ "Can't create %s: %s\n", temp_name, strerror(errno));
+ free(temp_name);
+ } else {
+ *target_temp = temp_name;
+ /* deliberately leak dup_name as dname /might/ point into
+ * it and we need it for our caller
+ */
+ dup_name = NULL;
}
- if (mode == O_RDWR) {
- if (HaveRedundEnv) {
- /* switch to next partition for writing */
- dev_target = !dev_current;
- /* dev_target: fd_target, erase_target */
- fd_target = open(DEVNAME(dev_target), mode);
- if (fd_target < 0) {
- fprintf(stderr,
- "Can't open %s: %s\n",
- DEVNAME(dev_target), strerror(errno));
- rc = -1;
- goto exit;
- }
- } else {
- dev_target = dev_current;
- fd_target = fd_current;
+err:
+ if (dup_name)
+ free(dup_name);
+
+ return rc;
+}
+
+static int flash_io_write(int fd_current)
+{
+ int fd_target = -1, rc, dev_target;
+ const char *dname, *target_temp = NULL;
+
+ if (have_redund_env) {
+ /* switch to next partition for writing */
+ dev_target = !dev_current;
+ /* dev_target: fd_target, erase_target */
+ fd_target = open(DEVNAME(dev_target), O_RDWR);
+ if (fd_target < 0) {
+ fprintf(stderr,
+ "Can't open %s: %s\n",
+ DEVNAME(dev_target), strerror(errno));
+ rc = -1;
+ goto exit;
}
+ } else {
+ struct stat sb;
+
+ if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) {
+ /* if any part of flash_open_tempfile() fails we fall
+ * back to in-place writes
+ */
+ fd_target = flash_open_tempfile(&dname, &target_temp);
+ }
+ dev_target = dev_current;
+ if (fd_target == -1)
+ fd_target = fd_current;
+ }
+
+ rc = flash_write(fd_current, fd_target, dev_target);
- rc = flash_write(fd_current, fd_target, dev_target);
+ if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+ fprintf(stderr,
+ "fsync failed on %s: %s\n",
+ DEVNAME(dev_current), strerror(errno));
+ }
- if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
+ if (fd_current != fd_target) {
+ if (fsync(fd_target) &&
+ !(errno == EINVAL || errno == EROFS)) {
fprintf(stderr,
"fsync failed on %s: %s\n",
DEVNAME(dev_current), strerror(errno));
}
- if (HaveRedundEnv) {
- if (fsync(fd_target) &&
- !(errno == EINVAL || errno == EROFS)) {
+ if (close(fd_target)) {
+ fprintf(stderr,
+ "I/O error on %s: %s\n",
+ DEVNAME(dev_target), strerror(errno));
+ rc = -1;
+ }
+
+ if (target_temp) {
+ int dir_fd;
+
+ dir_fd = open(dname, O_DIRECTORY | O_RDONLY);
+ if (dir_fd == -1)
fprintf(stderr,
- "fsync failed on %s: %s\n",
- DEVNAME(dev_current), strerror(errno));
- }
+ "Can't open %s: %s\n",
+ dname, strerror(errno));
- if (close(fd_target)) {
+ if (rename(target_temp, DEVNAME(dev_target))) {
fprintf(stderr,
- "I/O error on %s: %s\n",
- DEVNAME(dev_target), strerror(errno));
+ "rename failed %s => %s: %s\n",
+ target_temp, DEVNAME(dev_target),
+ strerror(errno));
rc = -1;
}
+
+ if (dir_fd != -1 && fsync(dir_fd))
+ fprintf(stderr,
+ "fsync failed on %s: %s\n",
+ dname, strerror(errno));
+
+ if (dir_fd != -1 && close(dir_fd))
+ fprintf(stderr,
+ "I/O error on %s: %s\n",
+ dname, strerror(errno));
}
+ }
+ exit:
+ return rc;
+}
+
+static int flash_io(int mode)
+{
+ int fd_current, rc;
+
+ /* dev_current: fd_current, erase_current */
+ fd_current = open(DEVNAME(dev_current), mode);
+ if (fd_current < 0) {
+ fprintf(stderr,
+ "Can't open %s: %s\n",
+ DEVNAME(dev_current), strerror(errno));
+ return -1;
+ }
+
+ if (mode == O_RDWR) {
+ rc = flash_io_write(fd_current);
} else {
rc = flash_read(fd_current);
}
- exit:
if (close(fd_current)) {
fprintf(stderr,
"I/O error on %s: %s\n",
/* 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;
crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
crc0_ok = (crc0 == *environment.crc);
- if (!HaveRedundEnv) {
+ if (!have_redund_env) {
if (!crc0_ok) {
fprintf(stderr,
"Warning: Bad CRC, using default environment\n");
#ifdef DEVICE2_ENVSECTORS
ENVSECTORS(1) = DEVICE2_ENVSECTORS;
#endif
- HaveRedundEnv = 1;
+ have_redund_env = 1;
#endif
#endif
rc = check_device_config(0);
if (rc < 0)
return rc;
- if (HaveRedundEnv) {
+ if (have_redund_env) {
rc = check_device_config(1);
if (rc < 0)
return rc;
}
usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
- if (HaveRedundEnv)
+ if (have_redund_env)
usable_envsize -= sizeof(char);
return 0;
}
fclose(fp);
- HaveRedundEnv = i - 1;
+ have_redund_env = i - 1;
if (!i) { /* No valid entries found */
errno = EINVAL;
return -1;