m1n1/src/adt.c

238 lines
6 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
#include "adt.h"
#include "string.h"
/* This API is designed to match libfdt's read-only API */
#define ADT_CHECK_HEADER(adt) \
{ \
int err; \
if ((err = adt_check_header(adt)) != 0) \
return err; \
}
//#define DEBUG
#ifdef DEBUG
#include "utils.h"
#define dprintf printf
#else
#define dprintf(...) \
do { \
} while (0)
#endif
int _adt_check_node_offset(const void *adt, int offset)
{
if ((offset < 0) || (offset % ADT_ALIGN))
return -ADT_ERR_BADOFFSET;
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
// Sanity check
if (node->property_count > 2048 || !node->property_count || node->child_count > 2048)
return -ADT_ERR_BADOFFSET;
return 0;
}
int _adt_check_prop_offset(const void *adt, int offset)
{
if ((offset < 0) || (offset % ADT_ALIGN))
return -ADT_ERR_BADOFFSET;
const struct adt_property *prop = ADT_PROP(adt, offset);
if (prop->size & 0x7ff00000) // up to 1MB properties
return -ADT_ERR_BADOFFSET;
return 0;
}
int adt_check_header(const void *adt)
{
return _adt_check_node_offset(adt, 0);
}
static int _adt_string_eq(const char *a, const char *b, size_t len)
{
return (strlen(a) == len) && (memcmp(a, b, len) == 0);
}
static int _adt_nodename_eq(const char *a, const char *b, size_t len)
{
if (memcmp(a, b, len) != 0)
return 0;
if (a[len] == '\0')
return 1;
else if (!memchr(b, '@', len) && (a[len] == '@'))
return 1;
else
return 0;
}
const struct adt_property *adt_get_property_namelen(const void *adt, int offset, const char *name,
size_t namelen)
{
dprintf("adt_get_property_namelen(%p, %d, \"%s\", %u)\n", adt, offset, name, namelen);
for (offset = adt_first_property_offset(adt, offset); (offset >= 0);
(offset = adt_next_property_offset(adt, offset))) {
const struct adt_property *prop;
prop = adt_get_property_by_offset(adt, offset);
dprintf(" off=0x%x name=\"%s\"\n", offset, prop->name);
if (_adt_string_eq(prop->name, name, namelen))
return prop;
}
return NULL;
}
const struct adt_property *adt_get_property(const void *adt, int nodeoffset, const char *name)
{
return adt_get_property_namelen(adt, nodeoffset, name, strlen(name));
}
const void *adt_getprop_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen,
u32 *lenp)
{
const struct adt_property *prop;
prop = adt_get_property_namelen(adt, nodeoffset, name, namelen);
if (!prop)
return NULL;
if (lenp)
*lenp = prop->size;
return prop->value;
}
const void *adt_getprop_by_offset(const void *adt, int offset, const char **namep, u32 *lenp)
{
const struct adt_property *prop;
prop = adt_get_property_by_offset(adt, offset);
if (!prop)
return NULL;
if (namep)
*namep = prop->name;
if (lenp)
*lenp = prop->size;
return prop->value;
}
const void *adt_getprop(const void *adt, int nodeoffset, const char *name, u32 *lenp)
{
return adt_getprop_namelen(adt, nodeoffset, name, strlen(name), lenp);
}
int adt_getprop_copy(const void *adt, int nodeoffset, const char *name, void *out, size_t len)
{
u32 plen;
const void *p = adt_getprop(adt, nodeoffset, name, &plen);
if (!p)
return -ADT_ERR_NOTFOUND;
if (plen != len)
return -ADT_ERR_BADLENGTH;
memcpy(out, p, len);
return len;
}
int adt_first_child_offset(const void *adt, int offset)
{
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
u32 cnt = node->property_count;
offset = adt_first_property_offset(adt, offset);
while (cnt--) {
offset = adt_next_property_offset(adt, offset);
}
return offset;
}
int adt_next_sibling_offset(const void *adt, int offset)
{
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
u32 cnt = node->child_count;
offset = adt_first_child_offset(adt, offset);
while (cnt--) {
offset = adt_next_sibling_offset(adt, offset);
}
return offset;
}
int adt_subnode_offset_namelen(const void *adt, int offset, const char *name, size_t namelen)
{
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
ADT_CHECK_HEADER(adt);
offset = adt_first_child_offset(adt, offset);
for (u32 i = 0; i < node->child_count; i++) {
const char *cname = adt_get_name(adt, offset);
if (_adt_nodename_eq(cname, name, namelen))
return offset;
offset = adt_next_sibling_offset(adt, offset);
}
return -ADT_ERR_NOTFOUND;
}
int adt_subnode_offset(const void *adt, int parentoffset, const char *name)
{
return adt_subnode_offset_namelen(adt, parentoffset, name, strlen(name));
}
int adt_path_offset(const void *adt, const char *path)
{
const char *end = path + strlen(path);
const char *p = path;
int offset = 0;
ADT_CHECK_HEADER(adt);
while (*p) {
const char *q;
while (*p == '/')
p++;
if (!*p)
return offset;
q = strchr(p, '/');
if (!q)
q = end;
offset = adt_subnode_offset_namelen(adt, offset, p, q - p);
if (offset < 0)
return offset;
p = q;
}
return offset;
}
const char *adt_get_name(const void *adt, int nodeoffset)
{
return adt_getprop(adt, nodeoffset, "name", NULL);
}