// SPDX-FileCopyrightText: 2020 Florian Märkl // SPDX-License-Identifier: LGPL-3.0-only #include #include /* * SDB Format: * * / * /config => see config.c * /flags => see flag.c * /analysis => see analysis.c * /file => see below * offset= * blocksize= */ static void file_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file); static bool file_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res); RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file) { file_save(sdb_ns(db, "file", true), core, prj_file); rz_serialize_config_save(sdb_ns(db, "config", true), core->config); rz_serialize_flag_save(sdb_ns(db, "flags", true), core->flags); rz_serialize_analysis_save(sdb_ns(db, "analysis", true), core->analysis); rz_serialize_debug_save(sdb_ns(db, "debug", true), core->dbg); char buf[0x20]; if (snprintf(buf, sizeof(buf), "0x%" PFMT64x, core->offset) < 0) { return; } sdb_set(db, "offset", buf, 0); if (snprintf(buf, sizeof(buf), "0x%" PFMT32x, core->blocksize) < 0) { return; } sdb_set(db, "blocksize", buf, 0); } static const char *const config_exclude[] = { "dir.home", "dir.libs", "dir.magic", "dir.plugins", "dir.prefix", "dir.projects", "dir.source", "dir.tmp", "dir.types", "http.root", "pdb.symstore", "scr.color", "scr.color.args", "scr.color.bytes", "scr.color.grep", "scr.color.ops", "scr.color.pipe", "scr.interactive", // especially relevant for Cutter since it needs this to be false "scr.prompt", // especially relevant for rzpipe, otherwise loading a project might break the pipe "scr.rainbow", "scr.utf8", "scr.utf8.curvy", "ghidra.sleighhome", // also important for Cutter NULL }; RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, bool load_bin_io, RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res) { Sdb *subdb; #define SUB(ns, call) RZ_SERIALIZE_SUB_DO(db, subdb, res, ns, call, return false;) if (load_bin_io) { SUB("file", file_load(subdb, core, prj_file, res)); } SUB("config", rz_serialize_config_load(subdb, core->config, config_exclude, res)); SUB("flags", rz_serialize_flag_load(subdb, core->flags, res)); SUB("analysis", rz_serialize_analysis_load(subdb, core->analysis, res)); SUB("debug", rz_serialize_debug_load(subdb, core->dbg, res)); const char *str = sdb_get(db, "offset", 0); if (!str || !*str) { RZ_SERIALIZE_ERR(res, "missing offset in core"); return false; } core->offset = strtoull(str, NULL, 0); str = sdb_get(db, "blocksize", 0); if (!str || !*str) { RZ_SERIALIZE_ERR(res, "missing blocksize in core"); return false; } ut64 bs = strtoull(str, NULL, 0); rz_core_block_size(core, (int)bs); // handled by config already: // cfglog, cmdrepeat, cmdtimes return true; } /* these file functions are a high-level serialization of RBin and RIO, i.e. for loading the project's underlying binary. * It only supports a subset of possible RBin and RIO configurations: * - Only a single binary, loaded as a regular file * - No IO cache (if there is cache, it will be discarded on save) * - No custom IO mappings, etc. * Thus it will eventually be replaced by more in-depth serialization of RBin and RIO. * * tl;dr it's a quick and dirty thing that only saves the binary's filename. * * SDB Format: * /file * raw= * absolute= * relative= */ static char *prj_dir_abs(const char *prj_file) { char *prj_abs = rz_file_abspath(prj_file); if (!prj_abs) { return NULL; } char *r = rz_file_dirname(prj_abs); free(prj_abs); return r; } // (absolute) local filepath => project-relative platform-agnostic path static char *prj_relative_make(const char *prj_dir, const char *abs_file) { char *rel = rz_file_relpath(prj_dir, abs_file); if (!rel) { return NULL; } // convert only the relative path to a common format because it is the only path that makes any sense // when a project file is relocated to another machine. Absolute and raw are always specific to one environment. char *rel_unix = rz_file_path_local_to_unix(rel); free(rel); return rel_unix; } // project-relative platform-agnostic path => (absolute) local filepath static char *prj_relative_restore(const char *prj_dir, const char *rel_file) { char *rel_local = rz_file_path_unix_to_local(rel_file); if (!rel_local) { return NULL; } char *abs = rz_file_abspath_rel(prj_dir, rel_local); free(rel_local); return abs; } static void file_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file) { rz_return_if_fail(db && core); if (!core->file) { return; } RzIODesc *desc = rz_io_desc_get(core->io, core->file->fd); if (!desc) { return; } if (!desc->plugin || strcmp(desc->plugin->name, "default")) { RZ_LOG_WARN("core: The current file is not loaded as a regular file. " "This is not supported in projects yet and it will be necessary to manually re-load to use the project.\n"); return; } const char *filename = desc->uri; if (!filename) { return; } sdb_set(db, "raw", filename, 0); char *abs = rz_file_abspath(filename); if (!abs) { return; } sdb_set(db, "absolute", abs, 0); if (prj_file) { char *prj_dir = prj_dir_abs(prj_file); if (!prj_dir) { goto beach; } char *rel = prj_relative_make(prj_dir, abs); if (rel) { sdb_set(db, "relative", rel, 0); free(rel); } free(prj_dir); } beach: free(abs); return; } typedef enum { FILE_SUCCESS, FILE_DOES_NOT_EXIST, FILE_LOAD_FAIL } FileRet; static FileRet try_load_file(RZ_NONNULL RzCore *core, const char *file, RZ_NULLABLE RzSerializeResultInfo *res) { if (!rz_file_is_regular(file)) { return FILE_DOES_NOT_EXIST; } RzCoreFile *fh = rz_core_file_open(core, file, RZ_PERM_RX, 0); if (!fh) { RZ_SERIALIZE_ERR(res, "failed re-open file \"%s\" referenced by project", file); return FILE_LOAD_FAIL; } rz_core_bin_load(core, file, UT64_MAX); return FILE_SUCCESS; } static bool file_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res) { rz_core_file_close_fd(core, -1); rz_io_close_all(core->io); rz_bin_file_delete_all(core->bin); FileRet r = FILE_DOES_NOT_EXIST; const char *rel = sdb_const_get(db, "relative", 0); if (rel && prj_file) { char *prj_dir = prj_dir_abs(prj_file); if (prj_dir) { char *file = prj_relative_restore(prj_dir, rel); if (file) { r = try_load_file(core, file, res); free(file); } free(prj_dir); } } if (r != FILE_DOES_NOT_EXIST) { return r == FILE_SUCCESS; } const char *file = sdb_const_get(db, "absolute", 0); if (file) { r = try_load_file(core, file, res); } if (r != FILE_DOES_NOT_EXIST) { return r == FILE_SUCCESS; } file = sdb_const_get(db, "raw", 0); if (file) { r = try_load_file(core, file, res); } if (r != FILE_DOES_NOT_EXIST) { return r == FILE_SUCCESS; } RZ_SERIALIZE_ERR(res, "failed to re-locate file referenced by project"); return false; }