salvage: add yyjson metadata helpers

This commit is contained in:
Mario Fetka
2026-05-31 09:49:17 +00:00
parent 5d0c665b52
commit 054ea6c867
7 changed files with 492 additions and 0 deletions

View File

@@ -77,6 +77,10 @@ add_executable(ftrustee ftrustee.c tools.c nwfname.c unxfile.c nwvolume.c nwattr
target_link_libraries(nwserv ${CRYPT_LIBRARIES} )
target_link_libraries(nwconn ${CRYPT_LIBRARIES} ${XATTR_LIBRARIES} )
if(MARS_NWE_HAVE_YYJSON)
target_compile_definitions(nwconn PRIVATE MARS_NWE_HAVE_YYJSON)
target_link_libraries(nwconn yyjson)
endif()
target_link_libraries(ncpserv ${CRYPT_LIBRARIES} )
target_link_libraries(nwclient ${CRYPT_LIBRARIES} )
target_link_libraries(nwbind ${CRYPT_LIBRARIES} ${GDBM_LIBRARIES} )

View File

@@ -3,8 +3,15 @@
#include <ctype.h>
#include <errno.h>
#ifdef MARS_NWE_HAVE_YYJSON
#include <stdint.h>
#include <yyjson.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int nwsalvage_repository_name_valid(const char *name)
{
@@ -293,3 +300,316 @@ int nwsalvage_build_metadata_path(char *out, size_t out_len,
return(build_path(out, out_len, volume_root,
config->metadata_repository, relative_path, ".json"));
}
#ifdef MARS_NWE_HAVE_YYJSON
static int make_dir_if_missing(const char *path)
{
struct stat st;
if (!path || !*path) {
errno = EINVAL;
return(-1);
}
if (!stat(path, &st)) {
if (S_ISDIR(st.st_mode))
return(0);
errno = ENOTDIR;
return(-1);
}
if (mkdir(path, 0777) == 0)
return(0);
if (errno == EEXIST)
return(0);
return(-1);
}
static int make_parent_dirs(const char *path)
{
char tmp[NWSALVAGE_PATH_MAX];
char *p;
size_t len;
if (!path || !*path) {
errno = EINVAL;
return(-1);
}
len = strlen(path);
if (len >= sizeof(tmp)) {
errno = ENAMETOOLONG;
return(-1);
}
memcpy(tmp, path, len + 1);
p = strrchr(tmp, '/');
if (!p) {
errno = EINVAL;
return(-1);
}
if (p == tmp)
return(0);
*p = '\0';
for (p = tmp + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
if (make_dir_if_missing(tmp) < 0)
return(-1);
*p = '/';
}
}
return(make_dir_if_missing(tmp));
}
#endif
#ifndef MARS_NWE_HAVE_YYJSON
int nwsalvage_write_metadata(const char *metadata_path,
const struct nwsalvage_deleted_entry *entry)
{
(void)metadata_path;
(void)entry;
errno = ENOSYS;
return(-1);
}
int nwsalvage_read_metadata(const char *metadata_path,
struct nwsalvage_metadata_entry *entry)
{
(void)metadata_path;
if (entry)
memset(entry, 0, sizeof(*entry));
errno = ENOSYS;
return(-1);
}
#else
static int json_add_string(yyjson_mut_doc *doc,
yyjson_mut_val *object,
const char *name,
const char *value)
{
if (!doc || !object || !name || !value) {
errno = EINVAL;
return(-1);
}
if (!yyjson_mut_obj_add_strcpy(doc, object, name, value)) {
errno = ENOMEM;
return(-1);
}
return(0);
}
static int json_add_uint64(yyjson_mut_doc *doc,
yyjson_mut_val *object,
const char *name,
unsigned long long value)
{
if (!doc || !object || !name) {
errno = EINVAL;
return(-1);
}
if (!yyjson_mut_obj_add_uint(doc, object, name, (uint64_t)value)) {
errno = ENOMEM;
return(-1);
}
return(0);
}
static int json_get_string_copy(yyjson_val *object,
const char *name,
char *out,
size_t out_len)
{
yyjson_val *value_object;
const char *value;
size_t len;
if (!object || !name || !out || !out_len) {
errno = EINVAL;
return(-1);
}
value_object = yyjson_obj_get(object, name);
if (!yyjson_is_str(value_object)) {
errno = EINVAL;
return(-1);
}
value = yyjson_get_str(value_object);
len = strlen(value);
if (len >= out_len) {
errno = ENAMETOOLONG;
return(-1);
}
memcpy(out, value, len + 1);
return(0);
}
static int json_get_uint64(yyjson_val *object,
const char *name,
unsigned long long *out)
{
yyjson_val *value_object;
if (!object || !name || !out) {
errno = EINVAL;
return(-1);
}
value_object = yyjson_obj_get(object, name);
if (!yyjson_is_uint(value_object)) {
errno = EINVAL;
return(-1);
}
*out = (unsigned long long)yyjson_get_uint(value_object);
return(0);
}
int nwsalvage_write_metadata(const char *metadata_path,
const struct nwsalvage_deleted_entry *entry)
{
yyjson_mut_doc *doc;
yyjson_mut_val *object;
yyjson_write_err err;
int failed = 0;
if (!metadata_path || !*metadata_path || !entry ||
!entry->volume_name || !entry->relative_path || !entry->recycle_path) {
errno = EINVAL;
return(-1);
}
if (make_parent_dirs(metadata_path) < 0)
return(-1);
doc = yyjson_mut_doc_new(NULL);
if (!doc) {
errno = ENOMEM;
return(-1);
}
object = yyjson_mut_obj(doc);
if (!object) {
yyjson_mut_doc_free(doc);
errno = ENOMEM;
return(-1);
}
yyjson_mut_doc_set_root(doc, object);
if (json_add_uint64(doc, object, "version", 1) < 0) failed = 1;
if (!failed && json_add_string(doc, object, "volume", entry->volume_name) < 0)
failed = 1;
if (!failed && json_add_string(doc, object, "path", entry->relative_path) < 0)
failed = 1;
if (!failed && json_add_string(doc, object, "recycle_path", entry->recycle_path) < 0)
failed = 1;
if (!failed && json_add_uint64(doc, object, "attributes", entry->attributes) < 0)
failed = 1;
if (!failed && json_add_uint64(doc, object, "mode", entry->mode) < 0)
failed = 1;
if (!failed && json_add_uint64(doc, object, "size", entry->size) < 0)
failed = 1;
if (!failed && entry->mtime < 0) {
errno = EINVAL;
failed = 1;
}
if (!failed && json_add_uint64(doc, object, "mtime",
(unsigned long long)entry->mtime) < 0)
failed = 1;
if (!failed && !yyjson_mut_write_file(metadata_path, doc,
YYJSON_WRITE_PRETTY_TWO_SPACES |
YYJSON_WRITE_NEWLINE_AT_END,
NULL, &err)) {
(void)err;
errno = EIO;
failed = 1;
}
yyjson_mut_doc_free(doc);
if (failed) {
unlink(metadata_path);
return(-1);
}
return(0);
}
int nwsalvage_read_metadata(const char *metadata_path,
struct nwsalvage_metadata_entry *entry)
{
yyjson_doc *doc;
yyjson_val *object;
yyjson_read_err err;
unsigned long long value;
int failed = 0;
if (!metadata_path || !*metadata_path || !entry) {
errno = EINVAL;
return(-1);
}
memset(entry, 0, sizeof(*entry));
doc = yyjson_read_file(metadata_path, YYJSON_READ_NOFLAG, NULL, &err);
if (!doc) {
(void)err;
errno = EINVAL;
return(-1);
}
object = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(object))
failed = 1;
if (!failed &&
(json_get_uint64(object, "version", &value) < 0 || value != 1))
failed = 1;
if (!failed && json_get_string_copy(object, "volume",
entry->volume_name,
sizeof(entry->volume_name)) < 0)
failed = 1;
if (!failed && json_get_string_copy(object, "path",
entry->relative_path,
sizeof(entry->relative_path)) < 0)
failed = 1;
if (!failed && json_get_string_copy(object, "recycle_path",
entry->recycle_path,
sizeof(entry->recycle_path)) < 0)
failed = 1;
if (!failed && json_get_uint64(object, "attributes", &value) < 0)
failed = 1;
else if (!failed)
entry->attributes = (unsigned int)value;
if (!failed && json_get_uint64(object, "mode", &value) < 0)
failed = 1;
else if (!failed)
entry->mode = (unsigned long)value;
if (!failed && json_get_uint64(object, "size", &value) < 0)
failed = 1;
else if (!failed)
entry->size = value;
if (!failed && json_get_uint64(object, "mtime", &value) < 0)
failed = 1;
else if (!failed)
entry->mtime = (long)value;
yyjson_doc_free(doc);
if (failed) {
errno = EINVAL;
return(-1);
}
return(0);
}
#endif