1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. 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 <assert.h> // for C11 static_assert
#include <stdint.h>
#include <stdio.h>
#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
static_assert(MAX_SIGNATURES <= 9, "too many signatures");
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;
/**
* File offset and length for tracking access of byte indexes
*/
typedef struct SeenIndex_ {
struct SeenIndex_* next; /* private field */
uint32_t offset; /* offset into archive */
uint32_t length; /* length of the data in bytes */
} SeenIndex;
#define TABLESIZE 256
/**
* Mozilla ARchive (MAR) file data structure
*/
struct MarFile_ {
unsigned char* buffer; /* file buffer containing the entire MAR */
size_t data_len; /* byte count of the data in the buffer */
MarItem* item_table[TABLESIZE]; /* hash table of files in the archive */
SeenIndex* index_list; /* file indexes processed */
int item_table_is_valid; /* header and index validation flag */
};
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);
enum MarReadResult_ {
MAR_READ_SUCCESS,
MAR_IO_ERROR,
MAR_MEM_ERROR,
MAR_FILE_TOO_BIG_ERROR,
};
typedef enum MarReadResult_ MarReadResult;
/**
* Open a MAR file for reading.
* @param path Specifies the path to the MAR file to open. This path must
* be compatible with fopen.
* @param out_mar Out-parameter through which the created MarFile structure is
* returned. Guaranteed to be a valid structure if
* MAR_READ_SUCCESS is returned. Otherwise NULL will be
* assigned.
* @return NULL if an error occurs.
*/
MarReadResult mar_open(const char* path, MarFile** out_mar);
#ifdef XP_WIN
MarReadResult mar_wopen(const wchar_t* path, MarFile** out_mar);
#endif
/**
* Close a MAR file that was opened using mar_open.
* @param mar The MarFile object to close.
*/
void mar_close(MarFile* mar);
/**
* Reads the specified amount of data from the buffer in MarFile that contains
* the entirety of the MAR file data.
* @param mar The MAR file to read from.
* @param dest The buffer to read into.
* @param position The byte index to start reading from the MAR at.
* On success, position will be incremented by size.
* @param size The number of bytes to read.
* @return 0 If the specified amount of data was read.
* -1 If the buffer MAR is not large enough to read the
* specified amount of data at the specified position.
*/
int mar_read_buffer(MarFile* mar, void* dest, size_t* position, size_t size);
/**
* Reads the specified amount of data from the buffer in MarFile that contains
* the entirety of the MAR file data. If there isn't that much data remaining,
* reads as much as possible.
* @param mar The MAR file to read from.
* @param dest The buffer to read into.
* @param position The byte index to start reading from the MAR at.
* This function will increment position by the number of bytes
* copied.
* @param size The maximum number of bytes to read.
* @return The number of bytes copied into dest.
*/
int mar_read_buffer_max(MarFile* mar, void* dest, size_t* position,
size_t size);
/**
* Increments position by distance. Checks that the resulting position is still
* within the bounds of the buffer. Much like fseek, this will allow position to
* be successfully placed just after the end of the buffer.
* @param mar The MAR file to read from.
* @param position The byte index to start reading from the MAR at.
* On success, position will be incremented by size.
* @param distance The number of bytes to move forward by.
* @return 0 If position was successfully moved.
* -1 If moving position by distance would move it outside the
* bounds of the buffer.
*/
int mar_buffer_seek(MarFile* mar, size_t* position, size_t distance);
/**
* 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, uint8_t* 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);
#define MAR_MAX_CERT_SIZE (16 * 1024) // Way larger than necessary
/* Read the entire file (not a MAR file) into a newly-allocated buffer.
* This function does not write to stderr. Instead, the caller should
* write whatever error messages it sees fit. The caller must free the returned
* buffer using free().
*
* @param filePath The path to the file that should be read.
* @param maxSize The maximum valid file size.
* @param data On success, *data will point to a newly-allocated buffer
* with the file's contents in it.
* @param size On success, *size will be the size of the created buffer.
*
* @return 0 on success, -1 on error
*/
int mar_read_entire_file(const char* filePath, uint32_t maxSize,
/*out*/ const uint8_t** data,
/*out*/ uint32_t* size);
/**
* Verifies a MAR file by verifying each signature with the corresponding
* certificate. That is, the first signature will be verified using the first
* certificate given, the second signature will be verified using the second
* certificate given, etc. The signature count must exactly match the number of
* certificates given, and all signature verifications must succeed.
* We do not check that the certificate was issued by any trusted authority.
* We assume it to be self-signed. We do not check whether the certificate
* is valid for this usage.
*
* @param mar The already opened MAR file.
* @param certData Pointer to the first element in an array of certificate
* file data.
* @param certDataSizes Pointer to the first element in an array for size of
* the cert data.
* @param certCount The number of elements in certData and certDataSizes
* @return 0 on success
* a negative number if there was an error
* a positive number if the signature does not verify
*/
int mar_verify_signatures(MarFile* mar, const uint8_t* const* certData,
const uint32_t* certDataSizes, uint32_t certCount);
/**
* 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_ */
|