// license:BSD-3-Clause // copyright-holders:Nathan Woods /*************************************************************************** imgtool.cpp Core code for Imgtool ***************************************************************************/ #include #include #include #include "imgtool.h" #include "formats/imageutl.h" #include "library.h" #include "modules.h" #include "pool.h" /*************************************************************************** GLOBALS ***************************************************************************/ static std::unique_ptr global_imgtool_library; static int global_omit_untested; static void (*global_warn)(const char *message); void CLIB_DECL ATTR_PRINTF(1,2) logerror(const char *format, ...) { va_list arg; va_start(arg, format); vprintf(format, arg); va_end(arg); } /*************************************************************************** Imgtool initialization and basics ***************************************************************************/ //------------------------------------------------- // rtrim //------------------------------------------------- void rtrim(char *buf) { size_t buflen; char *s; buflen = strlen(buf); if (buflen) { for (s = &buf[buflen-1]; s >= buf && (*s >= '\0') && isspace(*s); s--) *s = '\0'; } } //------------------------------------------------- // strncpyz //------------------------------------------------- char *strncpyz(char *dest, const char *source, size_t len) { char *s; if (len) { s = strncpy(dest, source, len - 1); dest[len-1] = '\0'; } else { s = dest; } return s; } //------------------------------------------------- // extract_padded_string //------------------------------------------------- static std::string extract_padded_string(const char *source, size_t len) { while ((len > 0) && (source[len - 1] == ' ')) len--; return std::string(source, len); } //------------------------------------------------- // extract_padded_filename - this is a common // enough scenario that it is justified to have // this in common code //------------------------------------------------- std::string extract_padded_filename(const char *source, size_t filename_length, size_t extension_length) { std::string filename = extract_padded_string(source, filename_length); std::string extension = extract_padded_string(source + filename_length, extension_length); return extension.empty() ? filename : filename + "." + extension; } //------------------------------------------------- // markerrorsource - marks where an error source //------------------------------------------------- static imgtoolerr_t markerrorsource(imgtoolerr_t err) { switch(err) { case IMGTOOLERR_OUTOFMEMORY: case IMGTOOLERR_UNEXPECTED: case IMGTOOLERR_BUFFERTOOSMALL: /* Do nothing */ break; case IMGTOOLERR_FILENOTFOUND: case IMGTOOLERR_BADFILENAME: err = imgtoolerr_t(err | IMGTOOLERR_SRC_FILEONIMAGE); break; default: err = imgtoolerr_t(err | IMGTOOLERR_SRC_IMAGEFILE); break; } return err; } //------------------------------------------------- // internal_error - debug function for raising // internal errors //------------------------------------------------- static void internal_error(const imgtool_module *module, const char *message) { #ifdef MAME_DEBUG printf("%s: %s\n", module->name, message); #endif } //------------------------------------------------- // imgtool_init - initializes the imgtool core //------------------------------------------------- void imgtool_init(bool omit_untested, void (*warn)(const char *message)) { imgtoolerr_t err; err = imgtool_create_cannonical_library(omit_untested, global_imgtool_library); assert(err == IMGTOOLERR_SUCCESS); if (err == IMGTOOLERR_SUCCESS) { global_imgtool_library->sort(imgtool::library::sort_type::DESCRIPTION); } global_omit_untested = omit_untested; global_warn = warn; } //------------------------------------------------- // imgtool_exit - closes out the imgtool core //------------------------------------------------- void imgtool_exit(void) { if (global_imgtool_library) global_imgtool_library.reset(); global_warn = nullptr; } //------------------------------------------------- // imgtool_find_module - looks up a module //------------------------------------------------- const imgtool_module *imgtool_find_module(const std::string &modulename) { return global_imgtool_library->findmodule(modulename); } //------------------------------------------------- // imgtool_find_module - looks up a module //------------------------------------------------- const imgtool::library::modulelist &imgtool_get_modules() { return global_imgtool_library->modules(); } //------------------------------------------------- // imgtool_get_module_features - retrieves a // structure identifying this module's features // associated with an image //------------------------------------------------- imgtool_module_features imgtool_get_module_features(const imgtool_module *module) { imgtool_module_features features; memset(&features, 0, sizeof(features)); if (module->create) features.supports_create = 1; if (module->open) features.supports_open = 1; if (module->read_sector) features.supports_readsector = 1; if (module->write_sector) features.supports_writesector = 1; return features; } //------------------------------------------------- // imgtool_warn - issues a warning //------------------------------------------------- void imgtool_warn(const char *format, ...) { va_list va; char buffer[2000]; if (global_warn) { va_start(va, format); vsprintf(buffer, format, va); va_end(va); global_warn(buffer); } } //------------------------------------------------- // evaluate_module - evaluates a single file to // determine what module can best handle a file //------------------------------------------------- static imgtoolerr_t evaluate_module(const char *fname, const imgtool_module *module, float &result) { imgtoolerr_t err; imgtool::image::ptr image; imgtool::partition::ptr partition; imgtool::directory::ptr imageenum; imgtool_dirent ent; float current_result; result = 0.0; err = imgtool::image::open(module, fname, OSD_FOPEN_READ, image); if (err) goto done; if (image) { current_result = module->open_is_strict ? 0.9 : 0.5; err = imgtool::partition::open(*image, 0, partition); if (err) goto done; err = imgtool::directory::open(*partition, "", imageenum); if (err) goto done; memset(&ent, 0, sizeof(ent)); do { err = imageenum->get_next(ent); if (err) goto done; if (ent.corrupt) current_result = (current_result * 99 + 1.00f) / 100; else current_result = (current_result + 1.00f) / 2; } while(!ent.eof); result = current_result; } done: if (ERRORCODE(err) == IMGTOOLERR_CORRUPTIMAGE) err = IMGTOOLERR_SUCCESS; return err; } //------------------------------------------------- // identify_file - attempts to determine the module // for any given image //------------------------------------------------- imgtoolerr_t imgtool::image::identify_file(const char *fname, imgtool_module **modules, size_t count) { imgtoolerr_t err = IMGTOOLERR_SUCCESS; imgtool::library &library = *global_imgtool_library.get(); imgtool_module *insert_module; imgtool_module *temp_module; size_t i = 0; const char *extension; float val, temp_val; std::unique_ptr values; if (count <= 0) return IMGTOOLERR_UNEXPECTED; for (i = 0; i < count; i++) modules[i] = nullptr; if (count > 1) count--; /* null terminate */ try { values = std::make_unique(count); } catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } for (i = 0; i < count; i++) values[i] = 0.0; /* figure out the file extension, if any */ extension = strrchr(fname, '.'); if (extension) extension++; /* iterate through all modules */ for (const auto &module : library.modules()) { if (!extension || image_find_extension(module->extensions, extension)) { err = evaluate_module(fname, module.get(), val); if (err) return err; insert_module = module.get(); for (i = 0; (val > 0.0f) && (i < count); i++) { if (val > values[i]) { temp_val = values[i]; temp_module = modules[i]; values[i] = val; modules[i] = insert_module; val = temp_val; insert_module = temp_module; } } } } if (!modules[0]) return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE); return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // get_geometry - gets the geometry // of an image; note that this may disagree with // particular sectors; this is a common copy // protection scheme //------------------------------------------------- imgtoolerr_t imgtool::image::get_geometry(uint32_t *tracks, uint32_t *heads, uint32_t *sectors) { uint32_t dummy; /* some sanitization, to make the callbacks easier to implement */ if (!tracks) tracks = &dummy; if (!heads) heads = &dummy; if (!sectors) sectors = &dummy; *tracks = 0; *heads = 0; *sectors = 0; /* implemented? */ if (!module().get_geometry) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); return module().get_geometry(*this, tracks, heads, sectors); } //------------------------------------------------- // read_sector - reads a sector on an image //------------------------------------------------- imgtoolerr_t imgtool::image::read_sector(uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer) { // implemented? if (!module().read_sector) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); return module().read_sector(*this, track, head, sector, buffer); } //------------------------------------------------- // write_sector - writes a sector on an image //------------------------------------------------- imgtoolerr_t imgtool::image::write_sector(uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len) { // implemented? if (!module().write_sector) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); return module().write_sector(*this, track, head, sector, buffer, len); } //------------------------------------------------- // get_block_size - gets the size of a standard // block on an image //------------------------------------------------- imgtoolerr_t imgtool::image::get_block_size(uint32_t &length) { // implemented? if (module().block_size == 0) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); length = module().block_size; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // read_block - reads a standard block on an image //------------------------------------------------- imgtoolerr_t imgtool::image::read_block(uint64_t block, void *buffer) { // implemented? if (!module().read_block) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); return module().read_block(*this, buffer, block); } //------------------------------------------------- // write_block - writes a standard block on an image //------------------------------------------------- imgtoolerr_t imgtool::image::write_block(uint64_t block, const void *buffer) { // implemented? if (!module().write_block) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); return module().write_block(*this, buffer, block); } //------------------------------------------------- // clear_block - clears a standard block on an image //------------------------------------------------- imgtoolerr_t imgtool::image::clear_block(uint64_t block, uint8_t data) { imgtoolerr_t err; uint8_t *block_data = nullptr; uint32_t length; err = get_block_size(length); if (err) goto done; block_data = (uint8_t*)malloc(length); if (!block_data) { err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; goto done; } memset(block_data, data, length); err = write_block(block, block_data); if (err) goto done; done: if (block_data) free(block_data); return err; } //------------------------------------------------- // list_partitions - lists the partitions on an image //------------------------------------------------- imgtoolerr_t imgtool::image::list_partitions(std::vector &partitions) { imgtoolerr_t err; // clear out partitions first partitions.clear(); // implemented? if (module().list_partitions) { // if so, call the module's callback err = module().list_partitions(*this, partitions); if (err) return err; } else { // default implementation partitions.emplace_back(module().imgclass, 0, ~0); } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // malloc - allocates memory associated with an image //------------------------------------------------- void *imgtool::image::malloc(size_t size) { return pool_malloc_lib(m_pool, size); } /*************************************************************************** Imgtool partition management ***************************************************************************/ //------------------------------------------------- // imgtool::partition ctor //------------------------------------------------- imgtool::partition::partition(imgtool::image &image, const imgtool_class &imgclass, int partition_index, uint64_t base_block, uint64_t block_count) : m_image(image) , m_base_block(base_block) , m_block_count(block_count) , m_imgclass(imgclass) { // does this partition type have extra bytes? size_t extra_bytes_size = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES); if (extra_bytes_size > 0) { m_extra_bytes = std::make_unique(extra_bytes_size); memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0]) * extra_bytes_size); } m_directory_extra_bytes = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES); m_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PATH_SEPARATOR); m_alternate_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR); m_prefer_ucase = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PREFER_UCASE) ? 1 : 0; m_supports_creation_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME) ? 1 : 0; m_supports_lastmodified_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME) ? 1 : 0; m_supports_bootblock = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK) ? 1 : 0; m_begin_enum = (imgtoolerr_t(*)(imgtool::directory &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_BEGIN_ENUM); m_next_enum = (imgtoolerr_t(*)(imgtool::directory &, imgtool_dirent &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_NEXT_ENUM); m_close_enum = (void(*)(imgtool::directory &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CLOSE_ENUM); m_free_space = (imgtoolerr_t(*)(imgtool::partition &, uint64_t *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_FREE_SPACE); m_read_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_READ_FILE); m_write_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &, util::option_resolution *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_WRITE_FILE); m_delete_file = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_FILE); m_list_forks = (imgtoolerr_t(*)(imgtool::partition &, const char *, std::vector &forks)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_FORKS); m_create_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_DIR); m_delete_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_DIR); m_list_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, uint32_t *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_ATTRS); m_get_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ATTRS); m_set_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, const imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SET_ATTRS); m_attr_name = (imgtoolerr_t(*)(uint32_t, const imgtool_attribute *, char *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_ATTR_NAME); m_get_iconinfo = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_iconinfo *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ICON_INFO); m_suggest_transfer = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_transfer_suggestion *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SUGGEST_TRANSFER); m_get_chain = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_chainent *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_CHAIN); m_writefile_optguide = (const util::option_guide *) imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE); const char *writefile_optspec = (const char *)imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_STR_WRITEFILE_OPTSPEC); if (writefile_optspec) m_writefile_optspec.assign(writefile_optspec); // mask out if writing is untested if (global_omit_untested && imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_WRITING_UNTESTED)) { m_write_file = nullptr; m_delete_file = nullptr; m_create_dir = nullptr; m_delete_dir = nullptr; m_writefile_optguide = nullptr; m_writefile_optspec = nullptr; } } //------------------------------------------------- // imgtool::partition dtor //------------------------------------------------- imgtool::partition::~partition() { } //------------------------------------------------- // open - opens a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::open(imgtool::image &image, int partition_index, imgtool::partition::ptr &partition) { imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; imgtool::partition::ptr p; std::vector partitions; imgtoolerr_t (*open_partition)(imgtool::partition &partition, uint64_t first_block, uint64_t block_count); // list the partitions err = image.list_partitions(partitions); if (err) return err; // is this an invalid index? if ((partition_index < 0) || (partition_index >= partitions.size()) || (!partitions[partition_index].imgclass().get_info && !partitions[partition_index].imgclass().derived_get_info)) return IMGTOOLERR_INVALIDPARTITION; // use this partition const imgtool_class &imgclass(partitions[partition_index].imgclass()); uint64_t base_block = partitions[partition_index].base_block(); uint64_t block_count = partitions[partition_index].block_count(); // allocate the new partition object try { p = std::make_unique(image, imgclass, partition_index, base_block, block_count); } catch (std::bad_alloc const &) { err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; goto done; } // call the partition open function, if present open_partition = (imgtoolerr_t (*)(imgtool::partition &, uint64_t, uint64_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_OPEN_PARTITION); if (open_partition) { /* we have an open partition function */ err = (*open_partition)(*p, base_block, block_count); if (err) goto done; } done: if (!err) partition = std::move(p); else partition.reset(); return err; } /*************************************************************************** Imgtool partition operations ***************************************************************************/ //------------------------------------------------- // get_attribute_name - retrieves the human readable // name for an attribute //------------------------------------------------- void imgtool::partition::get_attribute_name(uint32_t attribute, const imgtool_attribute *attr_value, char *buffer, size_t buffer_len) { imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED; buffer[0] = '\0'; if (attr_value) { if (m_attr_name) err = m_attr_name(attribute, attr_value, buffer, buffer_len); if (err == (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED) { switch(attribute & 0xF0000) { case IMGTOOLATTR_INT_FIRST: snprintf(buffer, buffer_len, "%d", (int) attr_value->i); break; } } } else { switch(attribute) { case IMGTOOLATTR_INT_MAC_TYPE: snprintf(buffer, buffer_len, "File type"); break; case IMGTOOLATTR_INT_MAC_CREATOR: snprintf(buffer, buffer_len, "File creator"); break; case IMGTOOLATTR_INT_MAC_FINDERFLAGS: snprintf(buffer, buffer_len, "Finder flags"); break; case IMGTOOLATTR_INT_MAC_COORDX: snprintf(buffer, buffer_len, "X coordinate"); break; case IMGTOOLATTR_INT_MAC_COORDY: snprintf(buffer, buffer_len, "Y coordinate"); break; case IMGTOOLATTR_INT_MAC_FINDERFOLDER: snprintf(buffer, buffer_len, "Finder folder"); break; case IMGTOOLATTR_INT_MAC_ICONID: snprintf(buffer, buffer_len, "Icon ID"); break; case IMGTOOLATTR_INT_MAC_SCRIPTCODE: snprintf(buffer, buffer_len, "Script code"); break; case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: snprintf(buffer, buffer_len, "Extended flags"); break; case IMGTOOLATTR_INT_MAC_COMMENTID: snprintf(buffer, buffer_len, "Comment ID"); break; case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: snprintf(buffer, buffer_len, "Putaway directory"); break; case IMGTOOLATTR_TIME_CREATED: snprintf(buffer, buffer_len, "Creation time"); break; case IMGTOOLATTR_TIME_LASTMODIFIED: snprintf(buffer, buffer_len, "Last modified time"); break; } } } //------------------------------------------------- // test_imgtool_datetime - unit test for imgtool::datetime //------------------------------------------------- static bool test_imgtool_datetime(int second, int minute, int hour, int day_of_month, int month, int year) { bool error = false; util::arbitrary_datetime t; t.second = second; t.minute = minute; t.hour = hour; t.day_of_month = day_of_month; t.month = month; t.year = year; imgtool::datetime dt(imgtool::datetime::datetime_type::GMT, t); std::tm t2 = dt.gmtime(); if (t2.tm_sec != second) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_sec to be %d, instead got %d\n", second, t2.tm_sec); error = true; } if (t2.tm_min != minute) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_min to be %d, instead got %d\n", minute, t2.tm_min); error = true; } if (t2.tm_hour != hour) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_hour to be %d, instead got %d\n", hour, t2.tm_hour); error = true; } if (t2.tm_mday != day_of_month) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mday to be %d, instead got %d\n", day_of_month, t2.tm_mday); error = true; } if (t2.tm_mon != month - 1) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", month - 1, t2.tm_mon); error = true; } if (t2.tm_year != year - 1900) { util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", year - 1900, t2.tm_year); error = true; } return error; } //------------------------------------------------- // test_imgtool_datetime - unit tests for imgtool::datetime //------------------------------------------------- static bool test_imgtool_datetime() { bool error = false; // various test cases for imgtool::datetime if (test_imgtool_datetime(34, 23, 12, 18, 3, 1993)) // March 18th, 1993 12:23:34 error = true; if (test_imgtool_datetime(0, 20, 16, 25, 12, 1976)) // December 25th, 1976 16:20:00 error = true; return error; } //------------------------------------------------- // imgtool_validitychecks - checks the validity // of the imgtool modules //------------------------------------------------- bool imgtool_validitychecks(void) { bool error = false; imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; imgtool_module_features features; int created_library = false; // various test cases for imgtool::datetime if (test_imgtool_datetime()) error = true; if (!global_imgtool_library) { imgtool_init(false, nullptr); created_library = true; } for (const auto &module : global_imgtool_library->modules()) { features = imgtool_get_module_features(module.get()); if (!module->name) { util::stream_format(std::wcerr, L"imgtool module %s has null 'name'\n", wstring_from_utf8(module->name)); error = true; } if (!module->description) { util::stream_format(std::wcerr, L"imgtool module %s has null 'description'\n", wstring_from_utf8(module->name)); error = true; } if (!module->extensions) { util::stream_format(std::wcerr, L"imgtool module %s has null 'extensions'\n", wstring_from_utf8(module->extensions)); error = true; } #if 0 /* sanity checks on modules that do not support directories */ if (!module->path_separator) { if (module->alternate_path_separator) { util::stream_format(std::wcerr, L"imgtool module %s specified alternate_path_separator but not path_separator\n", wstring_from_utf8(module->name)); error = true; } if (module->initial_path_separator) { util::stream_format(std::wcerr, L"imgtool module %s specified initial_path_separator without directory support\n", wstring_from_utf8(module->name)); error = true; } if (module->create_dir) { util::stream_format(std::wcerr, L"imgtool module %s implements create_dir without directory support\n", wstring_from_utf8(module->name)); error = true; } if (module->delete_dir) { util::stream_format(std::wcerr, L"imgtool module %s implements delete_dir without directory support\n", wstring_from_utf8(module->name)); error = true; } } #endif /* sanity checks on creation options */ if (module->createimage_optguide || module->createimage_optspec) { if (!module->create) { util::stream_format(std::wcerr, L"imgtool module %s has creation options without supporting create\n", wstring_from_utf8(module->name)); error = true; } if ((!module->createimage_optguide && module->createimage_optspec) || (module->createimage_optguide && !module->createimage_optspec)) { util::stream_format(std::wcerr, L"imgtool module %s does has partially incomplete creation options\n", wstring_from_utf8(module->name)); error = true; } if (module->createimage_optguide && module->createimage_optspec) { auto resolution = std::make_unique(*module->createimage_optguide); resolution->set_specification(module->createimage_optspec); } } } if (created_library) imgtool_exit(); if (err) { util::stream_format(std::wcerr, L"imgtool: %s\n", wstring_from_utf8(imgtool_error(err))); error = true; } return error; } /*------------------------------------------------- imgtool_temp_str - provides a temporary string buffer used for string passing -------------------------------------------------*/ char *imgtool_temp_str(void) { static int index; static char temp_string_pool[32][256]; return temp_string_pool[index++ % ARRAY_LENGTH(temp_string_pool)]; } /*************************************************************************** Image handling functions ***************************************************************************/ imgtoolerr_t imgtool::image::internal_open(const imgtool_module *module, const std::string &filename, int read_or_write, util::option_resolution *createopts, imgtool::image::ptr &outimg) { imgtoolerr_t err; imgtool::stream::ptr stream; imgtool::image::ptr image; object_pool *pool = nullptr; void *extra_bytes = nullptr; outimg.reset(); // is the requested functionality implemented? if ((read_or_write == OSD_FOPEN_RW_CREATE) ? !module->create : !module->open) { err = imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); goto done; } // create a memory pool pool = pool_alloc_lib(nullptr); if (!pool) { err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); goto done; } // open the stream stream = imgtool::stream::open(filename, read_or_write); if (!stream) { err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE); goto done; } // allocate extra if (module->image_extra_bytes > 0) { extra_bytes = pool_malloc_lib(pool, module->image_extra_bytes); if (!extra_bytes) { err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); goto done; } memset(extra_bytes, 0, module->image_extra_bytes); } // setup the image structure try { image = std::make_unique(*module, pool, extra_bytes); } catch (std::bad_alloc const &) { err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); goto done; } // the pool is no longer owned by this function pool = nullptr; // actually call create or open err = (read_or_write == OSD_FOPEN_RW_CREATE) ? module->create(*image, std::move(stream), createopts) : module->open(*image, std::move(stream)); if (err) { err = markerrorsource(err); goto done; } // we've succeeded - set the output image, and record that // we are "okay to close" image->m_okay_to_close = true; outimg = std::move(image); done: if (pool) pool_free_lib(pool); return err; } //------------------------------------------------- // open - open an image //------------------------------------------------- imgtoolerr_t imgtool::image::open(const imgtool_module *module, const std::string &filename, int read_or_write, ptr &outimg) { read_or_write = read_or_write ? OSD_FOPEN_RW : OSD_FOPEN_READ; return internal_open(module, filename, read_or_write, nullptr, outimg); } //------------------------------------------------- // imgtool::image::open_byname - open an image //------------------------------------------------- imgtoolerr_t imgtool::image::open(const std::string &modulename, const std::string &filename, int read_or_write, ptr &outimg) { const imgtool_module *module; module = imgtool_find_module(modulename); if (!module) return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); return open(module, filename, read_or_write, outimg); } //------------------------------------------------- // imgtool::image::image //------------------------------------------------- imgtool::image::image(const imgtool_module &module, object_pool *pool, void *extra_bytes) : m_module(module) , m_pool(pool) , m_extra_bytes(extra_bytes) , m_okay_to_close(false) { } //------------------------------------------------- // imgtool::image::~image //------------------------------------------------- imgtool::image::~image() { if (m_okay_to_close && module().close) module().close(*this); pool_free_lib(m_pool); } //------------------------------------------------- // create - creates an image //------------------------------------------------- imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts, ptr &image) { std::unique_ptr alloc_resolution; /* allocate dummy options if necessary */ if (!opts && module->createimage_optguide) { try { alloc_resolution.reset(new util::option_resolution(*module->createimage_optguide)); } catch (...) { return (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; } if (module->createimage_optspec) alloc_resolution->set_specification(module->createimage_optspec); opts = alloc_resolution.get(); } return internal_open(module, filename, OSD_FOPEN_RW_CREATE, opts, image); } //------------------------------------------------- // create - creates an image //------------------------------------------------- imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts, ptr &image) { const imgtool_module *module; module = imgtool_find_module(modulename); if (!module) return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); return create(module, filename, opts, image); } //------------------------------------------------- // create - creates an image //------------------------------------------------- imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts) { std::unique_ptr image; return create(module, filename, opts, image); } //------------------------------------------------- // create - creates an image //------------------------------------------------- imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts) { std::unique_ptr image; return create(modulename, filename, opts, image); } //------------------------------------------------- // info - returns format specific information about an image //------------------------------------------------- std::string imgtool::image::info() { std::string string; if (module().info) { std::stringstream stream; module().info(*this, stream); string = stream.str(); } return string; } #define PATH_MUSTBEDIR 0x00000001 #define PATH_LEAVENULLALONE 0x00000002 #define PATH_CANBEBOOTBLOCK 0x00000004 //------------------------------------------------- // partition::cannonicalize_path - normalizes a path string // into a NUL delimited list //------------------------------------------------- imgtoolerr_t imgtool::partition::cannonicalize_path(uint32_t flags, const char *path, std::string &result) { imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; // is this path NULL? if so, is that ignored? if (!path && (flags & PATH_LEAVENULLALONE)) return IMGTOOLERR_SUCCESS; // is this the special filename for bootblocks? if (path == FILENAME_BOOTBLOCK) { if (!(flags & PATH_CANBEBOOTBLOCK)) err = (imgtoolerr_t)IMGTOOLERR_UNEXPECTED; else if (!m_supports_bootblock) err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; return err; } // normalize the path into the native character set std::string converted_path; imgtool::charconverter * const converter = reinterpret_cast(get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER)); if (converter) { converted_path = converter->from_utf8(path); path = converted_path.c_str(); } if (m_path_separator == '\0') { if (flags & PATH_MUSTBEDIR) { // do we specify a path when paths are not supported? */ if (path && *path) return imgtoolerr_t(IMGTOOLERR_CANNOTUSEPATH | IMGTOOLERR_SRC_FUNCTIONALITY); result = ""; /* normalize empty path */ } else { // simple passthrough result = path; } } else { const char *s = path ? path : ""; // allocate space for a new cannonical path std::ostringstream stream; // copy the path bool in_path_separator = true; size_t i = 0; do { if ((s[i] != '\0') && (s[i] != m_path_separator) && (s[i] != m_alternate_path_separator)) { stream << s[i]; in_path_separator = false; } else if (!in_path_separator) { stream << '\0'; in_path_separator = true; } } while(s[i++] != '\0'); stream << '\0' << '\0'; result = stream.str(); } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::cannonicalize_fork //------------------------------------------------- imgtoolerr_t imgtool::partition::cannonicalize_fork(const char **fork) { // does this module support forks? if (m_list_forks) { // this module supports forks; make sure that fork is non-NULL if (!*fork) *fork = ""; } else { // this module does not support forks; make sure that fork is NULL if (*fork) return IMGTOOLERR_NOFORKS; } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_directory_entry - retrieves // the nth directory entry within a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_directory_entry(const char *path, int index, imgtool_dirent &ent) { imgtoolerr_t err; imgtool::directory::ptr imgenum; if (index < 0) { err = (imgtoolerr_t)IMGTOOLERR_PARAMTOOSMALL; goto done; } err = imgtool::directory::open(*this, path, imgenum); if (err) goto done; do { err = imgenum->get_next(ent); if (err) goto done; if (ent.eof) { err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; goto done; } } while(index--); done: if (err) memset(ent.filename, 0, sizeof(ent.filename)); return err; } //------------------------------------------------- // partition::get_file_size - returns free // space on a partition, in bytes //------------------------------------------------- imgtoolerr_t imgtool::partition::get_file_size(const char *fname, uint64_t &filesize) { imgtoolerr_t err; imgtool::directory::ptr imgenum; imgtool_dirent ent; const char *path; path = nullptr; /* TODO: Need to parse off the path */ filesize = ~((uint64_t) 0); memset(&ent, 0, sizeof(ent)); err = imgtool::directory::open(*this, path, imgenum); if (err) goto done; do { err = imgenum->get_next(ent); if (err) goto done; if (!core_stricmp(fname, ent.filename)) { filesize = ent.filesize; goto done; } } while(ent.filename[0]); err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; done: return err; } //------------------------------------------------- // partition::list_file_attributes - identifies // all attributes on a file //------------------------------------------------- imgtoolerr_t imgtool::partition::list_file_attributes(const char *path, uint32_t *attrs, size_t len) { imgtoolerr_t err; memset(attrs, 0, sizeof(*attrs) * len); if (!m_list_attrs) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path); if (err) return err; err = m_list_attrs(*this, cannonical_path.c_str(), attrs, len); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_file_attributes - retrieves // attributes on a file //------------------------------------------------- imgtoolerr_t imgtool::partition::get_file_attributes(const char *path, const uint32_t *attrs, imgtool_attribute *values) { imgtoolerr_t err; if (!m_get_attrs) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path); if (err) return err; err = m_get_attrs(*this, cannonical_path.c_str(), attrs, values); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // imgtool::partition::put_file_attributes - sets // attributes on a file //------------------------------------------------- imgtoolerr_t imgtool::partition::put_file_attributes(const char *path, const uint32_t *attrs, const imgtool_attribute *values) { imgtoolerr_t err; if (!m_set_attrs) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path); if (err) return err; err = m_set_attrs(*this, cannonical_path.c_str(), attrs, values); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_file_attribute - retrieves // an attribute on a single file //------------------------------------------------- imgtoolerr_t imgtool::partition::get_file_attribute(const char *path, uint32_t attr, imgtool_attribute &value) { uint32_t attrs[2]; attrs[0] = attr; attrs[1] = 0; return get_file_attributes(path, attrs, &value); } //------------------------------------------------- // partition::put_file_attribute - sets // attributes on a single file //------------------------------------------------- imgtoolerr_t imgtool::partition::put_file_attribute(const char *path, uint32_t attr, const imgtool_attribute &value) { uint32_t attrs[2]; attrs[0] = attr; attrs[1] = 0; return put_file_attributes(path, attrs, &value); } //------------------------------------------------- // partition::get_icon_info - retrieves the // icon for a file stored on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_icon_info(const char *path, imgtool_iconinfo *iconinfo) { imgtoolerr_t err; if (!m_get_iconinfo) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(0, path, cannonical_path); if (err) return err; memset(iconinfo, 0, sizeof(*iconinfo)); err = m_get_iconinfo(*this, cannonical_path.c_str(), iconinfo); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::suggest_file_filters - suggests a // list of filters appropriate for a file on a // partition //------------------------------------------------- imgtoolerr_t imgtool::partition::suggest_file_filters(const char *path, imgtool::stream *stream, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) { imgtoolerr_t err; int i, j; imgtoolerr_t (*check_stream)(imgtool::stream &stream, imgtool_suggestion_viability_t *viability); size_t position; // clear out buffer memset(suggestions, 0, sizeof(*suggestions) * suggestions_length); if (!m_suggest_transfer) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_LEAVENULLALONE, path, cannonical_path); if (err) return err; // invoke the module's suggest call err = m_suggest_transfer(*this, cannonical_path.c_str(), suggestions, suggestions_length); if (err) return err; // loop on resulting suggestions, and do the following: // 1. Call check_stream if present, and remove disqualified streams // 2. Fill in missing descriptions i = j = 0; while(suggestions[i].viability) { if (stream && suggestions[i].filter) { check_stream = (imgtoolerr_t (*)(imgtool::stream &, imgtool_suggestion_viability_t *)) filter_get_info_fct(suggestions[i].filter, FILTINFO_PTR_CHECKSTREAM); if (check_stream) { position = stream->tell(); err = check_stream(*stream, &suggestions[i].viability); stream->seek(position, SEEK_SET); if (err) return err; } } /* the check_stream proc can remove the option by clearing out the viability */ if (suggestions[i].viability) { /* we may have to move this suggestion, if one was removed */ if (i != j) memcpy(&suggestions[j], &suggestions[i], sizeof(*suggestions)); /* if the description is missing, fill it in */ if (!suggestions[j].description) { if (suggestions[j].filter) suggestions[j].description = filter_get_info_string(suggestions[i].filter, FILTINFO_STR_HUMANNAME); else suggestions[j].description = "Raw"; } j++; } i++; } suggestions[j].viability = (imgtool_suggestion_viability_t)0; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_chain - retrieves the block // chain for a file or directory on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_chain(const char *path, imgtool_chainent *chain, size_t chain_size) { size_t i; assert(chain_size > 0); if (!m_get_chain) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // initialize the chain array, so the module's get_chain function can be lazy for (i = 0; i < chain_size; i++) { chain[i].level = 0; chain[i].block = ~0; } return m_get_chain(*this, path, chain, chain_size - 1); } //------------------------------------------------- // partition::get_chain_string - retrieves // the block chain for a file or directory on a // partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_chain_string(const char *path, char *buffer, size_t buffer_len) { imgtoolerr_t err; imgtool_chainent chain[512]; uint64_t last_block; uint8_t cur_level = 0; int len, i; int comma_needed = false; // determine the last block identifier chain[0].block = ~0; last_block = chain[0].block; err = get_chain(path, chain, ARRAY_LENGTH(chain)); if (err) return err; len = snprintf(buffer, buffer_len, "["); buffer += len; buffer_len -= len; for (i = 0; chain[i].block != last_block; i++) { while(cur_level < chain[i].level) { len = snprintf(buffer, buffer_len, " ["); buffer += len; buffer_len -= len; cur_level++; comma_needed = false; } while(cur_level > chain[i].level) { len = snprintf(buffer, buffer_len, "]"); buffer += len; buffer_len -= len; cur_level--; } if (comma_needed) { len = snprintf(buffer, buffer_len, ", "); buffer += len; buffer_len -= len; } len = snprintf(buffer, buffer_len, "%u", (unsigned) chain[i].block); buffer += len; buffer_len -= len; comma_needed = true; } do { len = snprintf(buffer, buffer_len, "]"); buffer += len; buffer_len -= len; } while(cur_level-- > 0); return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_free_space - returns the // amount of free space on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_free_space(uint64_t &sz) { imgtoolerr_t err; if (!m_free_space) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); err = m_free_space(*this, &sz); if (err) return (imgtoolerr_t)(err | IMGTOOLERR_SRC_IMAGEFILE); return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::read_file - starts reading // from a file on a partition with a stream //------------------------------------------------- imgtoolerr_t imgtool::partition::read_file(const char *filename, const char *fork, imgtool::stream &destf, filter_getinfoproc filter) { imgtoolerr_t err; union filterinfo u; if (!m_read_file) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); if (filter) { // use a filter filter(FILTINFO_PTR_READFILE, &u); if (!u.read_file) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); err = u.read_file(*this, filename, fork, destf); if (err) return markerrorsource(err); } else { // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_CANBEBOOTBLOCK, filename, cannonical_path); if (err) return err; err = cannonicalize_fork(&fork); if (err) return err; // invoke the actual module err = m_read_file(*this, cannonical_path.c_str(), fork, destf); if (err) return markerrorsource(err); } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::write_file - starts writing // to a new file on an image with a stream //------------------------------------------------- imgtoolerr_t imgtool::partition::write_file(const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts, filter_getinfoproc filter) { imgtoolerr_t err; std::unique_ptr alloc_resolution; uint64_t free_space; uint64_t file_size; union filterinfo u; if (!m_write_file) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); if (filter) { // use a filter filter(FILTINFO_PTR_WRITEFILE, &u); if (!u.write_file) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); err = u.write_file(*this, filename, fork, sourcef, opts); if (err) return markerrorsource(err); } else { // does this partition prefer upper case file names? std::string ucase_str; if (m_prefer_ucase) { std::ostringstream s; size_t i = 0; while (filename[i]) { char32_t ch; int count = uchar_from_utf8(&ch, &filename[i], UTF8_CHAR_MAX); if (count > 0) { char32_t upper_ch = uchar_toupper(ch); s << utf8_from_uchar(upper_ch); } i += count > 0 ? count : 1; } ucase_str = s.str(); filename = ucase_str.c_str(); } // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_CANBEBOOTBLOCK, filename, cannonical_path); if (err) return err; err = cannonicalize_fork(&fork); if (err) return err; // allocate dummy options if necessary if (!opts && m_writefile_optguide) { try { alloc_resolution.reset(new util::option_resolution(*m_writefile_optguide)); } catch (...) { return IMGTOOLERR_OUTOFMEMORY; } if (!m_writefile_optspec.empty()) alloc_resolution->set_specification(m_writefile_optspec); opts = alloc_resolution.get(); } // if free_space is implemented; do a quick check to see if space is available if (m_free_space) { err = m_free_space(*this, &free_space); if (err) return markerrorsource(err); file_size = sourcef.size(); if (file_size > free_space) return markerrorsource(IMGTOOLERR_NOSPACE); } // actually invoke the write file handler err = m_write_file(*this, cannonical_path.c_str(), fork, sourcef, opts); if (err) return markerrorsource(err); } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_file - read a file from // an image, storing it into a native file //------------------------------------------------- imgtoolerr_t imgtool::partition::get_file(const char *filename, const char *fork, const char *dest, filter_getinfoproc filter) { imgtoolerr_t err; imgtool::stream::ptr f; char *alloc_dest = nullptr; const char *filter_extension = nullptr; if (!dest) { // determine the filter extension, if appropriate if (filter != nullptr) filter_extension = filter_get_info_string(filter, FILTINFO_STR_EXTENSION); if (filter_extension != nullptr) { alloc_dest = (char*)malloc(strlen(filename) + 1 + strlen(filter_extension) + 1); if (!alloc_dest) return IMGTOOLERR_OUTOFMEMORY; sprintf(alloc_dest, "%s.%s", filename, filter_extension); dest = alloc_dest; } else { if (filename == FILENAME_BOOTBLOCK) dest = "boot.bin"; else dest = filename; } } f = imgtool::stream::open(dest, OSD_FOPEN_WRITE); if (!f) { err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); goto done; } err = read_file(filename, fork, *f, filter); done: if (alloc_dest != nullptr) free(alloc_dest); return err; } //------------------------------------------------- // partition::put_file - read a native file // and store it on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::put_file(const char *newfname, const char *fork, const char *source, util::option_resolution *opts, filter_getinfoproc filter) { imgtoolerr_t err; imgtool::stream::ptr f; std::string alloc_newfname; std::string basename; if (!newfname) { basename = core_filename_extract_base(source); newfname = basename.c_str(); } imgtool::charconverter *charconverter = (imgtool::charconverter *) get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER); if (charconverter) { // convert to native format try { alloc_newfname = charconverter->from_utf8(newfname); } catch (charconverter_exception) { return imgtoolerr_t(IMGTOOLERR_BADFILENAME | IMGTOOLERR_SRC_NATIVEFILE); } newfname = alloc_newfname.c_str(); } f = imgtool::stream::open(source, OSD_FOPEN_READ); if (f) err = write_file(newfname, fork, *f, opts, filter); else err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); return err; } /*------------------------------------------------- imgtool::partition::delete_file - delete a file on a partition -------------------------------------------------*/ imgtoolerr_t imgtool::partition::delete_file(const char *fname) { imgtoolerr_t err; if (!m_delete_file) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(0, fname, cannonical_path); if (err) return err; err = m_delete_file(*this, cannonical_path.c_str()); if (err) return markerrorsource(err); return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::list_file_forks - lists all // forks on an image //------------------------------------------------- imgtoolerr_t imgtool::partition::list_file_forks(const char *path, std::vector &forks) { imgtoolerr_t err; if (!m_list_forks) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(0, path, cannonical_path); if (err) return err; // call the callback forks.clear(); err = m_list_forks(*this, cannonical_path.c_str(), forks); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::create_directory - creates a // directory on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::create_directory(const char *path) { imgtoolerr_t err; // implemented? if (!m_create_dir) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_MUSTBEDIR, path, cannonical_path); if (err) return err; err = m_create_dir(*this, path); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::delete_directory - deletes a // directory on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::delete_directory(const char *path) { imgtoolerr_t err; // implemented? if (!m_delete_dir) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); // cannonicalize path std::string cannonical_path; err = cannonicalize_path(PATH_MUSTBEDIR, path, cannonical_path); if (err) return err; err = m_delete_dir(*this, path); if (err) return err; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::get_block_size - gets the // size of a standard block on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::get_block_size(uint32_t &length) { return m_image.get_block_size(length); } //------------------------------------------------- // partition::is_block_in_range //------------------------------------------------- imgtoolerr_t imgtool::partition::map_block_to_image_block(uint64_t partition_block, uint64_t &image_block) const { if (partition_block >= m_block_count) return IMGTOOLERR_SEEKERROR; image_block = m_base_block + partition_block; return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // partition::read_block - reads a standard // block on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::read_block(uint64_t block, void *buffer) { uint64_t image_block; imgtoolerr_t err = map_block_to_image_block(block, image_block); if (err) return err; return m_image.read_block(image_block, buffer); } //------------------------------------------------- // partition::write_block - writes a // standard block on a partition //------------------------------------------------- imgtoolerr_t imgtool::partition::write_block(uint64_t block, const void *buffer) { uint64_t image_block; imgtoolerr_t err = map_block_to_image_block(block, image_block); if (err) return err; return m_image.write_block(image_block, buffer); } //------------------------------------------------- // partition::get_features - retrieves a // structure identifying this partition's features // associated with an image //------------------------------------------------- imgtool_partition_features imgtool::partition::get_features() const { imgtool_partition_features features; memset(&features, 0, sizeof(features)); if (m_read_file) features.supports_reading = 1; if (m_write_file) features.supports_writing = 1; if (m_delete_file) features.supports_deletefile = 1; if (m_path_separator) features.supports_directories = 1; if (m_free_space) features.supports_freespace = 1; if (m_create_dir) features.supports_createdir = 1; if (m_delete_dir) features.supports_deletedir = 1; if (m_supports_creation_time) features.supports_creation_time = 1; if (m_supports_lastmodified_time) features.supports_lastmodified_time = 1; if (m_get_iconinfo) features.supports_geticoninfo = 1; if (!features.supports_writing && !features.supports_createdir && !features.supports_deletefile && !features.supports_deletedir) features.is_read_only = 1; return features; } //------------------------------------------------- // partition::get_info_ptr - retrieves a // pointer associated with a partition's format //------------------------------------------------- void *imgtool::partition::get_info_ptr(uint32_t state) { return imgtool_get_info_ptr(&m_imgclass, state); } //------------------------------------------------- // partition::get_info_string - retrieves a // string associated with a partition's format //------------------------------------------------- const char *imgtool::partition::get_info_string(uint32_t state) { return imgtool_get_info_string(&m_imgclass, state); } //------------------------------------------------- // partition::get_info_int - retrieves a // pointer associated with a partition's format //------------------------------------------------- uint64_t imgtool::partition::get_info_int(uint32_t state) { return imgtool_get_info_int(&m_imgclass, state); } //------------------------------------------------- // partition::extra_bytes - returns extra // bytes on a partition //------------------------------------------------- void *imgtool::partition::extra_bytes() { if (!m_extra_bytes) throw false; return m_extra_bytes.get(); } /*************************************************************************** Path handling functions ***************************************************************************/ //------------------------------------------------- // partition::get_root_path - retrieves // the path root of this partition //------------------------------------------------- const char *imgtool::partition::get_root_path() { int initial_path_separator; char path_separator; char *buf; int pos = 0; initial_path_separator = get_info_int(IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR) ? 1 : 0; path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); buf = imgtool_temp_str(); if (initial_path_separator) buf[pos++] = path_separator; buf[pos] = '\0'; return buf; } //------------------------------------------------- // partition::path_concatenate - retrieves // a pointer associated with a partition's format //------------------------------------------------- const char *imgtool::partition::path_concatenate(const char *path1, const char *path2) { char path_separator; size_t len; char *buffer; size_t buffer_len; path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); len = strlen(path1); /* prepare buffer */ buffer = imgtool_temp_str(); buffer_len = 256; if (!strcmp(path2, ".")) { /* keep the same directory */ snprintf(buffer, buffer_len, "%s", path1); } else if (!strcmp(path2, "..")) { /* up one directory */ while((len > 0) && (path1[len - 1] == path_separator)) len--; while((len > 0) && (path1[len - 1] != path_separator)) len--; while((len > 1) && (path1[len - 1] == path_separator)) len--; snprintf(buffer, buffer_len, "%s", path1); buffer[len] = '\0'; } else { /* append a path */ if ((*path1 != 0) && (path1[strlen(path1) - 1] != path_separator)) snprintf(buffer, buffer_len, "%s%c%s", path1, path_separator, path2); else snprintf(buffer, buffer_len, "%s%s", path1, path2); } return buffer; } //------------------------------------------------- // partition::get_base_name - retrieves // a base name for a partition specific path //------------------------------------------------- const char *imgtool::partition::get_base_name(const char *path) { char path_separator; const char *new_path = path; int i; path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); for (i = 0; path[i]; i++) { if (path[i] == path_separator) new_path = &path[i + 1]; } return new_path; } /*************************************************************************** Directory handling functions ***************************************************************************/ //------------------------------------------------- // directory ctor //------------------------------------------------- imgtool::directory::directory(imgtool::partition &partition) : m_partition(partition) , m_okay_to_close(false) { if (partition.m_directory_extra_bytes > 0) { m_extra_bytes = std::make_unique(partition.m_directory_extra_bytes); memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0] * partition.m_directory_extra_bytes)); } } //------------------------------------------------- // directory::open - begins // enumerating files on a partition //------------------------------------------------- imgtoolerr_t imgtool::directory::open(imgtool::partition &partition, const std::string &path, imgtool::directory::ptr &outenum) { imgtoolerr_t err = imgtoolerr_t(IMGTOOLERR_SUCCESS); imgtool::directory::ptr enumeration; outenum.reset(); if (!partition.m_next_enum) return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); std::string cannonical_path; err = partition.cannonicalize_path(PATH_MUSTBEDIR, path.c_str(), cannonical_path); if (err) return err; try { enumeration = std::make_unique(partition); } catch (std::bad_alloc const &) { return imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); } if (partition.m_begin_enum) { err = partition.m_begin_enum(*enumeration, cannonical_path.c_str()); if (err) return markerrorsource(err); } enumeration->m_okay_to_close = true; outenum = std::move(enumeration); return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // directory dtor //------------------------------------------------- imgtool::directory::~directory() { if (m_okay_to_close && m_partition.m_close_enum) m_partition.m_close_enum(*this); } //------------------------------------------------- // directory::get_next - continues // enumerating files within a partition //------------------------------------------------- imgtoolerr_t imgtool::directory::get_next(imgtool_dirent &ent) { imgtoolerr_t err; // This makes it so that drivers don't have to take care of clearing // the attributes if they don't apply memset(&ent, 0, sizeof(ent)); err = m_partition.m_next_enum(*this, ent); if (err) return markerrorsource(err); imgtool::charconverter * const converter = reinterpret_cast(m_partition.get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER)); if (converter) { std::string new_fname; try { new_fname = converter->to_utf8(ent.filename); } catch (charconverter_exception) { return imgtoolerr_t(IMGTOOLERR_BADFILENAME); } snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", new_fname.c_str()); } // don't trust the module! if (!m_partition.m_supports_creation_time && (ent.creation_time.type() != imgtool::datetime::datetime_type::NONE)) { internal_error(nullptr, "next_enum() specified creation_time, which is marked as unsupported by this module"); return IMGTOOLERR_UNEXPECTED; } if (!m_partition.m_supports_lastmodified_time && (ent.lastmodified_time.type() != imgtool::datetime::datetime_type::NONE)) { internal_error(nullptr, "next_enum() specified lastmodified_time, which is marked as unsupported by this module"); return IMGTOOLERR_UNEXPECTED; } if (!m_partition.m_path_separator && ent.directory) { internal_error(nullptr, "next_enum() returned a directory, which is marked as unsupported by this module"); return IMGTOOLERR_UNEXPECTED; } return IMGTOOLERR_SUCCESS; } //------------------------------------------------- // unknown_partition_get_info - represents an // unknown partition //------------------------------------------------- void unknown_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) { switch(state) { /* --- the following bits of info are returned as NULL-terminated strings --- */ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "unknown"); break; case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Unknown partition type"); break; } }