From a698dc48c5d3a01515127b131e9506dda6c74137 Mon Sep 17 00:00:00 2001 From: Jason Stephenson Date: Mon, 18 Mar 2013 20:03:42 -0400 Subject: [PATCH] Add libmar to build update tools to the repo. This adds code from Mozilla to build a command line mar program and the libmar library that it uses. This code is now dual-licensed under the MPL 2.0 and GPL v2 or later. We also add our own version of mbsdiff using a modified version of Colin Percival's FreeBSD-licensed bsdiff program. We also copy the crc32 code from Mozilla's updater. Add libmar as "subpackage" in cofigure.ac. Add AC_CONFIG_SUBDIRS macro call for Open-ILS/xul/staff_client/external/ libmar so that our update tools will get configured and built when Evergreen is built. Modify make_updates.sh for our update tools. Instead of downloading mar and mbsdiff from Mozilla.org's servers, we check if they exist in Open-ILS/xul/staff_client/external/libmar/tool and if not we build them. They should be there already. Signed-off-by: Jason Stephenson Signed-off-by: Ben Shum --- .../staff_client/external/libmar/Makefile.am | 1 + .../xul/staff_client/external/libmar/README | 11 + .../staff_client/external/libmar/configure.ac | 49 ++ .../external/libmar/src/Makefile.am | 2 + .../staff_client/external/libmar/src/mar.h | 169 +++++ .../external/libmar/src/mar_cmdline.h | 94 +++ .../external/libmar/src/mar_create.c | 416 +++++++++++++ .../external/libmar/src/mar_extract.c | 102 +++ .../external/libmar/src/mar_private.h | 88 +++ .../external/libmar/src/mar_read.c | 581 ++++++++++++++++++ .../external/libmar/tool/Makefile.am | 5 + .../external/libmar/tool/bsdiff.c | 393 ++++++++++++ .../staff_client/external/libmar/tool/crc32.c | 22 + .../staff_client/external/libmar/tool/mar.c | 179 ++++++ .../xul/staff_client/external/make_updates.sh | 11 +- configure.ac | 1 + 16 files changed, 2119 insertions(+), 5 deletions(-) create mode 100644 Open-ILS/xul/staff_client/external/libmar/Makefile.am create mode 100644 Open-ILS/xul/staff_client/external/libmar/README create mode 100644 Open-ILS/xul/staff_client/external/libmar/configure.ac create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/Makefile.am create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar.h create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar_cmdline.h create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar_create.c create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar_extract.c create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar_private.h create mode 100644 Open-ILS/xul/staff_client/external/libmar/src/mar_read.c create mode 100644 Open-ILS/xul/staff_client/external/libmar/tool/Makefile.am create mode 100644 Open-ILS/xul/staff_client/external/libmar/tool/bsdiff.c create mode 100644 Open-ILS/xul/staff_client/external/libmar/tool/crc32.c create mode 100644 Open-ILS/xul/staff_client/external/libmar/tool/mar.c diff --git a/Open-ILS/xul/staff_client/external/libmar/Makefile.am b/Open-ILS/xul/staff_client/external/libmar/Makefile.am new file mode 100644 index 0000000000..518638b566 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src tool diff --git a/Open-ILS/xul/staff_client/external/libmar/README b/Open-ILS/xul/staff_client/external/libmar/README new file mode 100644 index 0000000000..7513ded3c1 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/README @@ -0,0 +1,11 @@ +This directory contains code for a simple archive file format, which +is documented at http://wiki.mozilla.org/Software_Update:MAR + +The src directory builds a small static library used to create, read, +and extract an archive file. The tool directory builds a command line +utility around the library, mar. + +The tool directory also builds a modified version of bsdiff used by +the Mozilla update mechanism to generate patches. This version was +modified from a recent copy of Colin Percival's, FreeBSD-licensed +bsdiff sources with the crc32 code taken from the Mozilla updater. diff --git a/Open-ILS/xul/staff_client/external/libmar/configure.ac b/Open-ILS/xul/staff_client/external/libmar/configure.ac new file mode 100644 index 0000000000..e7715cd49a --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/configure.ac @@ -0,0 +1,49 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.68]) +AC_INIT([libmar], [1.0], [jason@sigio.com]) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_SRCDIR([tool/mar.c]) +AC_CONFIG_HEADER([config.h]) + +# Check for MAR_CHANNEL_ID: +AC_ARG_WITH([marchannelid], +[ --with-marchannelid=channelname default name for MAR channel (default is blank)], +[mar_channel_id=${withval}], +[mar_channel_id=]) +AC_DEFINE_UNQUOTED([MAR_CHANNEL_ID], ["${mar_channel_id}"], [Default MAR_CHANNEL_ID]) + +# Check for MOZ_APP_VERSION +AC_ARG_WITH([mozappversion], +[ --with-mozappversion=appversion default version for app (default is 19.0.1)], +[moz_app_version=${withval}], +[moz_app_version="19.0.1"]) +AC_DEFINE_UNQUOTED([MOZ_APP_VERSION], ["${moz_app_version}"], [Default MOZ_APP_VERSION]) + + +# Checks for programs. +AC_PROG_CC +AC_PROG_RANLIB + +# Checks for libraries. + +# Checks for header files. +AC_CHECK_HEADERS([err.h fcntl.h netinet/in.h stdint.h stdio.h stdlib.h string.h sys/stat.h sys/types.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_INT64_T +AC_TYPE_SIZE_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T + +# Checks for library functions. +AC_FUNC_FSEEKO +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([memset mkdir strdup strrchr]) + +AC_CONFIG_FILES([Makefile + src/Makefile + tool/Makefile]) +AC_OUTPUT diff --git a/Open-ILS/xul/staff_client/external/libmar/src/Makefile.am b/Open-ILS/xul/staff_client/external/libmar/src/Makefile.am new file mode 100644 index 0000000000..c586505bcd --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/Makefile.am @@ -0,0 +1,2 @@ +noinst_LIBRARIES = libmar.a +libmar_a_SOURCES = mar_cmdline.h mar.h mar_private.h mar_create.c mar_extract.c mar_read.c diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar.h b/Open-ILS/xul/staff_client/external/libmar/src/mar.h new file mode 100644 index 0000000000..8f4cb613da --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar.h @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef MAR_H__ +#define MAR_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* We have a MAX_SIGNATURES limit so that an invalid MAR will never + * waste too much of either updater's or signmar's time. + * It is also used at various places internally and will affect memory usage. + * If you want to increase this value above 9 then you need to adjust parsing + * code in tool/mar.c. +*/ +#define MAX_SIGNATURES 8 + +struct ProductInformationBlock { + const char *MARChannelID; + const char *productVersion; +}; + +/** + * The MAR item data structure. + */ +typedef struct MarItem_ { + struct MarItem_ *next; /* private field */ + uint32_t offset; /* offset into archive */ + uint32_t length; /* length of data in bytes */ + uint32_t flags; /* contains file mode bits */ + char name[1]; /* file path */ +} MarItem; + +#define TABLESIZE 256 + +struct MarFile_ { + FILE *fp; + MarItem *item_table[TABLESIZE]; +}; + +typedef struct MarFile_ MarFile; + +/** + * Signature of callback function passed to mar_enum_items. + * @param mar The MAR file being visited. + * @param item The MAR item being visited. + * @param data The data parameter passed by the caller of mar_enum_items. + * @return A non-zero value to stop enumerating. + */ +typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data); + +/** + * Open a MAR file for reading. + * @param path Specifies the path to the MAR file to open. This path must + * be compatible with fopen. + * @return NULL if an error occurs. + */ +MarFile *mar_open(const char *path); + +#ifdef XP_WIN +MarFile *mar_wopen(const PRUnichar *path); +#endif + +/** + * Close a MAR file that was opened using mar_open. + * @param mar The MarFile object to close. + */ +void mar_close(MarFile *mar); + +/** + * Find an item in the MAR file by name. + * @param mar The MarFile object to query. + * @param item The name of the item to query. + * @return A const reference to a MAR item or NULL if not found. + */ +const MarItem *mar_find_item(MarFile *mar, const char *item); + +/** + * Enumerate all MAR items via callback function. + * @param mar The MAR file to enumerate. + * @param callback The function to call for each MAR item. + * @param data A caller specified value that is passed along to the + * callback function. + * @return 0 if the enumeration ran to completion. Otherwise, any + * non-zero return value from the callback is returned. + */ +int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data); + +/** + * Read from MAR item at given offset up to bufsize bytes. + * @param mar The MAR file to read. + * @param item The MAR item to read. + * @param offset The byte offset relative to the start of the item. + * @param buf A pointer to a buffer to copy the data into. + * @param bufsize The length of the buffer to copy the data into. + * @return The number of bytes written or a negative value if an + * error occurs. + */ +int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf, + int bufsize); + +/** + * Create a MAR file from a set of files. + * @param dest The path to the file to create. This path must be + * compatible with fopen. + * @param numfiles The number of files to store in the archive. + * @param files The list of null-terminated file paths. Each file + * path must be compatible with fopen. + * @param infoBlock The information to store in the product information block. + * @return A non-zero value if an error occurs. + */ +int mar_create(const char *dest, + int numfiles, + char **files, + struct ProductInformationBlock *infoBlock); + +/** + * Extract a MAR file to the current working directory. + * @param path The path to the MAR file to extract. This path must be + * compatible with fopen. + * @return A non-zero value if an error occurs. + */ +int mar_extract(const char *path); + +/** + * Reads the product info block from the MAR file's additional block section. + * The caller is responsible for freeing the fields in infoBlock + * if the return is successful. + * + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +mar_read_product_info_block(MarFile *mar, + struct ProductInformationBlock *infoBlock); + +#ifdef __cplusplus +} +#endif + +#endif /* MAR_H__ */ diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar_cmdline.h b/Open-ILS/xul/staff_client/external/libmar/src/mar_cmdline.h new file mode 100644 index 0000000000..3f7914891f --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar_cmdline.h @@ -0,0 +1,94 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef MAR_CMDLINE_H__ +#define MAR_CMDLINE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ProductInformationBlock; + +/** + * Determines MAR file information. + * + * @param path The path of the MAR file to check. + * @param hasSignatureBlock Optional out parameter specifying if the MAR + * file has a signature block or not. + * @param numSignatures Optional out parameter for storing the number + * of signatures in the MAR file. + * @param hasAdditionalBlocks Optional out parameter specifying if the MAR + * file has additional blocks or not. + * @param offsetAdditionalBlocks Optional out parameter for the offset to the + * first additional block. Value is only valid if + * hasAdditionalBlocks is not equal to 0. + * @param numAdditionalBlocks Optional out parameter for the number of + * additional blocks. Value is only valid if + * has_additional_blocks is not equal to 0. + * @return 0 on success and non-zero on failure. + */ +int get_mar_file_info(const char *path, + int *hasSignatureBlock, + uint32_t *numSignatures, + int *hasAdditionalBlocks, + uint32_t *offsetAdditionalBlocks, + uint32_t *numAdditionalBlocks); + +/** + * Reads the product info block from the MAR file's additional block section. + * The caller is responsible for freeing the fields in infoBlock + * if the return is successful. + * + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +read_product_info_block(char *path, + struct ProductInformationBlock *infoBlock); + +/** + * Refreshes the product information block with the new information. + * The input MAR must not be signed or the function call will fail. + * + * @param path The path to the MAR file whose product info block + * should be refreshed. + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +refresh_product_info_block(const char *path, + struct ProductInformationBlock *infoBlock); + +#ifdef __cplusplus +} +#endif + +#endif /* MAR_CMDLINE_H__ */ diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar_create.c b/Open-ILS/xul/staff_client/external/libmar/src/mar_create.c new file mode 100644 index 0000000000..d540c370cf --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar_create.c @@ -0,0 +1,416 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include "mar_private.h" +#include "mar_cmdline.h" +#include "mar.h" + +#ifdef XP_WIN +#include +#else +#include +#include +#endif + +struct MarItemStack { + void *head; + uint32_t size_used; + uint32_t size_allocated; + uint32_t last_offset; +}; + +/** + * Push a new item onto the stack of items. The stack is a single block + * of memory. + */ +static int mar_push(struct MarItemStack *stack, uint32_t length, uint32_t flags, + const char *name) { + int namelen; + uint32_t n_offset, n_length, n_flags; + uint32_t size; + char *data; + + namelen = strlen(name); + size = MAR_ITEM_SIZE(namelen); + + if (stack->size_allocated - stack->size_used < size) { + /* increase size of stack */ + size_t size_needed = ROUND_UP(stack->size_used + size, BLOCKSIZE); + stack->head = realloc(stack->head, size_needed); + if (!stack->head) + return -1; + stack->size_allocated = size_needed; + } + + data = (((char *) stack->head) + stack->size_used); + + n_offset = htonl(stack->last_offset); + n_length = htonl(length); + n_flags = htonl(flags); + + memcpy(data, &n_offset, sizeof(n_offset)); + data += sizeof(n_offset); + + memcpy(data, &n_length, sizeof(n_length)); + data += sizeof(n_length); + + memcpy(data, &n_flags, sizeof(n_flags)); + data += sizeof(n_flags); + + memcpy(data, name, namelen + 1); + + stack->size_used += size; + stack->last_offset += length; + return 0; +} + +static int mar_concat_file(FILE *fp, const char *path) { + FILE *in; + char buf[BLOCKSIZE]; + size_t len; + int rv = 0; + + in = fopen(path, "rb"); + if (!in) + return -1; + + while ((len = fread(buf, 1, BLOCKSIZE, in)) > 0) { + if (fwrite(buf, len, 1, fp) != 1) { + rv = -1; + break; + } + } + + fclose(in); + return rv; +} + +/** + * Writes out the product information block to the specified file. + * + * @param fp The opened MAR file being created. + * @param stack A pointer to the MAR item stack being used to create + * the MAR + * @param infoBlock The product info block to store in the file. + * @return 0 on success. +*/ +static int +mar_concat_product_info_block(FILE *fp, + struct MarItemStack *stack, + struct ProductInformationBlock *infoBlock) +{ + char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE]; + uint32_t additionalBlockID = 1, infoBlockSize, unused; + if (!fp || !infoBlock || + !infoBlock->MARChannelID || + !infoBlock->productVersion) { + return -1; + } + + /* The MAR channel name must be < 64 bytes per the spec */ + if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) { + return -1; + } + + /* The product version must be < 32 bytes per the spec */ + if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) { + return -1; + } + + /* Although we don't need the product information block size to include the + maximum MAR channel name and product version, we allocate the maximum + amount to make it easier to modify the MAR file for repurposing MAR files + to different MAR channels. + 2 is for the NULL terminators. */ + infoBlockSize = sizeof(infoBlockSize) + + sizeof(additionalBlockID) + + PIB_MAX_MAR_CHANNEL_ID_SIZE + + PIB_MAX_PRODUCT_VERSION_SIZE + 2; + if (stack) { + stack->last_offset += infoBlockSize; + } + + /* Write out the product info block size */ + infoBlockSize = htonl(infoBlockSize); + if (fwrite(&infoBlockSize, + sizeof(infoBlockSize), 1, fp) != 1) { + return -1; + } + infoBlockSize = ntohl(infoBlockSize); + + /* Write out the product info block ID */ + additionalBlockID = htonl(additionalBlockID); + if (fwrite(&additionalBlockID, + sizeof(additionalBlockID), 1, fp) != 1) { + return -1; + } + additionalBlockID = ntohl(additionalBlockID); + + /* Write out the channel name and NULL terminator */ + if (fwrite(infoBlock->MARChannelID, + strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) { + return -1; + } + + /* Write out the product version string and NULL terminator */ + if (fwrite(infoBlock->productVersion, + strlen(infoBlock->productVersion) + 1, 1, fp) != 1) { + return -1; + } + + /* Write out the rest of the block that is unused */ + unused = infoBlockSize - (sizeof(infoBlockSize) + + sizeof(additionalBlockID) + + strlen(infoBlock->MARChannelID) + + strlen(infoBlock->productVersion) + 2); + memset(buf, 0, sizeof(buf)); + if (fwrite(buf, unused, 1, fp) != 1) { + return -1; + } + return 0; +} + +/** + * Refreshes the product information block with the new information. + * The input MAR must not be signed or the function call will fail. + * + * @param path The path to the MAR file whose product info block + * should be refreshed. + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +refresh_product_info_block(const char *path, + struct ProductInformationBlock *infoBlock) +{ + FILE *fp ; + int rv; + uint32_t numSignatures, additionalBlockSize, additionalBlockID, + offsetAdditionalBlocks, numAdditionalBlocks, i; + int additionalBlocks, hasSignatureBlock; + int64_t oldPos; + + rv = get_mar_file_info(path, + &hasSignatureBlock, + &numSignatures, + &additionalBlocks, + &offsetAdditionalBlocks, + &numAdditionalBlocks); + if (rv) { + fprintf(stderr, "ERROR: Could not obtain MAR information.\n"); + return -1; + } + + if (hasSignatureBlock && numSignatures) { + fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n"); + return -1; + } + + fp = fopen(path, "r+b"); + if (!fp) { + fprintf(stderr, "ERROR: could not open target file: %s\n", path); + return -1; + } + + if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) { + fprintf(stderr, "ERROR: could not seek to additional blocks\n"); + fclose(fp); + return -1; + } + + for (i = 0; i < numAdditionalBlocks; ++i) { + /* Get the position of the start of this block */ + oldPos = ftello(fp); + + /* Read the additional block size */ + if (fread(&additionalBlockSize, + sizeof(additionalBlockSize), + 1, fp) != 1) { + return -1; + } + additionalBlockSize = ntohl(additionalBlockSize); + + /* Read the additional block ID */ + if (fread(&additionalBlockID, + sizeof(additionalBlockID), + 1, fp) != 1) { + return -1; + } + additionalBlockID = ntohl(additionalBlockID); + + if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) { + if (fseeko(fp, oldPos, SEEK_SET)) { + fprintf(stderr, "Could not seek back to Product Information Block\n"); + fclose(fp); + return -1; + } + + if (mar_concat_product_info_block(fp, NULL, infoBlock)) { + fprintf(stderr, "Could not concat Product Information Block\n"); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; + } else { + /* This is not the additional block you're looking for. Move along. */ + if (fseek(fp, additionalBlockSize, SEEK_CUR)) { + fprintf(stderr, "ERROR: Could not seek past current block.\n"); + fclose(fp); + return -1; + } + } + } + + /* If we had a product info block we would have already returned */ + fclose(fp); + fprintf(stderr, "ERROR: Could not refresh because block does not exist\n"); + return -1; +} + +/** + * Create a MAR file from a set of files. + * @param dest The path to the file to create. This path must be + * compatible with fopen. + * @param numfiles The number of files to store in the archive. + * @param files The list of null-terminated file paths. Each file + * path must be compatible with fopen. + * @param infoBlock The information to store in the product information block. + * @return A non-zero value if an error occurs. + */ +int mar_create(const char *dest, int + num_files, char **files, + struct ProductInformationBlock *infoBlock) { + struct MarItemStack stack; + uint32_t offset_to_index = 0, size_of_index, + numSignatures, numAdditionalSections; + uint64_t sizeOfEntireMAR = 0; + struct stat st; + FILE *fp; + int i, rv = -1; + + memset(&stack, 0, sizeof(stack)); + + fp = fopen(dest, "wb"); + if (!fp) { + fprintf(stderr, "ERROR: could not create target file: %s\n", dest); + return -1; + } + + if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1) + goto failure; + if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1) + goto failure; + + stack.last_offset = MAR_ID_SIZE + + sizeof(offset_to_index) + + sizeof(numSignatures) + + sizeof(numAdditionalSections) + + sizeof(sizeOfEntireMAR); + + /* We will circle back on this at the end of the MAR creation to fill it */ + if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) { + goto failure; + } + + /* Write out the number of signatures, for now only at most 1 is supported */ + numSignatures = 0; + if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) { + goto failure; + } + + /* Write out the number of additional sections, for now just 1 + for the product info block */ + numAdditionalSections = htonl(1); + if (fwrite(&numAdditionalSections, + sizeof(numAdditionalSections), 1, fp) != 1) { + goto failure; + } + numAdditionalSections = ntohl(numAdditionalSections); + + if (mar_concat_product_info_block(fp, &stack, infoBlock)) { + goto failure; + } + + for (i = 0; i < num_files; ++i) { + if (stat(files[i], &st)) { + fprintf(stderr, "ERROR: file not found: %s\n", files[i]); + goto failure; + } + + if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i])) + goto failure; + + /* concatenate input file to archive */ + if (mar_concat_file(fp, files[i])) + goto failure; + } + + /* write out the index (prefixed with length of index) */ + size_of_index = htonl(stack.size_used); + if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1) + goto failure; + if (fwrite(stack.head, stack.size_used, 1, fp) != 1) + goto failure; + + /* To protect against invalid MAR files, we assumes that the MAR file + size is less than or equal to MAX_SIZE_OF_MAR_FILE. */ + if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) { + goto failure; + } + + /* write out offset to index file in network byte order */ + offset_to_index = htonl(stack.last_offset); + if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) + goto failure; + if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1) + goto failure; + offset_to_index = ntohl(stack.last_offset); + + sizeOfEntireMAR = ((uint64_t)stack.last_offset) + + stack.size_used + + sizeof(size_of_index); + sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); + if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) + goto failure; + sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); + + rv = 0; +failure: + if (stack.head) + free(stack.head); + fclose(fp); + if (rv) + remove(dest); + return rv; +} diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar_extract.c b/Open-ILS/xul/staff_client/external/libmar/src/mar_extract.c new file mode 100644 index 0000000000..404a5355f5 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar_extract.c @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include "mar_private.h" +#include "mar.h" + +#ifdef XP_WIN +#include +#include +#endif + +/* Ensure that the directory containing this file exists */ +static int mar_ensure_parent_dir(const char *path) +{ + char *slash = strrchr(path, '/'); + if (slash) + { + *slash = '\0'; + mar_ensure_parent_dir(path); +#ifdef XP_WIN + _mkdir(path); +#else + mkdir(path, 0755); +#endif + *slash = '/'; + } + return 0; +} + +static int mar_test_callback(MarFile *mar, const MarItem *item, void *unused) { + FILE *fp; + char buf[BLOCKSIZE]; + int fd, len, offset = 0; + + if (mar_ensure_parent_dir(item->name)) + return -1; + +#ifdef XP_WIN + fd = _open(item->name, _O_BINARY|_O_CREAT|_O_TRUNC|_O_WRONLY, item->flags); +#else + fd = creat(item->name, item->flags); +#endif + if (fd == -1) + return -1; + + fp = fdopen(fd, "wb"); + if (!fp) + return -1; + + while ((len = mar_read(mar, item, offset, buf, sizeof(buf))) > 0) { + if (fwrite(buf, len, 1, fp) != 1) + break; + offset += len; + } + + fclose(fp); + return len == 0 ? 0 : -1; +} + +int mar_extract(const char *path) { + MarFile *mar; + int rv; + + mar = mar_open(path); + if (!mar) + return -1; + + rv = mar_enum_items(mar, mar_test_callback, NULL); + + mar_close(mar); + return rv; +} diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar_private.h b/Open-ILS/xul/staff_client/external/libmar/src/mar_private.h new file mode 100644 index 0000000000..adcd25101a --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar_private.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef MAR_PRIVATE_H__ +#define MAR_PRIVATE_H__ + +#include + +#define BLOCKSIZE 4096 +#define ROUND_UP(n, incr) (((n) / (incr) + 1) * (incr)) + +#define MAR_ID "MAR1" +#define MAR_ID_SIZE 4 + +/* The signature block comes directly after the header block + which is 16 bytes */ +#define SIGNATURE_BLOCK_OFFSET 16 + +/* Make sure the file is less than 500MB. We do this to protect against + invalid MAR files. */ +#define MAX_SIZE_OF_MAR_FILE ((int64_t)524288000) + +/* The maximum size of any signature supported by current and future + implementations of the signmar program. */ +#define MAX_SIGNATURE_LENGTH 2048 + +/* Each additional block has a unique ID. + The product information block has an ID of 1. */ +#define PRODUCT_INFO_BLOCK_ID 1 + +#define MAR_ITEM_SIZE(namelen) (3*sizeof(uint32_t) + (namelen) + 1) + +/* Product Information Block (PIB) constants */ +#define PIB_MAX_MAR_CHANNEL_ID_SIZE 63 +#define PIB_MAX_PRODUCT_VERSION_SIZE 31 + +/* The mar program is compiled as a host bin so we don't have access to NSPR at + runtime. For that reason we use ntohl, htonl, and define HOST_TO_NETWORK64 + instead of the NSPR equivalents. */ +#ifdef XP_WIN +#include +#define ftello _ftelli64 +#define fseeko _fseeki64 +#else +#define _FILE_OFFSET_BITS 64 +#include +#include +#endif + +#include + +#define HOST_TO_NETWORK64(x) ( \ + ((((uint64_t) x) & 0xFF) << 56) | \ + ((((uint64_t) x) >> 8) & 0xFF) << 48) | \ + (((((uint64_t) x) >> 16) & 0xFF) << 40) | \ + (((((uint64_t) x) >> 24) & 0xFF) << 32) | \ + (((((uint64_t) x) >> 32) & 0xFF) << 24) | \ + (((((uint64_t) x) >> 40) & 0xFF) << 16) | \ + (((((uint64_t) x) >> 48) & 0xFF) << 8) | \ + (((uint64_t) x) >> 56) +#define NETWORK_TO_HOST64 HOST_TO_NETWORK64 + +#endif /* MAR_PRIVATE_H__ */ diff --git a/Open-ILS/xul/staff_client/external/libmar/src/mar_read.c b/Open-ILS/xul/staff_client/external/libmar/src/mar_read.c new file mode 100644 index 0000000000..8c14fd0658 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/src/mar_read.c @@ -0,0 +1,581 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include "mar_private.h" +#include "mar.h" + +#ifdef XP_WIN +#include +#else +#include +#endif + + +/* this is the same hash algorithm used by nsZipArchive.cpp */ +static uint32_t mar_hash_name(const char *name) { + uint32_t val = 0; + unsigned char* c; + + for (c = (unsigned char *) name; *c; ++c) + val = val*37 + *c; + + return val % TABLESIZE; +} + +static int mar_insert_item(MarFile *mar, const char *name, int namelen, + uint32_t offset, uint32_t length, uint32_t flags) { + MarItem *item, *root; + uint32_t hash; + + item = (MarItem *) malloc(sizeof(MarItem) + namelen); + if (!item) + return -1; + item->next = NULL; + item->offset = offset; + item->length = length; + item->flags = flags; + memcpy(item->name, name, namelen + 1); + + hash = mar_hash_name(name); + + root = mar->item_table[hash]; + if (!root) { + mar->item_table[hash] = item; + } else { + /* append item */ + while (root->next) + root = root->next; + root->next = item; + } + return 0; +} + +static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) { + /* + * Each item has the following structure: + * uint32_t offset (network byte order) + * uint32_t length (network byte order) + * uint32_t flags (network byte order) + * char name[N] (where N >= 1) + * char null_byte; + */ + uint32_t offset; + uint32_t length; + uint32_t flags; + const char *name; + int namelen; + + if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2)) + return -1; + + memcpy(&offset, *buf, sizeof(offset)); + *buf += sizeof(offset); + + memcpy(&length, *buf, sizeof(length)); + *buf += sizeof(length); + + memcpy(&flags, *buf, sizeof(flags)); + *buf += sizeof(flags); + + offset = ntohl(offset); + length = ntohl(length); + flags = ntohl(flags); + + name = *buf; + /* find namelen; must take care not to read beyond buf_end */ + while (**buf) { + if (*buf == buf_end) + return -1; + ++(*buf); + } + namelen = (*buf - name); + /* consume null byte */ + if (*buf == buf_end) + return -1; + ++(*buf); + + return mar_insert_item(mar, name, namelen, offset, length, flags); +} + +static int mar_read_index(MarFile *mar) { + char id[MAR_ID_SIZE], *buf, *bufptr, *bufend; + uint32_t offset_to_index, size_of_index; + + /* verify MAR ID */ + if (fread(id, MAR_ID_SIZE, 1, mar->fp) != 1) + return -1; + if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0) + return -1; + + if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1) + return -1; + offset_to_index = ntohl(offset_to_index); + + if (fseek(mar->fp, offset_to_index, SEEK_SET)) + return -1; + if (fread(&size_of_index, sizeof(uint32_t), 1, mar->fp) != 1) + return -1; + size_of_index = ntohl(size_of_index); + + buf = (char *) malloc(size_of_index); + if (!buf) + return -1; + if (fread(buf, size_of_index, 1, mar->fp) != 1) { + free(buf); + return -1; + } + + bufptr = buf; + bufend = buf + size_of_index; + while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0); + + free(buf); + return (bufptr == bufend) ? 0 : -1; +} + +/** + * Internal shared code for mar_open and mar_wopen. + * On failure, will fclose(fp). + */ +static MarFile *mar_fpopen(FILE *fp) +{ + MarFile *mar; + + mar = (MarFile *) malloc(sizeof(*mar)); + if (!mar) { + fclose(fp); + return NULL; + } + + mar->fp = fp; + memset(mar->item_table, 0, sizeof(mar->item_table)); + if (mar_read_index(mar)) { + mar_close(mar); + return NULL; + } + + return mar; +} + +MarFile *mar_open(const char *path) { + FILE *fp; + + fp = fopen(path, "rb"); + if (!fp) + return NULL; + + return mar_fpopen(fp); +} + +#ifdef XP_WIN +MarFile *mar_wopen(const PRUnichar *path) { + FILE *fp; + + fp = _wfopen(path, L"rb"); + if (!fp) + return NULL; + + return mar_fpopen(fp); +} +#endif + +void mar_close(MarFile *mar) { + MarItem *item; + int i; + + fclose(mar->fp); + + for (i = 0; i < TABLESIZE; ++i) { + item = mar->item_table[i]; + while (item) { + MarItem *temp = item; + item = item->next; + free(temp); + } + } + + free(mar); +} + +/** + * Determines the MAR file information. + * + * @param fp An opened MAR file in read mode. + * @param hasSignatureBlock Optional out parameter specifying if the MAR + * file has a signature block or not. + * @param numSignatures Optional out parameter for storing the number + * of signatures in the MAR file. + * @param hasAdditionalBlocks Optional out parameter specifying if the MAR + * file has additional blocks or not. + * @param offsetAdditionalBlocks Optional out parameter for the offset to the + * first additional block. Value is only valid if + * hasAdditionalBlocks is not equal to 0. + * @param numAdditionalBlocks Optional out parameter for the number of + * additional blocks. Value is only valid if + * hasAdditionalBlocks is not equal to 0. + * @return 0 on success and non-zero on failure. + */ +int get_mar_file_info_fp(FILE *fp, + int *hasSignatureBlock, + int *numSignatures, + int *hasAdditionalBlocks, + int *offsetAdditionalBlocks, + int *numAdditionalBlocks) +{ + uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i; + + /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */ + if (!hasSignatureBlock && !hasAdditionalBlocks) { + return -1; + } + + + /* Skip to the start of the offset index */ + if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) { + return -1; + } + + /* Read the offset to the index. */ + if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) { + return -1; + } + offsetToIndex = ntohl(offsetToIndex); + + if (numSignatures) { + /* Skip past the MAR file size field */ + if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) { + return -1; + } + + /* Read the number of signatures field */ + if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) { + return -1; + } + *numSignatures = ntohl(*numSignatures); + } + + /* Skip to the first index entry past the index size field + We do it in 2 calls because offsetToIndex + sizeof(uint32_t) + could oerflow in theory. */ + if (fseek(fp, offsetToIndex, SEEK_SET)) { + return -1; + } + + if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) { + return -1; + } + + /* Read the first offset to content field. */ + if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) { + return -1; + } + offsetToContent = ntohl(offsetToContent); + + /* Check if we have a new or old MAR file */ + if (hasSignatureBlock) { + if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) { + *hasSignatureBlock = 0; + } else { + *hasSignatureBlock = 1; + } + } + + /* If the caller doesn't care about the product info block + value, then just return */ + if (!hasAdditionalBlocks) { + return 0; + } + + /* Skip to the start of the signature block */ + if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) { + return -1; + } + + /* Get the number of signatures */ + if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) { + return -1; + } + signatureCount = ntohl(signatureCount); + + /* Check that we have less than the max amount of signatures so we don't + waste too much of either updater's or signmar's time. */ + if (signatureCount > MAX_SIGNATURES) { + return -1; + } + + /* Skip past the whole signature block */ + for (i = 0; i < signatureCount; i++) { + /* Skip past the signature algorithm ID */ + if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) { + return -1; + } + + /* Read the signature length and skip past the signature */ + if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) { + return -1; + } + signatureLen = ntohl(signatureLen); + if (fseek(fp, signatureLen, SEEK_CUR)) { + return -1; + } + } + + if (ftell(fp) == offsetToContent) { + *hasAdditionalBlocks = 0; + } else { + if (numAdditionalBlocks) { + /* We have an additional block, so read in the number of additional blocks + and set the offset. */ + *hasAdditionalBlocks = 1; + if (fread(numAdditionalBlocks, sizeof(uint32_t), 1, fp) != 1) { + return -1; + } + *numAdditionalBlocks = ntohl(*numAdditionalBlocks); + if (offsetAdditionalBlocks) { + *offsetAdditionalBlocks = ftell(fp); + } + } else if (offsetAdditionalBlocks) { + /* numAdditionalBlocks is not specified but offsetAdditionalBlocks + is, so fill it! */ + *offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t); + } + } + + return 0; +} + +/** + * Reads the product info block from the MAR file's additional block section. + * The caller is responsible for freeing the fields in infoBlock + * if the return is successful. + * + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +read_product_info_block(char *path, + struct ProductInformationBlock *infoBlock) +{ + int rv; + MarFile mar; + mar.fp = fopen(path, "rb"); + if (!mar.fp) { + return -1; + } + rv = mar_read_product_info_block(&mar, infoBlock); + fclose(mar.fp); + return rv; +} + +/** + * Reads the product info block from the MAR file's additional block section. + * The caller is responsible for freeing the fields in infoBlock + * if the return is successful. + * + * @param infoBlock Out parameter for where to store the result to + * @return 0 on success, -1 on failure +*/ +int +mar_read_product_info_block(MarFile *mar, + struct ProductInformationBlock *infoBlock) +{ + int i, hasAdditionalBlocks, offset, + offsetAdditionalBlocks, numAdditionalBlocks, + additionalBlockSize, additionalBlockID; + /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and + product version < 32 bytes + 3 NULL terminator bytes. */ + char buf[97] = { '\0' }; + int ret = get_mar_file_info_fp(mar->fp, NULL, NULL, + &hasAdditionalBlocks, + &offsetAdditionalBlocks, + &numAdditionalBlocks); + for (i = 0; i < numAdditionalBlocks; ++i) { + /* Read the additional block size */ + if (fread(&additionalBlockSize, + sizeof(additionalBlockSize), + 1, mar->fp) != 1) { + return -1; + } + additionalBlockSize = ntohl(additionalBlockSize) - + sizeof(additionalBlockSize) - + sizeof(additionalBlockID); + + /* Read the additional block ID */ + if (fread(&additionalBlockID, + sizeof(additionalBlockID), + 1, mar->fp) != 1) { + return -1; + } + additionalBlockID = ntohl(additionalBlockID); + + if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) { + const char *location; + int len; + + /* This block must be at most 104 bytes. + MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL + terminator bytes. We only check for 96 though because we remove 8 + bytes above from the additionalBlockSize: We subtract + sizeof(additionalBlockSize) and sizeof(additionalBlockID) */ + if (additionalBlockSize > 96) { + return -1; + } + + if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) { + return -1; + } + + /* Extract the MAR channel name from the buffer. For now we + point to the stack allocated buffer but we strdup this + if we are within bounds of each field's max length. */ + location = buf; + len = strlen(location); + infoBlock->MARChannelID = location; + location += len + 1; + if (len >= 64) { + infoBlock->MARChannelID = NULL; + return -1; + } + + /* Extract the version from the buffer */ + len = strlen(location); + infoBlock->productVersion = location; + location += len + 1; + if (len >= 32) { + infoBlock->MARChannelID = NULL; + infoBlock->productVersion = NULL; + return -1; + } + infoBlock->MARChannelID = + strdup(infoBlock->MARChannelID); + infoBlock->productVersion = + strdup(infoBlock->productVersion); + return 0; + } else { + /* This is not the additional block you're looking for. Move along. */ + if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) { + return -1; + } + } + } + + /* If we had a product info block we would have already returned */ + return -1; +} + +const MarItem *mar_find_item(MarFile *mar, const char *name) { + uint32_t hash; + const MarItem *item; + + hash = mar_hash_name(name); + + item = mar->item_table[hash]; + while (item && strcmp(item->name, name) != 0) + item = item->next; + + return item; +} + +int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) { + MarItem *item; + int i; + + for (i = 0; i < TABLESIZE; ++i) { + item = mar->item_table[i]; + while (item) { + int rv = callback(mar, item, closure); + if (rv) + return rv; + item = item->next; + } + } + + return 0; +} + +int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf, + int bufsize) { + int nr; + + if (offset == (int) item->length) + return 0; + if (offset > (int) item->length) + return -1; + + nr = item->length - offset; + if (nr > bufsize) + nr = bufsize; + + if (fseek(mar->fp, item->offset + offset, SEEK_SET)) + return -1; + + return fread(buf, 1, nr, mar->fp); +} + +/** + * Determines the MAR file information. + * + * @param path The path of the MAR file to check. + * @param hasSignatureBlock Optional out parameter specifying if the MAR + * file has a signature block or not. + * @param numSignatures Optional out parameter for storing the number + * of signatures in the MAR file. + * @param hasAdditionalBlocks Optional out parameter specifying if the MAR + * file has additional blocks or not. + * @param offsetAdditionalBlocks Optional out parameter for the offset to the + * first additional block. Value is only valid if + * hasAdditionalBlocks is not equal to 0. + * @param numAdditionalBlocks Optional out parameter for the number of + * additional blocks. Value is only valid if + * has_additional_blocks is not equal to 0. + * @return 0 on success and non-zero on failure. + */ +int get_mar_file_info(const char *path, + int *hasSignatureBlock, + int *numSignatures, + int *hasAdditionalBlocks, + int *offsetAdditionalBlocks, + int *numAdditionalBlocks) +{ + int rv; + FILE *fp = fopen(path, "rb"); + if (!fp) { + return -1; + } + + rv = get_mar_file_info_fp(fp, hasSignatureBlock, + numSignatures, hasAdditionalBlocks, + offsetAdditionalBlocks, numAdditionalBlocks); + + fclose(fp); + return rv; +} diff --git a/Open-ILS/xul/staff_client/external/libmar/tool/Makefile.am b/Open-ILS/xul/staff_client/external/libmar/tool/Makefile.am new file mode 100644 index 0000000000..a84243e1e3 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/tool/Makefile.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -I../src/ +noinst_PROGRAMS = mar mbsdiff +mar_SOURCES = mar.c +mar_LDADD = ../src/libmar.a +mbsdiff_SOURCES = bsdiff.c crc32.c diff --git a/Open-ILS/xul/staff_client/external/libmar/tool/bsdiff.c b/Open-ILS/xul/staff_client/external/libmar/tool/bsdiff.c new file mode 100644 index 0000000000..ea384bbdc1 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/tool/bsdiff.c @@ -0,0 +1,393 @@ +/*- + * Copyright 2003-2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Modified 18 March 2013 by Jason Stephenson . + * + * Modifications to make it compatible with the output of Mozilla's mbsdiff: + * + * Switch from 64 bit off_t to 32 bit int32_t. + * Don't bzip2 the output blocks. + * Use the modified header that Mozilla's bspatch expects. + * Add crc32 of the original file to the header. + * Use 32-bit ftell and fseek instead of ftello and fseeko. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Calculate the crc32. */ +extern uint32_t +crc32(const unsigned char *buf, uint32_t len); + +#define MIN(x,y) (((x)<(y)) ? (x) : (y)) + +static void split(int32_t *I,int32_t *V,int32_t start,int32_t len,int32_t h) +{ + int32_t i,j,k,x,tmp,jj,kk; + + if(len<16) { + for(k=start;kstart) split(I,V,start,jj-start,h); + + for(i=0;ikk) split(I,V,kk,start+len-kk,h); +} + +static void qsufsort(int32_t *I,int32_t *V,u_char *old,int32_t oldsize) +{ + int32_t buckets[256]; + int32_t i,h,len; + + for(i=0;i<256;i++) buckets[i]=0; + for(i=0;i0;i--) buckets[i]=buckets[i-1]; + buckets[0]=0; + + for(i=0;iy) { + *pos=I[st]; + return x; + } else { + *pos=I[en]; + return y; + } + }; + + x=st+(en-st)/2; + if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { + return search(I,old,oldsize,new,newsize,x,en,pos); + } else { + return search(I,old,oldsize,new,newsize,st,x,pos); + }; +} + +int main(int argc,char *argv[]) +{ + int fd; + u_char *old,*new; + int32_t oldsize,newsize; + int32_t *I,*V; + int32_t scan,pos,len; + int32_t lastscan,lastpos,lastoffset; + int32_t oldscore,scsc; + int32_t s,Sf,lenf,Sb,lenb; + int32_t overlap,Ss,lens; + int32_t i; + int32_t dblen,eblen; + u_char *db,*eb; + u_char header[32]; + FILE * pf; + uint32_t crcVal; + int32_t outVal; /* place holder for result of htonl */ + + if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); + + /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure + that we never try to malloc(0) and get a NULL pointer */ + if(((fd=open(argv[1],O_RDONLY,0))<0) || + ((oldsize=lseek(fd,0,SEEK_END))==-1) || + ((old=malloc(oldsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,old,oldsize)!=oldsize) || + (close(fd)==-1)) err(1,"%s",argv[1]); + + if(((I=malloc((oldsize+1)*sizeof(int32_t)))==NULL) || + ((V=malloc((oldsize+1)*sizeof(int32_t)))==NULL)) err(1,NULL); + + /* Calculate the old file's crc32 value. */ + crcVal = crc32(old, oldsize); + + qsufsort(I,V,old,oldsize); + + free(V); + + /* Allocate newsize+1 bytes instead of newsize bytes to ensure + that we never try to malloc(0) and get a NULL pointer */ + if(((fd=open(argv[2],O_RDONLY,0))<0) || + ((newsize=lseek(fd,0,SEEK_END))==-1) || + ((new=malloc(newsize+1))==NULL) || + (lseek(fd,0,SEEK_SET)!=0) || + (read(fd,new,newsize)!=newsize) || + (close(fd)==-1)) err(1,"%s",argv[2]); + + if(((db=malloc(newsize+1))==NULL) || + ((eb=malloc(newsize+1))==NULL)) err(1,NULL); + dblen=0; + eblen=0; + + /* Create the patch file */ + if ((pf = fopen(argv[3], "w")) == NULL) + err(1, "%s", argv[3]); + + /* This is the modified header used by Mozilla's hacked bspatch. It + * doesn't actually bzip2 anything despite what the documentation on + * the wiki implies. */ + /* Header is + 0 8 "MBDIFF10" + 8 4 length of the file to be patched + 12 4 crc32 of file to be patched + 16 4 length of the new file + 20 4 length of ctrl block + 24 4 length of diff block + 28 4 length of extra block + /* File is + 0 32 Header + 32 ?? ctrl block + ?? ?? diff block + ?? ?? extra block */ + memcpy(header, "MBDIFF10", 8); + outVal = htonl(oldsize); + memcpy(header + 8, &outVal, 4); + outVal = htonl(crcVal); + memcpy(header + 12, &outVal, 4); + outVal = htonl(newsize); + memcpy(header + 16, &outVal, 4); + memcpy(header + 20, 0, 12); + if (fwrite(header, 32, 1, pf) != 1) + err(1, "fwrite(%s)", argv[3]); + + /* Compute the differences, writing ctrl as we go */ + scan=0;len=0; + lastscan=0;lastpos=0;lastoffset=0; + while(scanoldscore+8)) break; + + if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; }; + }; + + lenb=0; + if(scan=lastscan+i)&&(pos>=i);i++) { + if(old[pos-i]==new[scan-i]) s++; + if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; + }; + }; + + if(lastscan+lenf>scan-lenb) { + overlap=(lastscan+lenf)-(scan-lenb); + s=0;Ss=0;lens=0; + for(i=0;iSs) { Ss=s; lens=i+1; }; + }; + + lenf+=lens-overlap; + lenb-=lens; + }; + + for(i=0;i + +/* This guy lives in libbz2, and is the only reason we need libbz2. */ + extern uint32_t BZ2_crc32Table[256]; + +uint32_t +crc32(const unsigned char *buf, uint32_t len) +{ + uint32_t crc = 0xffffffffL; + + const unsigned char *end = buf + len; + for (; buf != end; ++buf) + crc = (crc << 8) ^ crc32Table[(crc >> 24) ^ *buf]; + + crc = ~crc; + return crc; +} diff --git a/Open-ILS/xul/staff_client/external/libmar/tool/mar.c b/Open-ILS/xul/staff_client/external/libmar/tool/mar.c new file mode 100644 index 0000000000..1e4db33106 --- /dev/null +++ b/Open-ILS/xul/staff_client/external/libmar/tool/mar.c @@ -0,0 +1,179 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* + * This file is part of Evergreen. + * + * Evergreen 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, either version 2 of the License, + * or (at your option) any later version. + * + * Evergreen 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 Evergreen. If not, see . + * + * This Source Code Form is derived from code that was originally + * subject to the terms of the Mozilla Public License, v. 2.0 and + * included in Evergreen. You may, therefore, use this Source Code + * Form under the terms of the Mozilla Public License 2.0. This + * licensing option does not affect the larger Evergreen project, only + * the Source Code Forms bearing this exception are affected. If a + * copy of the MPL was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include "mar.h" +#include "mar_cmdline.h" + +#ifdef XP_WIN +#include +#include +#define chdir _chdir +#else +#include +#endif + +static void print_usage() { + printf("usage:\n"); + printf("Create a MAR file:\n"); + printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " + "{-c|-x|-t|-T} archive.mar [files...]\n"); + printf("Print information on a MAR file:\n"); + printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] " + "-i unsigned_archive_to_refresh.mar\n"); + printf("This program does not handle unicode file paths properly\n"); +} + +static int mar_test_callback(MarFile *mar, + const MarItem *item, + void *unused) { + printf("%u\t0%o\t%s\n", item->length, item->flags, item->name); + return 0; +} + +static int mar_test(const char *path) { + MarFile *mar; + + mar = mar_open(path); + if (!mar) + return -1; + + printf("SIZE\tMODE\tNAME\n"); + mar_enum_items(mar, mar_test_callback, NULL); + + mar_close(mar); + return 0; +} + +int main(int argc, char **argv) { + char *MARChannelID = MAR_CHANNEL_ID; + char *productVersion = MOZ_APP_VERSION; + uint32_t i, k; + int rv = -1; + + if (argc < 3) { + print_usage(); + return -1; + } + + while (argc > 0) { + if (argv[1][0] == '-' && (argv[1][1] == 'c' || + argv[1][1] == 't' || argv[1][1] == 'x' || + argv[1][1] == 'i' || argv[1][1] == 'T')) { + break; + /* -C workingdirectory */ + } else if (argv[1][0] == '-' && argv[1][1] == 'C') { + chdir(argv[2]); + argv += 2; + argc -= 2; + /* MAR channel ID */ + } else if (argv[1][0] == '-' && argv[1][1] == 'H') { + MARChannelID = argv[2]; + argv += 2; + argc -= 2; + /* Product Version */ + } else if (argv[1][0] == '-' && argv[1][1] == 'V') { + productVersion = argv[2]; + argv += 2; + argc -= 2; + } + else { + print_usage(); + return -1; + } + } + + if (argv[1][0] != '-') { + print_usage(); + return -1; + } + + switch (argv[1][1]) { + case 'c': { + struct ProductInformationBlock infoBlock; + infoBlock.MARChannelID = MARChannelID; + infoBlock.productVersion = productVersion; + return mar_create(argv[2], argc - 3, argv + 3, &infoBlock); + } + case 'i': { + struct ProductInformationBlock infoBlock; + infoBlock.MARChannelID = MARChannelID; + infoBlock.productVersion = productVersion; + return refresh_product_info_block(argv[2], &infoBlock); + } + case 'T': { + struct ProductInformationBlock infoBlock; + uint32_t numSignatures, numAdditionalBlocks; + int hasSignatureBlock, hasAdditionalBlock; + if (!get_mar_file_info(argv[2], + &hasSignatureBlock, + &numSignatures, + &hasAdditionalBlock, + NULL, &numAdditionalBlocks)) { + if (hasSignatureBlock) { + printf("Signature block found with %d signature%s\n", + numSignatures, + numSignatures != 1 ? "s" : ""); + } + if (hasAdditionalBlock) { + printf("%d additional block%s found:\n", + numAdditionalBlocks, + numAdditionalBlocks != 1 ? "s" : ""); + } + + rv = read_product_info_block(argv[2], &infoBlock); + if (!rv) { + printf(" - Product Information Block:\n"); + printf(" - MAR channel name: %s\n" + " - Product version: %s\n", + infoBlock.MARChannelID, + infoBlock.productVersion); + free((void *)infoBlock.MARChannelID); + free((void *)infoBlock.productVersion); + } + } + printf("\n"); + /* The fall through from 'T' to 't' is intentional */ + } + case 't': + return mar_test(argv[2]); + + /* Extract a MAR file */ + case 'x': + return mar_extract(argv[2]); + + default: + print_usage(); + return -1; + } + + return 0; +} diff --git a/Open-ILS/xul/staff_client/external/make_updates.sh b/Open-ILS/xul/staff_client/external/make_updates.sh index d708e91cf6..db7006f396 100755 --- a/Open-ILS/xul/staff_client/external/make_updates.sh +++ b/Open-ILS/xul/staff_client/external/make_updates.sh @@ -193,12 +193,13 @@ function check_mar MBSDIFF=${MBSDIFF:-mbsdiff} fi if [ ! -x "$MAR" -o ! -x "$MBSDIFF" ]; then - if [ ! -f "external/mar" -o ! -f "external/mbsdiff" ]; then - wget ftp://ftp.mozilla.org/pub/mozilla.org/xulrunner/mar-generation-tools/mar-generation-tools-linux.zip - unzip mar-generation-tools-linux.zip -d external + if [ ! -f "external/limbar/tool/mar" -o ! -f "external/libmar/tool/mbsdiff" ]; then + cd external/libmar + make + cd - fi - MAR="$PWD/external/mar" - MBSDIFF="$PWD/external/mbsdiff" + MAR="$PWD/external/libmar/tool/mar" + MBSDIFF="$PWD/external/libmar/tool/mbsdiff" fi } diff --git a/configure.ac b/configure.ac index fab6180e0c..d8ac271bd5 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ AC_INIT(Open-ILS, trunk, open-ils-dev@list.georgialibraries.org) AM_INIT_AUTOMAKE([OpenILS], [trunk]) AC_REVISION($Revision: 0.1 $) AC_CONFIG_SRCDIR([configure.ac]) +AC_CONFIG_SUBDIRS([Open-ILS/xul/staff_client/external/libmar]) AC_SUBST(prefix) AC_SUBST([abs_top_builddir]) #----------------------------------- -- 2.43.2