util: add archive tool
'archive' concatenates files into a single binary blob. Files are indexed by the base names. See archive.h for the format description. BUG=chromium:502066 BRANCH=tot TEST=Tested on Glados Change-Id: Iea108160e65c8c7bd34c02af824a77cb075ee64b Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 21a9ba860f29599ac029f8d49d32399c4e3a73a8 Original-Change-Id: I46b4efb339e3a1e05772ae752f2861026ca09cfc Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/311200 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Original-Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://review.coreboot.org/12925 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
This commit is contained in:
		
				
					committed by
					
						 Patrick Georgi
						Patrick Georgi
					
				
			
			
				
	
			
			
			
						parent
						
							491c016d77
						
					
				
				
					commit
					a5ae62e9d2
				
			
							
								
								
									
										14
									
								
								util/archive/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								util/archive/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| PROGRAM = archive | ||||
| CC ?= gcc | ||||
|  | ||||
| SRCS = $(PROGRAM).c | ||||
|  | ||||
| all: $(PROGRAM) | ||||
|  | ||||
| $(PROGRAM): $(SRCS) | ||||
| 	$(CC) -o $@ $^ | ||||
|  | ||||
| clean: | ||||
| 	rm -f $(PROGRAM) *.o | ||||
|  | ||||
| .PHONY: all clean | ||||
							
								
								
									
										281
									
								
								util/archive/archive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								util/archive/archive.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 The ChromiumOS Authors.  All rights reserved. | ||||
|  *                 written by Daisuke Nojiri <dnojiri@chromium.org> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; version 2 of the License. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
|  | ||||
| #include "archive.h" | ||||
| #include <endian.h> | ||||
| #include <errno.h> | ||||
| #include <libgen.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static struct directory *archive; | ||||
|  | ||||
| static void usage(void) | ||||
| { | ||||
| 	printf("Name:\n"); | ||||
| 	printf("	archive - concatenate files and create an archive\n"); | ||||
| 	printf("Usage:\n"); | ||||
| 	printf("	archive archive_name create file0 file1 ...\n"); | ||||
| } | ||||
|  | ||||
| static int get_file_size(const char *file) | ||||
| { | ||||
| 	FILE *fp = fopen(file, "rb"); | ||||
| 	int size; | ||||
|  | ||||
| 	if (!fp) { | ||||
| 		fprintf(stderr, "Error: failed to open %s\n", file); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	fseek(fp, 0, SEEK_END); | ||||
| 	size = ftell(fp); | ||||
| 	fclose(fp); | ||||
| 	if (size < 0) { | ||||
| 		fprintf(stderr, "Error: failed to get file size\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| static int set_file_name(const char *path, struct dentry *dest) | ||||
| { | ||||
| 	struct dentry *entry; | ||||
| 	char *name, *copy; | ||||
| 	int i; | ||||
|  | ||||
| 	copy = strdup(path); | ||||
| 	name = basename(copy); | ||||
|  | ||||
| 	/* check name length */ | ||||
| 	if (strlen(name) > NAME_LENGTH) { | ||||
| 		fprintf(stderr, "Error: file name '%s' exceeds %d chars\n", | ||||
| 			name, NAME_LENGTH); | ||||
| 		free(copy); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* check if there is a duplicate name */ | ||||
| 	entry = get_first_dentry(archive); | ||||
| 	for (i = 0; i < archive->count && &entry[i] != dest; i++) { | ||||
| 		if (!strncmp(entry[i].name, name, NAME_LENGTH)) { | ||||
| 			fprintf(stderr, "Error: duplicate name '%s'\n", name); | ||||
| 			free(copy); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* copy the name to the entry */ | ||||
| 	strncpy(dest->name, name, NAME_LENGTH); | ||||
| 	free(copy); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Add a file to the archive in RAM | ||||
|  * | ||||
|  * path: path to the file to be added | ||||
|  * entry: pointer to struct dentry where file header is created | ||||
|  * offset: offset of the file contents from the archive header | ||||
|  * | ||||
|  * return: 0 on success or -1 on error | ||||
|  */ | ||||
| static int add_file(const char *path, struct dentry *entry, uint32_t offset) | ||||
| { | ||||
| 	FILE *fp; | ||||
| 	int size; | ||||
|  | ||||
| 	if (!path || !*path || !entry) { | ||||
| 		fprintf(stderr, "Error: invalid path or entry\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	size = get_file_size(path); | ||||
| 	if (size < 0) | ||||
| 		return -1; | ||||
| 	if (offset + size > archive->size) { | ||||
| 		fprintf(stderr, "Error: invalid offset or size\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	fp = fopen(path, "rb"); | ||||
| 	if (!fp) { | ||||
| 		fprintf(stderr, "Error: failed to open %s (%d: %s)\n", | ||||
| 			path, errno, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (fread((char *)archive + offset, sizeof(char), size, fp) != size) { | ||||
| 		fprintf(stderr, "Error: failed to read %s\n", path); | ||||
| 		fclose(fp); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	fclose(fp); | ||||
|  | ||||
| 	/* set file name*/ | ||||
| 	if (set_file_name(path, entry)) | ||||
| 		return -1; | ||||
|  | ||||
| 	entry->offset = offset; | ||||
| 	entry->size = size; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Allocate memory for archive | ||||
|  * | ||||
|  * count: number of files to add | ||||
|  * files: pointer to the array of file names | ||||
|  * | ||||
|  * return: 0 on success or -1 on error | ||||
|  */ | ||||
| static int setup_archive(int count, const char **files) | ||||
| { | ||||
| 	uint32_t size; | ||||
| 	int i, s; | ||||
|  | ||||
| 	size = sizeof(*archive); | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		s = get_file_size(files[i]); | ||||
| 		if (s < 0) | ||||
| 			return -1; | ||||
| 		size += sizeof(struct dentry); | ||||
| 		size += s; | ||||
| 	} | ||||
|  | ||||
| 	archive = calloc(size, 1); | ||||
| 	if (!archive) { | ||||
| 		fprintf(stderr, "Error: failed to allocate memory\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* install magic string */ | ||||
| 	memcpy(archive->magic, CBAR_MAGIC, sizeof(archive->magic)); | ||||
| 	archive->version = VERSION; | ||||
| 	archive->size = size; | ||||
| 	archive->count = count; | ||||
|  | ||||
| 	printf("Set up archive: size=%d count=%d\n", size, count); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Store files in archive | ||||
|  */ | ||||
| static int archive_files(const char **files) | ||||
| { | ||||
| 	struct dentry *entry; | ||||
| 	uint32_t offset; | ||||
| 	int i; | ||||
|  | ||||
| 	entry = get_first_dentry(archive); | ||||
| 	offset = get_first_offset(archive); | ||||
| 	for (i = 0; i < archive->count; i++) { | ||||
| 		if (add_file(files[i], entry, offset)) | ||||
| 			return -1; | ||||
| 		offset += entry->size; | ||||
| 		entry++; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void convert_endian(void) | ||||
| { | ||||
| 	struct dentry *entry; | ||||
| 	int i; | ||||
|  | ||||
| 	entry = get_first_dentry(archive); | ||||
| 	for (i = 0; i < archive->count; i++) { | ||||
| 		entry[i].offset = htole32(entry[i].offset); | ||||
| 		entry[i].size = htole32(entry[i].size); | ||||
| 	} | ||||
|  | ||||
| 	archive->version = htole32(archive->version); | ||||
| 	archive->size = htole32(archive->size); | ||||
| 	archive->count = htole32(archive->count); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Write archive to file | ||||
|  */ | ||||
| static int output_archive(const char *path) | ||||
| { | ||||
| 	FILE *fp; | ||||
|  | ||||
| 	convert_endian(); | ||||
|  | ||||
| 	fp = fopen(path, "wb"); | ||||
| 	if (!fp) { | ||||
| 		fprintf(stderr, "Error: failed to open %s\n", path); | ||||
| 		fclose(fp); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (fwrite(archive, sizeof(char), archive->size, fp) != archive->size) { | ||||
| 		fprintf(stderr, "Error: failed to write to %s\n", path); | ||||
| 		fclose(fp); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	fclose(fp); | ||||
| 	printf("Wrote archive to %s\n", path); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int cmd_create(const char *archive_path, int count, const char **files) | ||||
| { | ||||
| 	if (count < 1 || !files) { | ||||
| 		fprintf(stderr, "Error: no input files specified\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (setup_archive(count, files)) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (archive_files(files)) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (output_archive(archive_path)) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, const char *argv[]) | ||||
| { | ||||
| 	const char *command; | ||||
|  | ||||
| 	if (argc < 3) { | ||||
| 		fprintf(stderr, "Error: invalid number of arguments\n"); | ||||
| 		usage(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	command = argv[2]; | ||||
|  | ||||
| 	/* branch by command name */ | ||||
| 	if (!strncmp(command, "create", sizeof("create"))) { | ||||
| 		if (cmd_create(argv[1], argc - 3, &argv[3])) | ||||
| 			return -1; | ||||
| 	} else { | ||||
| 		fprintf(stderr, "Error: invalid command: %s\n", command); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										76
									
								
								util/archive/archive.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								util/archive/archive.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 The ChromiumOS Authors.  All rights reserved. | ||||
|  *                 written by Daisuke Nojiri <dnojiri@chromium.org> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; version 2 of the License. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
|  | ||||
| #ifndef __ARCHIVE_H | ||||
| #define __ARCHIVE_H | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| /* | ||||
|  * Archive file layout: | ||||
|  * | ||||
|  *  +----------------------------------+ | ||||
|  *  |           root header            | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file_header[0]           | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file_header[1]           | | ||||
|  *  +----------------------------------+ | ||||
|  *  |              ...                 | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file_header[count-1]     | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file(0) content          | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file(1) content          | | ||||
|  *  +----------------------------------+ | ||||
|  *  |              ...                 | | ||||
|  *  +----------------------------------+ | ||||
|  *  |         file(count-1) content    | | ||||
|  *  +----------------------------------+ | ||||
|  */ | ||||
|  | ||||
| #define VERSION		0 | ||||
| #define CBAR_MAGIC	"CBAR" | ||||
| #define NAME_LENGTH	32 | ||||
|  | ||||
| /* Root header */ | ||||
| struct directory { | ||||
| 	char magic[4]; | ||||
| 	uint32_t version;	/* version of the header. little endian */ | ||||
| 	uint32_t size;		/* total size of archive. little endian */ | ||||
| 	uint32_t count;		/* number of files. little endian */ | ||||
| }; | ||||
|  | ||||
| /* File header */ | ||||
| struct dentry { | ||||
| 	/* file name. null-terminated if shorter than NAME_LENGTH */ | ||||
| 	char name[NAME_LENGTH]; | ||||
| 	/* file offset from the root header. little endian */ | ||||
| 	uint32_t offset; | ||||
| 	/* file size. little endian */ | ||||
| 	uint32_t size; | ||||
| }; | ||||
|  | ||||
| static inline struct dentry *get_first_dentry(const struct directory *dir) | ||||
| { | ||||
| 	return (struct dentry *)(dir + 1); | ||||
| } | ||||
|  | ||||
| static inline uint32_t get_first_offset(const struct directory *dir) | ||||
| { | ||||
| 	return sizeof(struct directory) + sizeof(struct dentry) * dir->count; | ||||
| } | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user