Skip to contentSkip to conversation

tools: env: Fix format warnings in debug

PATCH New Marcin Niestroj 2 participants 5 messages May 6, 2016 – Jul 26, 2016

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 tools: env: Fix format warnings in debug

Format warnings (-Wformat) were shown in printf() calls after defining DEBUG macro.

Update format string and explicitly cast variables to suppress all warnings.

---
 tools/env/fw_env.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)
tools/env/fw_env.c +10 -7
index 06cf63d..b4b542a 100644
@@ -649,8 +649,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;
 		}
@@ -737,7 +737,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);
@@ -835,8 +836,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
@@ -899,8 +901,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",
Signature · 1 line
2.8.0
#1 lore raw

PATCH 1/2 tools: env: Fix format warnings in debug

ping

Quoted 61 lines · Marcin Niestroj

Format warnings (-Wformat) were shown in printf() calls after defining DEBUG macro.

Update format string and explicitly cast variables to suppress all warnings.

---
 tools/env/fw_env.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)
tools/env/fw_env.c +10 -7
index 06cf63d..b4b542a 100644
@@ -649,8 +649,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;
 		}
@@ -737,7 +737,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);
@@ -835,8 +836,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
@@ -899,8 +901,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",
Signature · 1 line
Marcin Niestroj
#2 lore raw

PATCH 2/2 tools: env: Add support for direct read/write UBI volumes

In reply to Marcin Niestroj

Up to now we were able to read/write environment data from/to UBI volumes only indirectly by gluebi driver. This driver creates NAND MTD on top of UBI volumes, which is quite a workaroung for this use case.

Add support for direct read/write UBI volumes in order to not use obsolete gluebi driver.

---
 tools/env/fw_env.c      | 249 +++++++++++++++++++++++++++++++++++++++++++++++-
 tools/env/fw_env.config |   8 ++
 2 files changed, 256 insertions(+), 1 deletion(-)
tools/env/fw_env.c +248 -1
index b4b542a..0e977e3 100644
@@ -24,6 +24,7 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #ifdef MTD_OLD
 # include <stdint.h>
@@ -33,6 +34,8 @@
 # include <mtd/mtd-user.h>
 #endif
 
+#include <mtd/ubi-user.h>
+
 #include "fw_env.h"
 
 struct common_args common_args;
@@ -54,6 +57,7 @@ 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] =
@@ -72,6 +76,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)
 
@@ -117,6 +122,229 @@ static unsigned char obsolete_flag = 0;
 #define DEFAULT_ENV_INSTANCE_STATIC
 #include <env_default.h>
 
+#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 char *envmatch (char * s1, char * s2);
 static int parse_config (void);
@@ -997,6 +1225,12 @@ static int flash_write (int fd_current, int fd_target, int 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));
@@ -1024,6 +1258,12 @@ static int flash_read (int fd)
 	struct stat st;
 	int rc;
 
+	if (IS_UBI(dev_current)) {
+		DEVTYPE(dev_current) = MTD_ABSENT;
+
+		return ubi_read(fd, environment.image, CUR_ENVSIZE);
+	}
+
 	rc = fstat(fd, &st);
 	if (rc < 0) {
 		fprintf(stderr, "Cannot stat the file %s\n",
@@ -1236,7 +1476,8 @@ int fw_env_open(void)
 			   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");
@@ -1369,6 +1610,12 @@ static int parse_config ()
 	HaveRedundEnv = 1;
 #endif
 #endif
+
+	/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
+	ubi_check_dev(0);
+	if (HaveRedundEnv)
+		ubi_check_dev(1);
+
 	if (stat (DEVNAME (0), &st)) {
 		fprintf (stderr,
 			"Cannot access MTD device %s: %s\n",
tools/env/fw_env.config +8 -0
index 6f216f9..db5498a 100644
@@ -23,3 +23,11 @@
 
 # VFAT example
 #/boot/uboot.env	0x0000          0x4000
+
+# UBI volume
+#/dev/ubi0_0		0x0		0x20000
+#/dev/ubi0_1		0x0		0x20000
+
+# UBI volume by name
+#/dev/ubi0:env		0x0		0x20000
+#/dev/ubi0:env-redund	0x0		0x20000
Signature · 1 line
2.8.0
#3 lore raw

PATCH 2/2 tools: env: Add support for direct read/write UBI volumes

Quoted 346 lines · Marcin Niestroj

Up to now we were able to read/write environment data from/to UBI volumes only indirectly by gluebi driver. This driver creates NAND MTD on top of UBI volumes, which is quite a workaroung for this use case.

Add support for direct read/write UBI volumes in order to not use obsolete gluebi driver.

---
 tools/env/fw_env.c      | 249 +++++++++++++++++++++++++++++++++++++++++++++++-
 tools/env/fw_env.config |   8 ++
 2 files changed, 256 insertions(+), 1 deletion(-)
tools/env/fw_env.c +248 -1
index b4b542a..0e977e3 100644
@@ -24,6 +24,7 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #ifdef MTD_OLD
 # include <stdint.h>
@@ -33,6 +34,8 @@
 # include <mtd/mtd-user.h>
 #endif
 
+#include <mtd/ubi-user.h>
+
 #include "fw_env.h"
 
 struct common_args common_args;
@@ -54,6 +57,7 @@ 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] =
@@ -72,6 +76,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)
 
@@ -117,6 +122,229 @@ static unsigned char obsolete_flag = 0;
 #define DEFAULT_ENV_INSTANCE_STATIC
 #include <env_default.h>
 
+#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 char *envmatch (char * s1, char * s2);
 static int parse_config (void);
@@ -997,6 +1225,12 @@ static int flash_write (int fd_current, int fd_target, int 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));
@@ -1024,6 +1258,12 @@ static int flash_read (int fd)
 	struct stat st;
 	int rc;
 
+	if (IS_UBI(dev_current)) {
+		DEVTYPE(dev_current) = MTD_ABSENT;
+
+		return ubi_read(fd, environment.image, CUR_ENVSIZE);
+	}
+
 	rc = fstat(fd, &st);
 	if (rc < 0) {
 		fprintf(stderr, "Cannot stat the file %s\n",
@@ -1236,7 +1476,8 @@ int fw_env_open(void)
 			   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");
@@ -1369,6 +1610,12 @@ static int parse_config ()
 	HaveRedundEnv = 1;
 #endif
 #endif
+
+	/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
+	ubi_check_dev(0);
+	if (HaveRedundEnv)
+		ubi_check_dev(1);
+
 	if (stat (DEVNAME (0), &st)) {
 		fprintf (stderr,
 			"Cannot access MTD device %s: %s\n",
tools/env/fw_env.config +8 -0
index 6f216f9..db5498a 100644
@@ -23,3 +23,11 @@
 
 # VFAT example
 #/boot/uboot.env	0x0000          0x4000
+
+# UBI volume
+#/dev/ubi0_0		0x0		0x20000
+#/dev/ubi0_1		0x0		0x20000
+
+# UBI volume by name
+#/dev/ubi0:env		0x0		0x20000
+#/dev/ubi0:env-redund	0x0		0x20000
Signature · 1 line
Marcin Niestroj
#4 lore raw

PATCH 2/2 tools: env: Add support for direct read/write UBI volumes

In reply to Marcin Niestroj

Hello Marcin,

Sorry for the long delay

Am 06.05.2016 um 14:58 schrieb Marcin Niestroj:

Quoted 4 lines

Up to now we were able to read/write environment data from/to UBI volumes only indirectly by gluebi driver. This driver creates NAND MTD on top of UBI volumes, which is quite a workaroung for this use case.

Quoted 8 lines

Add support for direct read/write UBI volumes in order to not use obsolete gluebi driver.

---
  tools/env/fw_env.c      | 249 +++++++++++++++++++++++++++++++++++++++++++++++-
  tools/env/fw_env.config |   8 ++
  2 files changed, 256 insertions(+), 1 deletion(-)

I tried to apply your patch, but this fails, and I was unsure, if I could fix it correct... can you please send a new rebased version?

Thanks!

bye,
Heiko

Quoted 334 lines
tools/env/fw_env.c +248 -1
index b4b542a..0e977e3 100644
@@ -24,6 +24,7 @@
  #include <sys/ioctl.h>
  #include <sys/stat.h>
  #include <unistd.h>
+#include <dirent.h>
 
  #ifdef MTD_OLD
  # include <stdint.h>
@@ -33,6 +34,8 @@
  # include <mtd/mtd-user.h>
  #endif
 
+#include <mtd/ubi-user.h>
+
  #include "fw_env.h"
 
  struct common_args common_args;
@@ -54,6 +57,7 @@ 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] =
@@ -72,6 +76,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)
 
@@ -117,6 +122,229 @@ static unsigned char obsolete_flag = 0;
  #define DEFAULT_ENV_INSTANCE_STATIC
  #include <env_default.h>
 
+#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 char *envmatch (char * s1, char * s2);
  static int parse_config (void);
@@ -997,6 +1225,12 @@ static int flash_write (int fd_current, int fd_target, int 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));
@@ -1024,6 +1258,12 @@ static int flash_read (int fd)
  	struct stat st;
  	int rc;
 
+	if (IS_UBI(dev_current)) {
+		DEVTYPE(dev_current) = MTD_ABSENT;
+
+		return ubi_read(fd, environment.image, CUR_ENVSIZE);
+	}
+
  	rc = fstat(fd, &st);
  	if (rc < 0) {
  		fprintf(stderr, "Cannot stat the file %s\n",
@@ -1236,7 +1476,8 @@ int fw_env_open(void)
  			   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");
@@ -1369,6 +1610,12 @@ static int parse_config ()
  	HaveRedundEnv = 1;
  #endif
  #endif
+
+	/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
+	ubi_check_dev(0);
+	if (HaveRedundEnv)
+		ubi_check_dev(1);
+
  	if (stat (DEVNAME (0), &st)) {
  		fprintf (stderr,
  			"Cannot access MTD device %s: %s\n",
tools/env/fw_env.config +8 -0
index 6f216f9..db5498a 100644
@@ -23,3 +23,11 @@
 
  # VFAT example
  #/boot/uboot.env	0x0000          0x4000
+
+# UBI volume
+#/dev/ubi0_0		0x0		0x20000
+#/dev/ubi0_1		0x0		0x20000
+
+# UBI volume by name
+#/dev/ubi0:env		0x0		0x20000
+#/dev/ubi0:env-redund	0x0		0x20000
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany