summaryrefslogtreecommitdiffstats
path: root/src/libr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libr.c')
-rw-r--r--src/libr.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/src/libr.c b/src/libr.c
new file mode 100644
index 0000000..d038594
--- /dev/null
+++ b/src/libr.c
@@ -0,0 +1,489 @@
+/*
+ *
+ * Copyright (c) 2008-2009 Erich Hoover
+ *
+ * libr - Add resources into ELF binaries
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * To provide feedback, report bugs, or otherwise contact me:
+ * ehoover at mines dot edu
+ *
+ */
+
+/* Include compile-time parameters */
+#include "config.h"
+
+#include "libr.h"
+#include "tempfiles.h"
+
+/* Obtain file information */
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Compress files */
+#include <zlib.h>
+#include <math.h> /* for ceil */
+
+/* Handle strings and variable arguments*/
+#include <string.h>
+#include <stdarg.h>
+
+/* For C99 number types */
+#include <stdint.h>
+
+/* Handle status codes for multiple threads */
+#include <pthread.h>
+
+#define SPEC_VERSION '1'
+#define OFFSET_TYPE ((unsigned long) 4)
+#define OFFSET_UNCOMPRESSED ((unsigned long) OFFSET_TYPE+sizeof(unsigned char))
+#define OFFSET_UNCOMPRESSED_SIZE ((unsigned long) OFFSET_TYPE+sizeof(unsigned char))
+#define OFFSET_COMPRESSED ((unsigned long) OFFSET_UNCOMPRESSED_SIZE+sizeof(uint32_t))
+
+#if 0
+ extern const char * __progname_full;
+ #define progpath() (char *) __progname_full
+#endif
+#define getself() ((char *) "/proc/self/exe")
+
+pthread_key_t error_key;
+
+/*
+ * Free the error status code/message structure
+ * (called on thread destruction or when a new code is set)
+ */
+void free_error_key(void *_m)
+{
+ libr_intstatus *error = (libr_intstatus *) _m;
+
+ if(error != NULL)
+ {
+ /* Free the error structure */
+ if(error->message != NULL)
+ free(error->message);
+ free(error);
+ }
+}
+
+/*
+ * Set the error code and message for retrieval
+ */
+void libr_set_error(libr_intstatus error)
+{
+ static int thread_key_initialized = false;
+ libr_intstatus *status = NULL;
+
+ if(!thread_key_initialized)
+ {
+ if(pthread_key_create(&error_key, free_error_key) != 0)
+ return; /* a serious pthread-related error occurred */
+ if(pthread_setspecific(error_key, NULL) != 0)
+ return; /* a serious pthread-related error occurred */
+ thread_key_initialized = true;
+ }
+ free_error_key(pthread_getspecific(error_key));
+ status = (libr_intstatus *) malloc(sizeof(libr_intstatus));
+ memcpy(status, &error, sizeof(libr_intstatus));
+ if(pthread_setspecific(error_key, (void *) status) != 0)
+ return; /* a serious pthread-related error occurred */
+}
+
+/*
+ * Make an internal status passing structure, set the error code with this status
+ * if the status is not LIBR_OK.
+ */
+libr_intstatus make_status(const char *function, libr_status code, char *message, ...)
+{
+ libr_intstatus status = {NULL, code, function};
+ va_list args;
+
+ if(message != NULL)
+ {
+ status.message = (char *) malloc(1024);
+ va_start(args, message);
+ vsnprintf(status.message, 1024, message, args);
+ va_end(args);
+ }
+
+ libr_set_error(status);
+ return status;
+}
+
+/*
+ * Make sure that the section is libr-compatible
+ */
+libr_intstatus section_ok(libr_section *scn, libr_data *data)
+{
+ char required_header[5], test_header[4] = {'R', 'E', 'S', SPEC_VERSION};
+ void *ptr = data_pointer(scn, data);
+ size_t size = data_size(scn, data);
+
+ if(ptr == NULL || size < sizeof(required_header))
+ RETURN(LIBR_ERROR_NOTRESOURCE, "Not a valid libr-resource");
+ memcpy(required_header, ptr, sizeof(required_header));
+ if(strncmp(required_header, test_header, sizeof(test_header)) != 0)
+ RETURN(LIBR_ERROR_NOTRESOURCE, "Not a valid libr-resource");
+ RETURN_OK;
+}
+
+/*
+ * Remove a resourcefrom the ELF binary handle
+ */
+EXPORT_FN int libr_clear(libr_file *file_handle, char *resource_name)
+{
+ libr_data *data = NULL;
+ libr_section *scn = NULL;
+
+ /* Ensure valid inputs */
+ if(file_handle == NULL || resource_name == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_INVALIDPARAMS, "Invalid parameters passed to function");
+ if(file_handle->access != LIBR_READ_WRITE)
+ PUBLIC_RETURN(LIBR_ERROR_NOPERM, "Open handle with LIBR_READ_WRITE access");
+ /* Find the section containing the icon */
+ if(find_section(file_handle, resource_name, &scn).status != LIBR_OK)
+ return false; /* error already set */
+ /* Get the section data (interested in header) */
+ if((data = get_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section");
+ /* Confirm that this resource is libr-compatible */
+ if(section_ok(scn, data).status != LIBR_OK)
+ return false; /* error already set */
+ /* Clear the data resource */
+ if(set_data(file_handle, scn, data, 0, NULL, 0).status != LIBR_OK)
+ return false; /* error already set */
+ /* Remove the section */
+ if(remove_section(file_handle, scn).status != LIBR_OK)
+ return false; /* error already set */
+ return true;
+}
+
+/*
+ * Close the specified ELF binary handle
+ */
+EXPORT_FN void libr_close(libr_file *file_handle)
+{
+ unregister_handle_cleanup(file_handle);
+ libr_close_internal(file_handle);
+}
+/* Only called directly by cleanup routine, all other calls should be through libr_close */
+void libr_close_internal(libr_file *file_handle)
+{
+ write_output(file_handle);
+ free(file_handle);
+}
+
+/*
+ * Return the last error message for the active thread
+ */
+EXPORT_FN char *libr_errmsg(void)
+{
+ libr_intstatus *error = (libr_intstatus *) pthread_getspecific(error_key);
+
+ if(error == NULL)
+ return NULL;
+ return error->message;
+}
+
+/*
+ * Return the last error code for the active thread (or LIBR_OK for no error)
+ */
+EXPORT_FN libr_status libr_errno(void)
+{
+ libr_intstatus *error = (libr_intstatus *) pthread_getspecific(error_key);
+
+ if(error == NULL) /* Nothing has happened yet */
+ return LIBR_OK;
+ return error->status;
+}
+
+/*
+ * Return the name of a libr-compatible resource
+ */
+EXPORT_FN char *libr_list(libr_file *file_handle, unsigned int resourceid)
+{
+ libr_section *scn = NULL;
+ libr_data *data = NULL;
+ int i = 0;
+
+ while((scn = next_section(file_handle, scn)) != NULL)
+ {
+ /* Get the section data (interested in header) */
+ if((data = get_data(file_handle, scn)) == NULL)
+ return NULL;
+ if(section_ok(scn, data).status == LIBR_OK)
+ {
+ if(i == resourceid)
+ return strdup(section_name(file_handle, scn));
+ i++;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Allocate a buffer containing the data of a resource
+ */
+EXPORT_FN char *libr_malloc(libr_file *file_handle, char *resource_name, size_t *size)
+{
+ char *buffer = NULL;
+ size_t size_local;
+
+ if(size == NULL)
+ size = &size_local;
+ if(!libr_size(file_handle, resource_name, size))
+ return NULL; /* error already set */
+ buffer = (char *) malloc(*size);
+ if(!libr_read(file_handle, resource_name, buffer))
+ {
+ free(buffer);
+ return NULL; /* error already set */
+ }
+ return buffer;
+}
+
+/*
+ * Open the specified ELF binary (caller if filename is NULL)
+ */
+EXPORT_FN libr_file *libr_open(char *filename, libr_access_t access)
+{
+ libr_file *file_handle = NULL;
+ static int initialized = false;
+
+ if(!initialized)
+ {
+ if(strncmp(zlibVersion(), ZLIB_VERSION, 1) != 0)
+ {
+ SET_ERROR(LIBR_ERROR_ZLIBINIT, "zlib library initialization failed");
+ return NULL;
+ }
+ initialize_backend();
+ initialized = true;
+ }
+
+ if(filename == NULL)
+ filename = getself();
+ file_handle = (libr_file *) malloc(sizeof(libr_file));
+ memset(file_handle, 0, sizeof(libr_file));
+ if(open_handles(file_handle, filename, access).status != LIBR_OK)
+ {
+ /* failed to open file for processing, error already set */
+ free(file_handle);
+ file_handle = NULL;
+ }
+ /* Cleanup handles automatically when libr exits memory */
+ if(file_handle != NULL)
+ register_handle_cleanup(file_handle);
+ return file_handle;
+}
+
+/*
+ * Read a resource from the specified ELF binary handle
+ */
+EXPORT_FN int libr_read(libr_file *file_handle, char *resource_name, char *buffer)
+{
+ unsigned long uncompressed_size = 0, compressed_size = 0;
+ char *data_buffer = NULL;
+ libr_section *scn = NULL;
+ libr_data *data = NULL;
+ libr_type_t type;
+
+ /* Find the section containing the icon */
+ if(find_section(file_handle, resource_name, &scn).status != LIBR_OK)
+ return false; /* error already set */
+ /* Get the section data (interested in header) */
+ if((data = get_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section");
+ /* Confirm that this resource is libr-compatible */
+ if(section_ok(scn, data).status != LIBR_OK)
+ return false; /* error already set */
+ data_buffer = (char *) data_pointer(scn, data);
+ /* Get the size of the data resource */
+ type = (libr_type_t) data_buffer[OFFSET_TYPE];
+ switch(type)
+ {
+ case LIBR_UNCOMPRESSED:
+ { if(data_size(scn, data)-OFFSET_UNCOMPRESSED < 0)
+ PUBLIC_RETURN(LIBR_ERROR_SIZEMISMATCH, "Section's data size does not make sense");
+ uncompressed_size = data_size(scn, data)-OFFSET_UNCOMPRESSED;
+ memcpy(buffer, &data_buffer[OFFSET_UNCOMPRESSED], uncompressed_size);
+ } break;
+ case LIBR_COMPRESSED:
+ {
+ uint32_t size_temp;
+
+ memcpy(&size_temp, &data_buffer[OFFSET_UNCOMPRESSED_SIZE], sizeof(uint32_t));
+ uncompressed_size = size_temp;
+ compressed_size = data_size(scn, data)-OFFSET_COMPRESSED;
+ if(uncompress((unsigned char *)buffer, &uncompressed_size, (unsigned char *)&data_buffer[OFFSET_COMPRESSED], compressed_size) != Z_OK)
+ PUBLIC_RETURN(LIBR_ERROR_UNCOMPRESS, "Failed to uncompress resource data");
+ } break;
+ default:
+ PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified");
+ }
+ return true;
+}
+
+/*
+ * Retrieve the number of libr-compatible resources
+ */
+EXPORT_FN unsigned int libr_resources(libr_file *file_handle)
+{
+ libr_section *scn = NULL;
+ libr_data *data = NULL;
+ int i = 0;
+
+ while((scn = next_section(file_handle, scn)) != NULL)
+ {
+ if((data = get_data(file_handle, scn)) == NULL)
+ continue;
+ if(section_ok(scn, data).status == LIBR_OK)
+ i++;
+ }
+ return i;
+}
+
+/*
+ * Get the size of a resource from the specified ELF binary handle
+ */
+EXPORT_FN int libr_size(libr_file *file_handle, char *resource_name, size_t *retsize)
+{
+ char *data_buffer = NULL;
+ libr_section *scn = NULL;
+ libr_data *data = NULL;
+ unsigned long size = 0;
+ libr_type_t type;
+
+ /* Find the section containing the icon */
+ if(find_section(file_handle, resource_name, &scn).status != LIBR_OK)
+ return false; /* error already set */
+ /* Get the section data (interested in header) */
+ if((data = get_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section");
+ /* Confirm that this resource is libr-compatible */
+ if(section_ok(scn, data).status != LIBR_OK)
+ return false; /* error already set */
+ data_buffer = (char *) data_pointer(scn, data);
+ /* Get the size of the data resource */
+ type = (libr_type_t) data_buffer[OFFSET_TYPE];
+ switch(type)
+ {
+ case LIBR_UNCOMPRESSED:
+ {
+ size_t full_size = data_size(scn, data);
+
+ if(full_size-OFFSET_UNCOMPRESSED < 0)
+ PUBLIC_RETURN(LIBR_ERROR_SIZEMISMATCH, "Section's data size does not make sense");
+ size = full_size - OFFSET_UNCOMPRESSED;
+ } break;
+ case LIBR_COMPRESSED:
+ {
+ memcpy(&size, &data_buffer[OFFSET_UNCOMPRESSED_SIZE], sizeof(uint32_t));
+ } break;
+ default:
+ PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified");
+ }
+ *retsize = size;
+ return true;
+}
+
+/*
+ * Write a resource to the specified ELF binary handle
+ */
+EXPORT_FN int libr_write(libr_file *file_handle, char *resource_name, char *buffer, size_t size, libr_type_t type, libr_overwrite_t overwrite)
+{
+ char header[9] = {'R', 'E', 'S', SPEC_VERSION};
+ unsigned int header_size = 4;
+ libr_section *scn = NULL;
+ libr_data *data = NULL;
+ libr_intstatus ret;
+
+ /* Ensure valid inputs */
+ if(file_handle == NULL || resource_name == NULL || buffer == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_INVALIDPARAMS, "Invalid parameters passed to function");
+ if(file_handle->access != LIBR_READ_WRITE)
+ PUBLIC_RETURN(LIBR_ERROR_NOPERM, "Open handle with LIBR_READ_WRITE access");
+ /* Get the section if it already exists */
+ ret = find_section(file_handle, resource_name, &scn);
+ if(ret.status == LIBR_OK)
+ {
+ /* If the section exists (and overwrite is not specified) then fail */
+ if(!overwrite)
+ PUBLIC_RETURN(LIBR_ERROR_OVERWRITE, "Section already exists, over-write not specified");
+ /* Grab the existing data section for overwriting */
+ if((data = get_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section");
+ }
+ else if(ret.status == LIBR_ERROR_NOSECTION)
+ {
+ /* Create a new section named "resource_name" */
+ if(add_section(file_handle, resource_name, &scn).status != LIBR_OK)
+ return false; /* error already set */
+ /* Create a data segment to store the compressed image */
+ if((data = new_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_NEWDATA, "Failed to create data for section");
+ }
+ else
+ return false; /* error already set */
+
+ header[header_size++] = (char) type;
+ switch(type)
+ {
+ case LIBR_UNCOMPRESSED:
+ /* Do nothing, just stick the data in */
+ break;
+ case LIBR_COMPRESSED:
+ {
+ char *compressed_buffer = NULL, *uncompressed_buffer = buffer;
+ unsigned long compressed_size = 0, uncompressed_size = size;
+ uint32_t size_temp;
+
+ /* Store the uncompressed size to the header */
+ size_temp = uncompressed_size;
+ memcpy(&header[header_size], &size_temp, sizeof(uint32_t));
+ header_size += sizeof(uint32_t);
+ /* Compress the data for storage */
+ compressed_size = ceil((uncompressed_size+12)*1.1);
+ compressed_buffer = (char *) malloc(compressed_size);
+ if(compress((unsigned char *)compressed_buffer, &compressed_size, (unsigned char *)uncompressed_buffer, uncompressed_size) != Z_OK)
+ {
+ free(compressed_buffer);
+ PUBLIC_RETURN(LIBR_ERROR_COMPRESS, "Failed to compress resource data");
+ }
+ /* From here on treat the compressed buffer as the data */
+ buffer = compressed_buffer;
+ size = compressed_size;
+ } break;
+ default:
+ PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified");
+ }
+ /* Store the resource header data */
+ if(set_data(file_handle, scn, data, 0, &header[0], header_size).status != LIBR_OK)
+ return false; /* error already set */
+ /* Create a data segment to store the post-header data
+ * NOTE: For existing files the data of the section is represented as a continuous stream
+ * (so calling elf_getdata now WILL NOT return the post-header data)
+ */
+ if((data = new_data(file_handle, scn)) == NULL)
+ PUBLIC_RETURN(LIBR_ERROR_NEWDATA, "Failed to create data for section");
+ /* Store the actual user data to the section */
+ if(set_data(file_handle, scn, data, header_size, buffer, size).status != LIBR_OK)
+ return false; /* error already set */
+ /* Close compression resources */
+ if(type == LIBR_COMPRESSED)
+ free(buffer);
+ return true;
+}