diff mbox

[09/13] tools: added file append functionality

Message ID 1465979400-2964-10-git-send-email-adrian@lisas.de
State Accepted
Headers show

Commit Message

Adrian Reber June 15, 2016, 8:29 a.m. UTC
The -a, --append can now be used to append any file to
flash image filesystem. It is required that -o, --output
specifies an output file name.

(cherry picked from commit 339c6f4d17cae84526c7cba63928bf7769de5abe)

Cherry picked from https://lisas.de/~adrian/slof/slof.git/

Signed-off-by: Adrian Reber <adrian@lisas.de>
---
 tools/sloffs.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 220 insertions(+), 12 deletions(-)
diff mbox

Patch

diff --git a/tools/sloffs.c b/tools/sloffs.c
index bd8ebd2..4db5cfe 100644
--- a/tools/sloffs.c
+++ b/tools/sloffs.c
@@ -21,6 +21,7 @@ 
 #include <stdlib.h>
 #include <byteswap.h>
 #include <getopt.h>
+#include <time.h>
 
 #include <calculatecrc.h>
 #include <crclib.h>
@@ -59,6 +60,11 @@  struct sloffs {
 	char *name;
 };
 
+/* sloffs metadata size:
+ * 4 * 8: 4 * uint64_t + (filename length) */
+#define SLOFFS_META (4 * 8)
+#define ALIGN64(x) (((x) + 7) & ~7)
+
 static struct sloffs *
 next_file(struct sloffs *sloffs)
 {
@@ -82,24 +88,194 @@  find_file(const void *data, const char *name)
 	return NULL;
 }
 
-static void
-sloffs_dump(const void *data)
+static struct stH *
+sloffs_header(const void *data)
 {
-	struct stH *header;
 	struct sloffs *sloffs;
-	int i;
-	uint64_t crc;
+	struct stH *header;
 
 	/* find the "header" file with all the information about
 	 * the flash image */
 	sloffs = find_file(data, "header");
 	if (!sloffs) {
 		printf("sloffs file \"header\" not found. aborting...\n");
-		return;
+		return NULL;
 	}
 
 	header = (struct stH *)((unsigned char *)sloffs +
 				be64_to_cpu(sloffs->data));
+	return header;
+}
+
+static uint64_t
+header_length(const void *data)
+{
+	struct sloffs *sloffs;
+
+	/* find the "header" file with all the information about
+	 * the flash image */
+	sloffs = find_file(data, "header");
+	if (!sloffs) {
+		printf("sloffs file \"header\" not found. aborting...\n");
+		return 0;
+	}
+	return be64_to_cpu(sloffs->len);
+}
+
+
+static void
+update_modification_time(struct stH *header)
+{
+	struct tm *tm;
+	time_t caltime;
+	char dastr[16] = { 0, };
+
+	/* update modification date
+	 * copied from create_crc.c */
+	caltime = time(NULL);
+	tm = localtime(&caltime);
+	strftime(dastr, 15, "0x%Y%m%d%H%M", tm);
+
+	/* this does not match the definition from
+	 * struct stH, but we immitate the bug from
+	 * flash image creation in create_crc.c */
+	*(uint64_t *)(header->mdate) = cpu_to_be64(strtoll(dastr, NULL, 16));
+}
+
+static void
+update_crc(void *data)
+{
+	uint64_t crc;
+	struct stH *header = sloffs_header(data);
+	uint64_t len = be64_to_cpu(header->flashlen);
+
+	/* calculate header CRC */
+	header->ui64CRC = 0;
+	crc = checkCRC(data, header_length(data), 0);
+	header->ui64CRC = cpu_to_be64(crc);
+	/* calculate flash image CRC */
+	crc = checkCRC(data, len, 0);
+	*(uint64_t *)(data + len - 8) = cpu_to_be64(crc);
+}
+
+static void
+sloffs_append(const void *data, const char *name, const char *dest)
+{
+	void *append;
+	unsigned char *write_data;
+	void *write_start;
+	int fd;
+	int out;
+	struct stat stat;
+	struct stH *header;
+	uint64_t new_len;
+	struct sloffs *sloffs;
+	struct sloffs new_file;
+
+	fd = open(name, O_RDONLY);
+
+	if (fd == -1) {
+		perror(name);
+		exit(1);
+	}
+
+	fstat(fd, &stat);
+	append = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	header = sloffs_header(data);
+
+	if (!header)
+		return;
+
+	new_len = ALIGN64(stat.st_size) + be64_to_cpu(header->flashlen);
+	/* add the length of the sloffs file meta information */
+	new_len += SLOFFS_META;
+	/* add the length of the filename */
+	new_len += ALIGN64(strlen(name) + 1);
+
+	out = open(dest, O_CREAT | O_RDWR | O_TRUNC, 00666);
+
+	if (out == -1) {
+		perror(dest);
+		exit(1);
+	}
+
+	/* write byte at the end to be able to mmap it */
+	lseek(out, new_len - 1, SEEK_SET);
+	write(out, "", 1);
+	write_start = mmap(NULL, new_len, PROT_READ | PROT_WRITE,
+			   MAP_SHARED, out, 0);
+
+	memset(write_start, 0, new_len);
+	memset(&new_file, 0, sizeof(struct sloffs));
+
+	new_file.len = cpu_to_be64(stat.st_size);
+	new_file.data = cpu_to_be64(SLOFFS_META + ALIGN64(strlen(name) + 1));
+
+	if (write_start == MAP_FAILED) {
+		perror("mmap");
+		exit(1);
+	}
+
+	write_data = memcpy(write_start, data, be64_to_cpu(header->flashlen));
+	/* -8: overwrite old CRC */
+	write_data += be64_to_cpu(header->flashlen) - 8;
+	memcpy(write_data, &new_file, SLOFFS_META);
+	write_data += SLOFFS_META;
+	/* write the filename */
+	memcpy(write_data, name, strlen(name));
+	write_data += ALIGN64(strlen(name) + 1 );
+	memcpy(write_data, append, stat.st_size);
+
+	write_data = write_start;
+
+	/* find last file */
+	sloffs = (struct sloffs *)write_start;
+	for (;;) {
+		if (be64_to_cpu(sloffs->next) == 0)
+			break;
+		sloffs = next_file(sloffs);
+	}
+	/* get the distance to the next file */
+	sloffs->next = ALIGN64(be64_to_cpu(sloffs->len));
+	/* and the offset were the data starts */
+	sloffs->next += be64_to_cpu(sloffs->data);
+	/* and we have to skip the end of file marker
+	 * if one is there; if the last uint64_t is -1
+	 * it is an end of file marker; this is a bit dangerous
+	 * but there is no other way to detect the end of
+	 * file marker */
+	if ((uint64_t)be64_to_cpu(*(uint64_t *)((unsigned char *)sloffs +
+						sloffs->next)) == (uint64_t)-1ULL)
+		sloffs->next += 8;
+
+	sloffs->next = cpu_to_be64(sloffs->next);
+
+	/* update new length of flash image */
+	header = sloffs_header(write_start);
+	header->flashlen = cpu_to_be64(new_len);
+
+	update_modification_time(header);
+
+	update_crc(write_start);
+
+	munmap(append, stat.st_size);
+	munmap(write_start, new_len);
+	close(fd);
+	close(out);
+}
+
+static void
+sloffs_dump(const void *data)
+{
+	struct stH *header;
+	struct sloffs *sloffs;
+	int i;
+	uint64_t crc;
+
+	header = sloffs_header(data);
+
+	if (!header)
+		return;
 
 	if (memcmp(FLASHFS_MAGIC, header->magic, strlen(FLASHFS_MAGIC))) {
 		printf("sloffs magic not found. "
@@ -138,7 +314,12 @@  sloffs_dump(const void *data)
 	printf("  Revision    : %s\n", header->platform_revision);
 	crc = be64_to_cpu(header->ui64CRC);
 	printf("  Header CRC  : 0x%016llx CRC check: ", crc);
-	crc = calCRCword((unsigned char *)data, be64_to_cpu(sloffs->len), 0);
+	/* to test the CRC of the header we need to know the actual
+	 * size of the file and not just the size of the data
+	 * which could be easily obtained with sizeof(struct stH);
+	 * the actual size can only be obtained from the filesystem
+	 * meta information */
+	crc = calCRCword((unsigned char *)data, header_length(data), 0);
 	if (!crc)
 		printf("[OK]");
 	else
@@ -227,10 +408,16 @@  usage(void)
 	printf("Usage:\n");
 	printf("  sloffs [OPTION]... [FILE]\n\n");
 	printf("Options:\n");
-	printf("  -h, --help      show this help, then exit\n");
-	printf("  -l, --list      print a listing of all files in the image\n");
-	printf("  -v, --version   print the version, then exit\n");
-	printf("  -d, --dump      dump the information from the header\n");
+	printf("  -h, --help             show this help, then exit\n");
+	printf(
+		"  -l, --list             print a listing of all files in the image\n");
+	printf("  -v, --version          print the version, then exit\n");
+	printf("  -d, --dump             dump the information from the header\n");
+	printf(
+		"  -a, --append=FILENAME  append file at the end of the existing image\n");
+	printf("  -o, --output=FILENAME  if appending a file this parameter is\n");
+	printf(
+		"                         to specify the name of the output file\n");
 	printf("\n");
 	exit(1);
 }
@@ -246,11 +433,15 @@  main(int argc, char *argv[])
 		{ "list", 0, NULL, 'l' },
 		{ "version", 0, NULL, 'v' },
 		{ "dump", 0, NULL, 'd' },
+		{ "append", 1, NULL, 'a' },
+		{ "output", 1, NULL, 'o' },
 		{ 0, 0, 0, 0 }
 	};
-	const char *soption = "dhlv";
+	const char *soption = "dhlva:o:";
 	int c;
 	char mode = 0;
+	char *append = NULL;
+	char *output = NULL;
 
 	for (;;) {
 		c = getopt_long(argc, argv, soption, loption, NULL);
@@ -266,6 +457,13 @@  main(int argc, char *argv[])
 		case 'd':
 			mode = 'd';
 			break;
+		case 'a':
+			mode = 'a';
+			append = strdup(optarg);
+			break;
+		case 'o':
+			output = strdup(optarg);
+			break;
 		case 'h':
 		default:
 			usage();
@@ -292,8 +490,18 @@  main(int argc, char *argv[])
 	case 'd':
 		sloffs_dump(file);
 		break;
+	case 'a':
+		if (!output) {
+			printf(
+				"sloffs requires -o, --output=FILENAME when in append mode\n\n");
+			usage();
+		}
+		sloffs_append(file, append, output);
+		break;
 	}
 
+	free(append);
+	free(output);
 	munmap(file, stat.st_size);
 	close(fd);
 	return 0;