/* * Ouroboros - Copyright (C) 2016 - 2017 * * Resource Information Base * * Dimitri Staessens <dimitri.staessens@ugent.be> * Sander Vrijders <sander.vrijders@ugent.be> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include <ouroboros/config.h> #include <ouroboros/errno.h> #include <ouroboros/list.h> #include <ouroboros/rib.h> #include <ouroboros/rqueue.h> #include <ouroboros/bitmap.h> #include <ouroboros/crc32.h> #include <ouroboros/time_utils.h> #include "sha3.h" #include "btree.h" #include "ro.pb-c.h" typedef RoMsg ro_msg_t; #include <pthread.h> #include <string.h> #include <assert.h> #include <stdlib.h> #include <stdio.h> #define RIB_PATH_DLR "/" #define RIB_BTREE_ORDER 64 #define GEN_NAME_SIZE 8 struct revent { struct list_head next; char * path; int32_t flags; }; struct rqueue { struct list_head events; }; struct ro_set { uint32_t sid; }; struct rn_ptr { struct list_head next; struct rnode * node; }; struct rib_sub { struct list_head next; uint32_t sid; struct list_head rnodes; struct list_head events; pthread_cond_t cond; pthread_mutex_t lock; }; struct rn_sub { struct list_head next; struct rib_sub * sub; int32_t flags; }; struct rnode { char * path; char * name; uint8_t * data; size_t len; uint8_t sha3[sha3_256_hash_size]; struct rnode * parent; size_t chlen; struct list_head children; struct list_head subs; }; struct child { struct list_head next; struct rnode * node; }; struct rib { struct rnode * root; struct btree * idx; pthread_rwlock_t lock; struct bmp * sids; struct list_head subs; pthread_rwlock_t s_lock; } rib; static void rnode_hash(struct rnode * node) { struct sha3_ctx ctx; struct list_head * p; assert(node); assert(node->path); assert(node->name); rhash_sha3_256_init(&ctx); rhash_sha3_update(&ctx, (uint8_t *) node->path, strlen(node->path)); if (node->data != NULL) rhash_sha3_update(&ctx, node->data, node->len); list_for_each(p, &node->children) { struct child * c = list_entry(p, struct child, next); rhash_sha3_update(&ctx, c->node->sha3, sha3_256_hash_size); } rhash_sha3_final(&ctx, node->sha3); } static void branch_hash(struct rnode * node) { assert(node); do { rnode_hash(node); node = node->parent; } while (node != NULL); } static struct revent * revent_dup(struct revent * ev) { struct revent * re; assert(ev); assert(ev->path); re = malloc(sizeof(*re)); if (re == NULL) return NULL; re->path = strdup(ev->path); if (re->path == NULL) { free(re); return NULL; } re->flags = ev->flags; list_head_init(&re->next); return re; } /* defined below but needed here */ static void rib_sub_del_rnode(struct rib_sub * sub, struct rnode * node); static void rnode_notify_subs(struct rnode * node, struct rnode * ch, struct revent * ev) { struct list_head * p; assert(node); list_for_each(p, &node->subs) { struct rn_sub * s = list_entry(p, struct rn_sub, next); if (s->flags & ev->flags) { struct revent * e = revent_dup(ev); if (e == NULL) continue; pthread_mutex_lock(&s->sub->lock); list_add_tail(&e->next, &s->sub->events); pthread_cond_signal(&s->sub->cond); pthread_mutex_unlock(&s->sub->lock); } if (ev->flags & RO_DELETE) rib_sub_del_rnode(s->sub, ch); } } static int rnode_throw_event(struct rnode * node, int32_t flags) { struct revent * ev = malloc(sizeof(*ev)); struct rnode * rn = node; assert(node); assert(node->path); if (ev == NULL) return -ENOMEM; list_head_init(&ev->next); ev->path = strdup(node->path); if (ev->path == NULL) { free(ev); return -ENOMEM; } ev->flags = flags; do { rnode_notify_subs(rn, node, ev); rn = rn->parent; } while (rn != NULL); free(ev->path); free(ev); return 0; } static int rnode_add_child(struct rnode * node, struct rnode * child) { struct child * c; struct list_head * p; struct child * n; assert(node); assert(child); c = malloc(sizeof(*c)); if (c == NULL) return -ENOMEM; c->node = child; list_for_each(p, &node->children) { n = list_entry(p, struct child, next); if (strcmp(n->node->name, child->name) > 0) break; } list_add_tail(&c->next, p); ++node->chlen; return 0; } static void rnode_remove_child(struct rnode * node, struct rnode * child) { struct list_head * p; struct list_head * h; assert(node); assert(child); list_for_each_safe(p, h, &node->children) { struct child * c = list_entry(p, struct child, next); if (c->node == child) { list_del(&c->next); free(c); --node->chlen; return; } } } static struct rnode * rnode_create(struct rnode * parent, const char * name) { struct rnode * node; char * parent_path; uint32_t crc = 0; assert(name); node = malloc(sizeof(*node)); if (node == NULL) return NULL; list_head_init(&node->children); list_head_init(&node->subs); if (parent == NULL) parent_path = ""; else parent_path = parent->path; node->path = malloc(strlen(parent_path) + strlen(RIB_PATH_DLR) + strlen(name) + 1); if (node->path == NULL) { free(node); return NULL; } strcpy(node->path, parent_path); node->name = node->path + strlen(parent_path); if (parent != NULL) { strcpy(node->name, RIB_PATH_DLR); node->name += strlen(RIB_PATH_DLR); } strcpy(node->name, name); if (parent != NULL) { if (rnode_add_child(parent, node)) { free(node->path); free(node); return NULL; } } node->data = NULL; node->len = 0; node->parent = parent; node->chlen = 0; crc32(&crc, node->path, strlen(node->path)); btree_insert(rib.idx, crc, node); branch_hash(node); rnode_throw_event(node, RO_CREATE); return node; } static void destroy_rnode(struct rnode * node) { struct list_head * p; struct list_head * h; uint32_t crc = 0; assert(node); if (node != rib.root) { rnode_remove_child(node->parent, node); branch_hash(node->parent); } rnode_throw_event(node, RO_DELETE); list_for_each_safe(p, h, &node->subs) { struct rn_sub * s = list_entry(p, struct rn_sub, next); list_del(&s->next); free(s); } crc32(&crc, node->path, strlen(node->path)); btree_remove(rib.idx, crc); free(node->path); if (node->data != NULL) free(node->data); free(node); } static void destroy_rtree(struct rnode * node) { struct list_head * p; struct list_head * h; assert(node); list_for_each_safe(p, h, &node->children) { struct child * c = list_entry(p, struct child, next); destroy_rtree(c->node); } destroy_rnode(node); } static void rnode_update(struct rnode * node, uint8_t * data, size_t len) { assert(node); assert(!(data == NULL && len != 0)); assert(!(data != NULL && len == 0)); if (node->data != NULL) free(node->data); node->data = data; node->len = len; rnode_throw_event(node, RO_MODIFY); branch_hash(node); } static struct rn_sub * rnode_get_sub(struct rnode * node, struct rib_sub * sub) { struct list_head * p; list_for_each(p, &node->subs) { struct rn_sub * r = list_entry(p, struct rn_sub, next); if (r->sub == sub) return r; } return NULL; } static int rnode_add_sub(struct rnode * node, struct rib_sub * sub, int32_t flags) { struct rn_sub * rs; assert(node); assert(sub); rs = rnode_get_sub(node, sub); if (rs != NULL) return -EPERM; rs = malloc(sizeof(*rs)); if (rs == NULL) return -ENOMEM; rs->sub = sub; rs->flags = flags; list_add(&rs->next, &node->subs); return 0; } static int rnode_del_sub(struct rnode * node, struct rib_sub * sub) { struct rn_sub * rs; assert(node); assert(sub); rs = rnode_get_sub(node, sub); if (rs == NULL) return 0; list_del(&rs->next); free(rs); return 0; } static struct rnode * find_rnode_by_path(const char * path) { uint32_t crc = 0; if (strcmp(path, RIB_ROOT) == 0) return rib.root; crc32(&crc, path, strlen(path)); return (struct rnode *) btree_search(rib.idx, crc); } int rib_init(void) { if (rib.root != NULL) return -EPERM; rib.idx = btree_create(RIB_BTREE_ORDER); if (rib.idx == NULL) { destroy_rtree(rib.root); rib.root = NULL; return -1; } rib.root = rnode_create(NULL, ""); if (rib.root == NULL) return -ENOMEM; rib.sids = bmp_create(32, 1); if (rib.sids == NULL) { btree_destroy(rib.idx); destroy_rtree(rib.root); rib.root = NULL; return -1; } if (pthread_rwlock_init(&rib.lock, NULL)) { bmp_destroy(rib.sids); btree_destroy(rib.idx); destroy_rtree(rib.root); rib.root = NULL; return -1; } if (pthread_rwlock_init(&rib.s_lock, NULL)) { pthread_rwlock_destroy(&rib.lock); bmp_destroy(rib.sids); btree_destroy(rib.idx); destroy_rtree(rib.root); rib.root = NULL; return -1; } list_head_init(&rib.subs); assert(rib.root); return 0; } void rib_fini(void) { if (rib.root == NULL) return; bmp_destroy(rib.sids); destroy_rtree(rib.root); rib.root = NULL; btree_destroy(rib.idx); pthread_rwlock_destroy(&rib.lock); } int rib_add(const char * path, const char * name) { struct rnode * parent; struct rnode * node; if (name == NULL) return -EINVAL; pthread_rwlock_wrlock(&rib.lock); parent = find_rnode_by_path(path); if (parent == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } node = rnode_create(parent, name); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -ENOMEM; } pthread_rwlock_unlock(&rib.lock); return 0; } int rib_del(char * path) { struct rnode * node; if (path == NULL) return -EINVAL; pthread_rwlock_wrlock(&rib.lock); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -EINVAL; } destroy_rtree(node); pthread_rwlock_unlock(&rib.lock); return 0; } ssize_t rib_read(const char * path, void * data, size_t len) { struct rnode * node; ssize_t rlen; if (path == NULL || data == NULL) return -EINVAL; pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } if (len < node->len) { pthread_rwlock_unlock(&rib.lock); return -EFBIG; } if (node->data == NULL) { pthread_rwlock_unlock(&rib.lock); return 0; } assert(node->len > 0); memcpy(data, node->data, node->len); rlen = node->len; rnode_throw_event(node, RO_READ); pthread_rwlock_unlock(&rib.lock); return rlen; } int rib_write(const char * path, const void * data, size_t len) { struct rnode * node; uint8_t * cdata; if (path == NULL) return -EINVAL; cdata = malloc(len); if (cdata == NULL) return -ENOMEM; memcpy(cdata, data, len); pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); if (node != NULL) rnode_update(node, cdata, len); pthread_rwlock_unlock(&rib.lock); return 0; } int rib_put(const char * path, void * data, size_t len) { struct rnode * node; if (path == NULL) return -EINVAL; pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); if (node != NULL) rnode_update(node, (uint8_t *) data, len); pthread_rwlock_unlock(&rib.lock); return 0; } bool rib_has(const char * path) { struct rnode * node; if (path == NULL) return -EINVAL; pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); pthread_rwlock_unlock(&rib.lock); return node != NULL; } ssize_t rib_children(const char * path, char *** children) { struct list_head * p; struct rnode * node; ssize_t i = 0; if (path == NULL) return -EINVAL; pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } if (children == NULL) { pthread_rwlock_unlock(&rib.lock); assert((ssize_t) node->chlen >= 0); return (ssize_t) node->chlen; } if (node->chlen == 0) { pthread_rwlock_unlock(&rib.lock); *children = NULL; return 0; } *children = malloc(sizeof(**children) * node->chlen); if (*children == NULL) { pthread_rwlock_unlock(&rib.lock); return -ENOMEM; } list_for_each(p, &node->children) { struct child * c = list_entry(p, struct child, next); (*children)[i] = strdup(c->node->name); if ((*children)[i] == NULL) { ssize_t j; pthread_rwlock_unlock(&rib.lock); for (j = 0; j < i; ++j) free((*children)[j]); free(*children); return -ENOMEM; } ++i; } assert(i > 0); assert((size_t) i == node->chlen); pthread_rwlock_unlock(&rib.lock); return i; } static struct rib_sub * rib_get_sub(uint32_t sid) { struct list_head * p; struct list_head * h; list_for_each_safe(p, h, &rib.subs) { struct rib_sub * r = list_entry(p, struct rib_sub, next); if (r->sid == sid) return r; } return 0; } static struct rib_sub * rib_sub_create(uint32_t sid) { pthread_condattr_t cattr; struct rib_sub * sub = malloc(sizeof(*sub)); if (sub == NULL) return NULL; if (pthread_condattr_init(&cattr)) { free(sub); return NULL; } #ifndef __APPLE__ pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); #endif if (pthread_cond_init(&sub->cond, &cattr)) { free(sub); return NULL; } if (pthread_mutex_init(&sub->lock, NULL)) { pthread_cond_destroy(&sub->cond); free(sub); return NULL; } list_head_init(&sub->rnodes); list_head_init(&sub->events); sub->sid = sid; return sub; } static void rib_sub_zero(struct rib_sub * sub) { struct list_head * p; struct list_head * h; assert(sub); list_for_each_safe(p, h, &sub->rnodes) { struct rn_ptr * r = list_entry(p, struct rn_ptr, next); assert(r->node); rnode_del_sub(r->node, sub); list_del(&r->next); free(r); } list_for_each_safe(p, h, &sub->events) { struct revent * r = list_entry(p, struct revent, next); list_del(&r->next); assert(r->path); free(r->path); free(r); } } static struct rn_ptr * rib_sub_get_rn_ptr(struct rib_sub * sub, struct rnode * node) { struct list_head * p; list_for_each(p, &sub->rnodes) { struct rn_ptr * r = list_entry(p, struct rn_ptr, next); assert(r->node); if (r->node == node) return r; } return NULL; } static int rib_sub_add_rnode(struct rib_sub * sub, struct rnode * node) { struct rn_ptr * rn; assert(sub); assert(node); if (rib_sub_get_rn_ptr(sub, node) != NULL) return 0; rn = malloc(sizeof(*rn)); if (rn == NULL) return -ENOMEM; rn->node = node; list_add(&rn->next, &sub->rnodes); return 0; } static void rib_sub_del_rnode(struct rib_sub * sub, struct rnode * node) { struct rn_ptr * rn; assert(sub); assert(node); rn = rib_sub_get_rn_ptr(sub, node); if (rn == NULL) return; list_del(&rn->next); free(rn); } static void rib_sub_destroy(struct rib_sub * sub) { assert(sub); rib_sub_zero(sub); free(sub); } /* Event calls from rqueue.h. */ ro_set_t * ro_set_create(void) { ro_set_t * set; struct rib_sub * sub; set = malloc(sizeof(*set)); if (set == NULL) return NULL; pthread_rwlock_wrlock(&rib.s_lock); set->sid = bmp_allocate(rib.sids); if (!bmp_is_id_valid(rib.sids, set->sid)) { pthread_rwlock_unlock(&rib.s_lock); free(set); return NULL; } pthread_rwlock_unlock(&rib.s_lock); pthread_rwlock_wrlock(&rib.lock); sub = rib_sub_create(set->sid); if (sub == NULL) { pthread_rwlock_unlock(&rib.lock); free(set); return NULL; } list_add(&sub->next, &rib.subs); pthread_rwlock_unlock(&rib.lock); return set; } void ro_set_destroy(ro_set_t * set) { struct rib_sub * sub = NULL; struct list_head * p; struct list_head * h; pthread_rwlock_wrlock(&rib.lock); list_for_each_safe(p, h, &rib.subs) { struct rib_sub * r = list_entry(p, struct rib_sub, next); if (r->sid == set->sid) { sub = r; break; } } if (sub != NULL) rib_sub_destroy(sub); pthread_rwlock_unlock(&rib.lock); pthread_rwlock_wrlock(&rib.s_lock); bmp_release(rib.sids, set->sid); pthread_rwlock_unlock(&rib.s_lock); free(set); } rqueue_t * rqueue_create(void) { rqueue_t * rq = malloc(sizeof(*rq)); if (rq == NULL) return NULL; list_head_init(&rq->events); return rq; } int rqueue_destroy(struct rqueue * rq) { struct list_head * p; struct list_head * h; list_for_each_safe(p, h, &rq->events) { struct revent * e = list_entry(p, struct revent, next); list_del(&e->next); free(e->path); free(e); } free(rq); return 0; } int ro_set_zero(ro_set_t * set) { struct rib_sub * sub; if (set == NULL) return -EINVAL; pthread_rwlock_wrlock(&rib.lock); sub = rib_get_sub(set->sid); assert(sub); rib_sub_zero(sub); pthread_rwlock_unlock(&rib.lock); return 0; } int ro_set_add(ro_set_t * set, const char * path, int32_t flags) { struct rib_sub * sub; struct rnode * node; if (set == NULL) return -EINVAL; pthread_rwlock_wrlock(&rib.lock); sub = rib_get_sub(set->sid); assert(sub); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -1; } if (rnode_add_sub(node, sub, flags)) { pthread_rwlock_unlock(&rib.lock); return -ENOMEM; } if (rib_sub_add_rnode(sub, node)) { pthread_rwlock_unlock(&rib.lock); return -ENOMEM; } pthread_rwlock_unlock(&rib.lock); return 0; } int ro_set_del(ro_set_t * set, const char * path) { struct rib_sub * sub; struct rnode * node; if (set == NULL) return -EINVAL; pthread_rwlock_wrlock(&rib.lock); sub = rib_get_sub(set->sid); assert(sub); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -1; } rnode_del_sub(node, sub); rib_sub_del_rnode(sub, node); pthread_rwlock_unlock(&rib.lock); return 0; } int32_t rqueue_next(rqueue_t * rq, char * path) { struct revent * ev; int32_t ret; if (list_is_empty(&rq->events)) return -1; ev = list_first_entry(&rq->events, struct revent, next); list_del(&ev->next); strcpy(path, ev->path); ret = ev->flags; free(ev->path); free(ev); return ret; } int rib_event_wait(ro_set_t * set, rqueue_t * rq, const struct timespec * timeout) { struct rib_sub * sub; struct timespec abstime; struct revent * ev; ssize_t ret = 0; if (set == NULL || rq == NULL) return -EINVAL; if (!list_is_empty(&rq->events)) return 0; if (timeout != NULL) { clock_gettime(PTHREAD_COND_CLOCK, &abstime); ts_add(&abstime, timeout, &abstime); } pthread_rwlock_rdlock(&rib.lock); sub = rib_get_sub(set->sid); pthread_rwlock_unlock(&rib.lock); pthread_mutex_lock(&sub->lock); pthread_cleanup_push((void(*)(void *)) pthread_mutex_unlock, (void *) &sub->lock); while (list_is_empty(&sub->events) && ret != -ETIMEDOUT) { if (timeout != NULL) ret = -pthread_cond_timedwait(&sub->cond, &sub->lock, &abstime); else ret = -pthread_cond_wait(&sub->cond, &sub->lock); } pthread_cleanup_pop(true); pthread_rwlock_wrlock(&rib.lock); if (ret != -ETIMEDOUT) { ev = list_first_entry(&sub->events, struct revent, next); list_move(&ev->next, &rq->events); } pthread_rwlock_unlock(&rib.lock); return ret; } /* Path name management. */ char * rib_path_append(char * path, const char * name) { char * pos; if (path == NULL || name == NULL || strstr(name, RIB_PATH_DLR)) return NULL; pos = path + strlen(path); memcpy(pos++, RIB_PATH_DLR, 1); strcpy(pos, name); return path; } char * rib_name_gen(void * data, size_t len) { uint32_t crc = 0; char * name; if (data == NULL || len == 0) return NULL; name= malloc(GEN_NAME_SIZE + 1); if (name == NULL) return NULL; crc32(&crc, data, len); sprintf(name, "%08x", crc); return name; } static ro_msg_t * rnode_pack(struct rnode * node, uint32_t flags, bool root) { ro_msg_t * msg; assert(node); if (node->parent == NULL) return NULL; msg = malloc(sizeof(*msg)); if (msg == NULL) return NULL; ro_msg__init(msg); msg->name = node->name; if (root) { assert(node->parent->path); msg->parent = node->parent->path; } if ((root && (flags & PACK_HASH_ROOT)) || (flags & PACK_HASH_ALL)) { msg->has_hash = true; msg->hash.data = node->sha3; msg->hash.len = sha3_256_hash_size; } if (node->data != NULL) { msg->has_data = true; msg->data.data = node->data; msg->data.len = node->len; } if (node->chlen > 0) { int n = 0; struct list_head * p; ro_msg_t ** msgs = malloc(sizeof(*msgs) * node->chlen); if (msgs == NULL) { free(msg); return NULL; } msg->n_children = node->chlen; list_for_each(p, &node->children) { struct child * c = list_entry(p, struct child, next); msgs[n] = rnode_pack(c->node, flags, false); if (msgs[n] == NULL) { int i; for (i = 0; i < n; ++i) free(msgs[i]); free(msgs); free(msg); return NULL; } ++n; } msg->children = msgs; } return msg; } static void free_ro_msg(ro_msg_t * msg) { size_t n = 0; while (n < msg->n_children) free_ro_msg(msg->children[n++]); free(msg->children); free(msg); } ssize_t rib_pack(const char * path, uint8_t ** buf, uint32_t flags) { struct rnode * node; ro_msg_t * msg; ssize_t len; if (path == NULL) return -EINVAL; pthread_rwlock_rdlock(&rib.lock); node = find_rnode_by_path(path); if (node == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } msg = rnode_pack(node, flags, true); if (msg == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } len = ro_msg__get_packed_size(msg); if (len == 0) { pthread_rwlock_unlock(&rib.lock); free_ro_msg(msg); return 0; } *buf = malloc(len); if (*buf == NULL) { pthread_rwlock_unlock(&rib.lock); free_ro_msg(msg); return -ENOMEM; } ro_msg__pack(msg, *buf); pthread_rwlock_unlock(&rib.lock); free_ro_msg(msg); return len; } static struct rnode * rnode_get_child(struct rnode * node, const char * name) { struct list_head * p; list_for_each(p, &node->children) { struct child * c = list_entry(p, struct child, next); if (strcmp(c->node->name, name) == 0) return c->node; } return NULL; } static int rnode_unpack(ro_msg_t * msg, struct rnode * parent, uint32_t flags) { struct rnode * node; size_t i; assert(msg); assert(parent); node = rnode_get_child(parent, msg->name); if (node == NULL) { if (flags & UNPACK_CREATE) node = rnode_create(parent, msg->name); else return -EPERM; } if (node == NULL) return -ENOMEM; /* Unpack in reverse order for faster insertion */ i = msg->n_children; while (i > 0) rnode_unpack(msg->children[--i], node, flags); if (msg->has_data) { uint8_t * data = malloc(msg->data.len); if (data == NULL) return -ENOMEM; memcpy(data, msg->data.data, msg->data.len); rnode_update(node, data, msg->data.len); } return 0; } int rib_unpack(uint8_t * packed, size_t len, uint32_t flags) { ro_msg_t * msg; struct rnode * root; int ret; if (packed == NULL) return -EINVAL; msg = ro_msg__unpack(NULL, len, packed); if (msg == NULL) return -EPERM; assert(msg->parent); pthread_rwlock_wrlock(&rib.lock); root = find_rnode_by_path(msg->parent); if (root == NULL) { pthread_rwlock_unlock(&rib.lock); return -EPERM; } ret = rnode_unpack(msg, root, flags); pthread_rwlock_unlock(&rib.lock); if (ret == 0 && msg->has_hash) { root = rnode_get_child(root, msg->name); if (memcmp(msg->hash.data, root->sha3, sha3_256_hash_size)) { ro_msg__free_unpacked(msg, NULL); return -EFAULT; } } ro_msg__free_unpacked(msg, NULL); free(packed); return ret; }