rizin/librz/core/project.c

195 lines
5.2 KiB
C

// SPDX-FileCopyrightText: 2020-2021 Florian Märkl <info@florianmaerkl.de>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_project.h>
#define RZ_PROJECT_KEY_TYPE "type"
#define RZ_PROJECT_KEY_VERSION "version"
#define RZ_PROJECT_TYPE "rizin rz-db project"
RZ_API RZ_NONNULL const char *rz_project_err_message(RzProjectErr err) {
switch (err) {
case RZ_PROJECT_ERR_SUCCESS:
return "success";
case RZ_PROJECT_ERR_FILE:
return "file access error";
case RZ_PROJECT_ERR_INVALID_TYPE:
return "invalid file type";
case RZ_PROJECT_ERR_INVALID_VERSION:
return "invalid project version";
case RZ_PROJECT_ERR_NEWER_VERSION:
return "newer project version";
case RZ_PROJECT_ERR_INVALID_CONTENTS:
return "invalid content encountered";
case RZ_PROJECT_ERR_MIGRATION_FAILED:
return "migration failed";
case RZ_PROJECT_ERR_COMPRESSION_FAILED:
return "project file compression failed";
case RZ_PROJECT_ERR_UNKNOWN:
break;
}
return "unknown error";
}
RZ_API RzProjectErr rz_project_save(RzCore *core, RzProject *prj, const char *file) {
char projver[32];
sdb_set(prj, RZ_PROJECT_KEY_TYPE, RZ_PROJECT_TYPE);
sdb_set(prj, RZ_PROJECT_KEY_VERSION, rz_strf(projver, "%u", RZ_PROJECT_VERSION));
rz_serialize_core_save(sdb_ns(prj, "core", true), core, file);
return RZ_PROJECT_ERR_SUCCESS;
}
RZ_API RzProjectErr rz_project_save_file(RzCore *core, const char *file, bool compress) {
char *tmp_file = NULL;
if (compress) {
int mkstemp_fd = rz_file_mkstemp("svprj", &tmp_file);
if (mkstemp_fd == -1 || !tmp_file) {
return RZ_PROJECT_ERR_FILE;
}
close(mkstemp_fd);
}
RzProjectErr err;
const char *save_file = compress ? tmp_file : file;
RzProject *prj = sdb_new0();
if (!prj) {
err = RZ_PROJECT_ERR_UNKNOWN;
goto tmp_file_err;
}
err = rz_project_save(core, prj, file);
if (err != RZ_PROJECT_ERR_SUCCESS) {
sdb_free(prj);
return err;
}
if (!sdb_text_save(prj, save_file, true)) {
err = RZ_PROJECT_ERR_FILE;
}
sdb_free(prj);
if (err != RZ_PROJECT_ERR_SUCCESS) {
goto tmp_file_err;
}
if (compress && !rz_file_deflate(tmp_file, file)) {
err = RZ_PROJECT_ERR_COMPRESSION_FAILED;
goto tmp_file_err;
}
rz_config_set(core->config, "prj.file", file);
tmp_file_err:
rz_file_rm(tmp_file);
free(tmp_file);
return err;
}
/// Load a file into an RzProject but don't actually migrate anything or load it into an RzCore
RZ_API RzProject *rz_project_load_file_raw(const char *file) {
RzProject *prj = sdb_new0();
if (!prj) {
return NULL;
}
char *tmp_file;
int mkstemp_fd = rz_file_mkstemp("ldprj", &tmp_file);
close(mkstemp_fd);
if (mkstemp_fd == -1 || !tmp_file) {
free(tmp_file);
return NULL;
}
const char *load_file = tmp_file;
if (!rz_file_exists(file)) {
prj = NULL;
goto return_goto;
}
if (rz_file_is_deflated(file)) {
if (!rz_file_inflate(file, tmp_file)) {
prj = NULL;
goto return_goto;
}
} else {
load_file = file;
}
if (!sdb_text_load(prj, load_file)) {
sdb_free(prj);
prj = NULL;
}
return_goto:
rz_file_rm(tmp_file);
free(tmp_file);
return prj;
}
RZ_API void rz_project_free(RzProject *prj) {
sdb_free(prj);
}
RZ_API RzProjectErr rz_project_load(RzCore *core, RzProject *prj, bool load_bin_io, RZ_NULLABLE const char *file, RzSerializeResultInfo *res) {
rz_return_val_if_fail(core && prj, RZ_PROJECT_ERR_UNKNOWN);
const char *type = sdb_const_get(prj, RZ_PROJECT_KEY_TYPE);
if (!type || strcmp(type, RZ_PROJECT_TYPE) != 0) {
return RZ_PROJECT_ERR_INVALID_TYPE;
}
const char *version_str = sdb_const_get(prj, RZ_PROJECT_KEY_VERSION);
if (!version_str) {
return RZ_PROJECT_ERR_INVALID_VERSION;
}
unsigned long version = strtoul(version_str, NULL, 0);
if (!version || version == ULONG_MAX) {
return RZ_PROJECT_ERR_INVALID_VERSION;
}
if (version > RZ_PROJECT_VERSION) {
return RZ_PROJECT_ERR_NEWER_VERSION;
}
if (!rz_project_migrate(prj, version, res)) {
return RZ_PROJECT_ERR_MIGRATION_FAILED;
}
Sdb *core_db = sdb_ns(prj, "core", false);
if (!core_db) {
RZ_SERIALIZE_ERR(res, "missing core namespace");
return RZ_PROJECT_ERR_INVALID_CONTENTS;
}
if (!rz_serialize_core_load(core_db, core, load_bin_io, file, res)) {
return RZ_PROJECT_ERR_INVALID_CONTENTS;
}
rz_config_set(core->config, "prj.file", file);
return RZ_PROJECT_ERR_SUCCESS;
}
RZ_API RzProjectErr rz_project_load_file(RzCore *core, const char *file, bool load_bin_io, RzSerializeResultInfo *res) {
RzProject *prj = rz_project_load_file_raw(file);
if (!prj) {
RZ_SERIALIZE_ERR(res, "failed to read database file");
return RZ_PROJECT_ERR_FILE;
}
RzProjectErr ret = rz_project_load(core, prj, load_bin_io, file, res);
sdb_free(prj);
return ret;
}
RZ_API bool rz_core_project_load_for_cli(RzCore *core, const char *file, bool load_bin_io) {
RzSerializeResultInfo *res = rz_serialize_result_info_new();
RzProjectErr err = rz_project_load_file(core, file, load_bin_io, res);
if (err != RZ_PROJECT_ERR_SUCCESS) {
RZ_LOG_ERROR("core: Failed to load project: %s\n", rz_project_err_message(err));
} else if (!rz_list_empty(res)) {
rz_cons_printf("Detailed project load info:\n");
}
RzListIter *it;
char *s;
rz_list_foreach (res, it, s) {
rz_cons_printf(" %s\n", s);
}
rz_serialize_result_info_free(res);
return err == RZ_PROJECT_ERR_SUCCESS;
}