Okay, here we go. This patch is the meat and potatoes of the whole operation. This takes the code previous in create.c, extract.c, list.c and bootblock.c, and consolidates them into one file. We use the concept of a "stream" of sorts here - we create or open a lar, apply operations to it, and then close it when we are done. This allows us to mmap the LAR for speed and simplicity. This has the slight side effect of requiring that the user specify a size for the LAR when they create it, but I don't think thats really a bad thing.
One thing you might miss is that I've turned the path name of the bootblock into a constant name 'bootblock'. I did this for a number of reasons - mainly because its easier to partition off the "bootblock area", and keep the other blobs from infringing on it - but also because I can't grok an good reason to have arbitrary bootblock names.
BTW - There isn't anything in this patch precluding a 'lar_delete_files" function. If deleting things from a LAR is interesting, then it should be pretty easy to get that in here too.
Jordan
[PATCH][LAR] New LAR access functions
In preparation for adding new LAR functionality - this patch consolidates creating and accessing the LAR into new code utilizing mmap which facilitates moving about within the archive.
This code also turns the bootblock path name as a constant value. It also requires that the user specify a size when the LAR is created.
Signed-off-by: Jordan crouse jordan.crouse@amd.com Index: LinuxBIOSv3/util/lar/Makefile =================================================================== --- LinuxBIOSv3.orig/util/lar/Makefile 2007-07-11 11:40:37.000000000 -0600 +++ LinuxBIOSv3/util/lar/Makefile 2007-07-11 11:41:37.000000000 -0600 @@ -18,7 +18,7 @@ ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA ##
-LAROBJ := lar.o create.o extract.o list.o lib.o bootblock.o +LAROBJ := lar.o stream.o lib.o
LARDIR := lardir
Index: LinuxBIOSv3/util/lar/lar.c =================================================================== --- LinuxBIOSv3.orig/util/lar/lar.c 2007-07-11 11:40:29.000000000 -0600 +++ LinuxBIOSv3/util/lar/lar.c 2007-07-11 11:41:37.000000000 -0600 @@ -62,6 +62,64 @@ return bootblock; }
+int create_lar(const char *archivename, struct file *files) +{ + struct lar *lar = lar_new_archive(archivename, larsize); + + if (lar == NULL) { + fprintf(stderr, "Unable to create %s as a LAR archive.\n", + archivename); + exit(1); + } + + for( ; files; files = files->next) { + if (lar_add_file(lar, files->name)) { + fprintf(stderr, "Error adding %s to the LAR.\n", files->name); + lar_close_archive(lar); + exit(1); + } + } + + if (lar_add_bootblock(lar, bootblock)) { + fprintf(stderr, "Error adding the bootblock to the LAR.\n"); + lar_close_archive(lar); + exit(1); + } + + lar_close_archive(lar); + return 0; +} + +int list_lar(const char *archivename, struct file *files) +{ + struct lar *lar = lar_open_archive(archivename); + + if (lar == NULL) { + fprintf(stderr, "Unable to open LAR archive %s\n", archivename); + exit(1); + } + + lar_list_files(lar, files); + lar_close_archive(lar); + return 0; +} + +int extract_lar(const char *archivename, struct file *files) +{ + int ret; + + struct lar *lar = lar_open_archive(archivename); + + if (lar == NULL) { + fprintf(stderr, "Unable to open LAR archive %s\n", archivename); + exit(1); + } + + ret = lar_extract_files(lar, files); + lar_close_archive(lar); + return ret; +} + int main(int argc, char *argv[]) { int opt; @@ -173,16 +231,9 @@
/* adding a bootblock only makes sense when creating a lar */ if (!larsize) { - printf("Warning: When specifying a bootblock " - "you should also set an archive size.\n"); - } - - /* load the bootblock */ - if (larmode == CREATE) { - load_bootblock(bootblock); - fixup_bootblock(); + printf("When creating a LAR archive, you must specify an archive size.\n"); + exit(1); } - }
if (optind < argc) { Index: LinuxBIOSv3/util/lar/lar.h =================================================================== --- LinuxBIOSv3.orig/util/lar/lar.h 2007-07-11 11:40:29.000000000 -0600 +++ LinuxBIOSv3/util/lar/lar.h 2007-07-11 11:41:37.000000000 -0600 @@ -55,6 +55,9 @@ #define MAX_PATHLEN 1024 #define BOOTBLOCK_SIZE 16384
+#define BOOTBLOCK_NAME "bootblock" +#define BOOTBLOCK_NAME_LEN 16 + typedef uint32_t u32;
struct lar_header { @@ -72,6 +75,12 @@ u32 compression; };
+struct lar { + int fd; + unsigned char *map; + int size; +}; + enum compalgo { none = 0, lzma = 1, nrv2b = 2 };
typedef void (*compress_func) (char *, u32, char *, u32 *); Index: LinuxBIOSv3/util/lar/stream.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ LinuxBIOSv3/util/lar/stream.c 2007-07-11 11:48:23.000000000 -0600 @@ -0,0 +1,555 @@ +/* + * lar - LinuxBIOS archiver + * + * This includes code from previous versions of the LAR utility, + * including create.c, list.c, extract.c and bootblock.c + * + * Copyright (C) 2006-2007 coresystems GmbH + * (Written by Stefan Reinauer stepan@coresystems.de for coresystems GmbH) + * Copyright (C) 2007 Patrick Georgi patrick@georgi-clan.de + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <netinet/in.h> +#include <libgen.h> + +#include "lib.h" +#include "lar.h" + +#define err(fmt,args...) fprintf(stderr, fmt, ##args) + +extern enum compalgo algo; + +static inline int get_bootblock_offset(int size) +{ + return size - (BOOTBLOCK_SIZE + sizeof(struct lar_header) + BOOTBLOCK_NAME_LEN); +} + +static int lar_read_size(struct lar *lar, int filelen) +{ + unsigned int *ptr = (unsigned int *) (lar->map + (filelen - 12)); + return ptr[0]; +} + +static void annotate_bootblock(unsigned char *ptr, unsigned int size) +{ + int i; + unsigned int *p; + + for(i = 13; i > 0; i--) + ptr[BOOTBLOCK_SIZE - i] = '\0'; + + p = (unsigned int *) (ptr + BOOTBLOCK_SIZE - 12); + p[0] = size; +} + +int lar_add_bootblock(struct lar *lar, char *bootblock) +{ + unsigned char *offset; + struct lar_header *header; + int ret; + + if (bootblock != NULL) { + struct stat s; + + ret = stat(bootblock, &s); + + if (ret == -1) { + err("Unable to stat %s\n", bootblock); + return -1; + } + + if (s.st_size != BOOTBLOCK_SIZE) { + err("Bootblock %s does not appear to be a bootblock.\n", + bootblock); + + return -1; + } + } + + offset = lar->map + get_bootblock_offset(lar->size); + header = (struct lar_header *) offset; + + memcpy(header->magic, MAGIC, 8); + header->reallen = htonl(BOOTBLOCK_SIZE); + header->len = htonl(BOOTBLOCK_SIZE); + header->offset = htonl(sizeof(struct lar_header) + BOOTBLOCK_NAME_LEN); + + offset += sizeof(struct lar_header); + strcpy((char *) offset, BOOTBLOCK_NAME); + + offset += BOOTBLOCK_NAME_LEN; + + /* If a file waas specified, then load it, and read it directly into place */ + + if (bootblock != NULL) { + + int fd = open(bootblock, O_RDONLY); + + if (fd == -1) { + err("Unable to read bootblock file %s\n", bootblock); + return -1; + } + + ret = read(fd, offset, BOOTBLOCK_SIZE); + + close(fd); + + if (ret != BOOTBLOCK_SIZE) { + err("Unable to read all the bytes in the bootblock file.\n"); + return -1; + } + } + + annotate_bootblock(offset, lar->size); + return 0; +} + +static struct lar * _open_lar(const char *archive, unsigned int size, int flags) +{ + struct lar *lar = calloc(sizeof(*lar), 1); + + if (lar == NULL) { + err("Unable to allocate memory.\n"); + return NULL; + } + + lar->fd = open(archive, flags, S_IRUSR | S_IWUSR); + + if (lar->fd == -1) { + err("Couldn't open the archive %s\n", archive); + goto err; + } + + /* Expand the file to the correct size */ + + if (lseek(lar->fd, size - 1, SEEK_SET) == -1) { + err("Unable to write the archive %s\n", archive); + goto err; + } + + if (write(lar->fd, "", 1) != 1) { + err("Unable to write the file %s\n", archive); + goto err; + } + + lar->map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, + lar->fd, 0); + + lar->size = size; + + if (lar->map == MAP_FAILED) { + err("Unable to map the archive %s\n", archive); + goto err; + } + + return lar; + + err: + if (lar->fd >= 0) + close(lar->fd); + + unlink(archive); + + if (lar) + free(lar); + + return NULL; +} + +static void _close_lar(struct lar *lar) +{ + munmap(lar->map, lar->size); + close(lar->fd); + + free(lar); +} + +void lar_close_archive(struct lar *lar) +{ + _close_lar(lar); +} + +struct lar * lar_new_archive(const char *archive, unsigned int size) +{ + struct lar *lar; + int i; + + if (!access(archive, F_OK)) { + err("Archive file %s already exists\n", archive); + return NULL; + } + + lar = _open_lar(archive, size, O_RDWR | O_CREAT); + + if (lar == NULL) + return NULL; + + /* Fill the whole thing with flash friendly 0xFFs */ + memset(lar->map, 0xFF, lar->size); + + /* Make a dummy bootblock */ + + if (lar_add_bootblock(lar, NULL)) { + _close_lar(lar); + return NULL; + } + + return lar; +} + +struct lar * lar_open_archive(const char *archive) +{ + struct lar *lar; + int ret, romlen; + struct stat s; + + ret = stat(archive, &s); + + if (ret == -1) { + err("Unable to stat %s\n", archive); + return NULL; + } + + lar = _open_lar(archive, s.st_size, O_RDWR); + + /* Sanity check - make sure the bootblock header is the same length as the LAR archive */ + + romlen = lar_read_size(lar, s.st_size); + + if (romlen != s.st_size) { + err("Size mismatch - the header says %d but the file is %d bytes long.\n", + romlen, (int) s.st_size); + _close_lar(lar); + return NULL; + } + + return lar; +} + +/* return the offset of the empty space in the LAR */ + +static int lar_empty_offset(struct lar *lar) +{ + int offset = 0; + struct lar_header *header; + + while (offset < get_bootblock_offset(lar->size)) { + header = (struct lar_header *) (lar->map + offset); + + /* We interpet the absence of the magic as empty space */ + + if (strncmp(header->magic, MAGIC, 8)) + break; + + offset += (ntohl(header->len) + ntohl(header->offset) - 1) + & 0xfffffff0; + } + + if (offset >= get_bootblock_offset(lar->size)) + return -1; + + return offset; +} + +static int file_in_list(struct file *files, char *filename) +{ + struct file *p; + + if (files == NULL) + return 1; + + for(p = files ; p; p = p->next) { + if (!strcmp(p->name, filename)) + return 1; + } + + return 0; +} + +void lar_list_files(struct lar *lar, struct file *files) +{ + unsigned char *ptr = lar->map; + char *filename; + + struct lar_header *header; + struct file *fp; + + while (ptr < (lar->map + get_bootblock_offset(lar->size))) { + header = (struct lar_header *) ptr; + + /* We interpet the absence of the magic as empty space */ + + if (strncmp(header->magic, MAGIC, 8)) + break; + + filename = (char *) (ptr + sizeof(struct lar_header)); + + if (file_in_list(files, filename)) { + printf(" %s ", filename); + + if (ntohl(header->compression) == none) { + printf("(%d bytes @0x%lx)\n", + ntohl(header->len), + (unsigned long)(ptr - lar->map) + + ntohl(header->offset)); + } else { + printf("(%d bytes, %s compressed to %d bytes " + "@0x%lx)\n", + ntohl(header->reallen), + algo_name[ntohl(header->compression)], + ntohl(header->len), + (unsigned long)(ptr - lar->map) + + ntohl(header->offset)); + } + } + + ptr += (ntohl(header->len) + ntohl(header->offset) - 1) + & 0xfffffff0; + } + + /* Show the bootblock */ + + if (file_in_list(files, BOOTBLOCK_NAME)) { + header = (struct lar_header *) + (lar->map + get_bootblock_offset(lar->size)); + + printf(" bootblock (%d bytes @0x%x)\n", + ntohl(header->len), + get_bootblock_offset(lar->size) + + ntohl(header->offset)); + } +} + +static int _write_file(char *filename, unsigned char *buffer, int len) +{ + char *path = strdup(filename); + int fd, ret; + + if (path == NULL) { + err("Out of memory.\n"); + return -1; + } + + mkdirp((const char *) dirname(path), 0755); + free(path); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (fd == -1) { + err("Error creating file %s\n", filename); + return -1; + } + + ret = write(fd, buffer, len); + + if (ret != len) + err("Error writingthe file %s\n", filename); + + close(fd); + return (ret == len) ? 0 : -1; +} + +int lar_extract_files(struct lar *lar, struct file *files) +{ + unsigned char *ptr = lar->map; + char *filename; + struct lar_header *header; + int ret = 0; + + while (ptr < (lar->map + get_bootblock_offset(lar->size))) { + + header = (struct lar_header *) ptr; + + if (strncmp(header->magic, MAGIC, 8)) + break; + + filename = (char *) (ptr + sizeof(struct lar_header)); + + if (file_in_list(files, filename)) { + + if (ntohl(header->compression) == none) { + ret = _write_file(filename, + ptr + ntohl(header->offset), + ntohl(header->len)); + } + else { + unsigned char *buf = + malloc(ntohl(header->reallen)); + + if (buf == NULL) { + err("Out of memory.\n"); + return -1; + } + + uncompress_functions[ntohl(header->compression)]( + (char*) buf, + (char *) ptr + ntohl(header->offset), + ntohl(header->len)); + + ret = _write_file(filename, buf, + ntohl(header->reallen)); + + free(buf); + } + + if (ret == -1) + return -1; + } + + ptr += (ntohl(header->len) + ntohl(header->offset) - 1) + & 0xfffffff0; + } + + if (file_in_list(files, BOOTBLOCK_NAME)) { + header = (struct lar_header *) + (lar->map + get_bootblock_offset(lar->size)); + + ret = _write_file((char *) BOOTBLOCK_NAME, + lar->map + (get_bootblock_offset(lar->size) + ntohl(header->offset)), + BOOTBLOCK_SIZE); + } + + return ret; +} + +int lar_add_file(struct lar *lar, char *name) +{ + char *filename, *ptr, *temp; + enum compalgo thisalgo; + struct lar_header *header; + int offset, ret, fd, hlen; + u32 complen; + int pathlen; + struct stat s; + u32 *walk, csum; + + /* Find the beginning of the available space in the LAR */ + offset = lar_empty_offset(lar); + + thisalgo = algo; + + filename = name; + + if (strstr(name, "nocompress:") == name) { + filename += 11; + thisalgo = none; + } + + if (filename[0] == '.' && filename[1] == '/') + filename += 2; + + ret = stat (filename, &s); + + if (ret) { + err("Unable to stat %s\n", filename); + return -1; + } + + /* Allocate a temporary buffer to compress into - this is unavoidable, + because we need to make sure that the compressed data will fit in + the LAR, and we won't know the size of the compressed data until + we actually compress it */ + + temp = calloc(s.st_size, 1); + + if (temp == NULL) { + err("Out of memory.\n"); + return -1; + } + + /* Open the file */ + fd = open(filename, O_RDONLY); + + if (fd == -1) { + err("Unable to open %s\n", filename); + free(temp); + return -1; + } + + ptr = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + + if (ptr == MAP_FAILED) { + err("Unable to map %s\n", filename); + close(fd); + free(temp); + return -1; + } + + + /* Do the compression step */ + compress_functions[thisalgo](ptr, s.st_size, temp, &complen); + + if (complen >= s.st_size && (thisalgo != none)) { + thisalgo = none; + compress_functions[thisalgo](ptr, s.st_size, temp, &complen); + } + + munmap(ptr, s.st_size); + close(fd); + + pathlen = strlen(filename) + 1 > MAX_PATHLEN ? MAX_PATHLEN : strlen(filename) + 1; + + /* Figure out how big our header will be */ + hlen = sizeof(struct lar_header) + pathlen; + hlen = (hlen + 15) & 0xFFFFFFF0; + + if (offset + hlen + complen >= get_bootblock_offset(lar->size)) { + err("Not enough room in the LAR to add the file.\n"); + free(temp); + return -1; + } + + /* Lets do this thing */ + + /* Zero out the header area */ + memset(lar->map + offset, 0, hlen); + + header = (struct lar_header *) (lar->map + offset); + + memcpy(header, MAGIC, 8); + header->compression = htonl(thisalgo); + header->reallen = htonl(s.st_size); + header->len = htonl(complen); + header->offset = htonl(hlen); + + /* Copy the path name */ + strncpy((char *) (lar->map + offset + sizeof(struct lar_header)), + filename, pathlen - 1); + + /* Copy in the data */ + memcpy(lar->map + (offset + hlen), temp, complen); + + /* Figure out the checksum */ + + csum = 0; + for (walk = (u32 *) (lar->map + offset); + walk < (u32 *) (temp + complen + hlen); + walk++) { + csum += ntohl(*walk); + } + header->checksum = htonl(csum); + + free(temp); + return 0; +}