+Assure that the list of deltas is ordered to facilitate the validation of contiguous serials, and the processing of only the required deltas (only if there's a delta update).
+Change enum 'rrdp_uri_cmp_result' to a type 'rrdp_uri_cmp_result_t'.
+Process the snapshot if there's an error processing deltas.
+Make 'delta_head' attributes public, global and doc data init methods are now void.
+Remove 'SLIST' usage at 'deltas_head' struct, use instead an array list implementation, ready to store a defined amount of elements.
return 0;
}
-enum rrdp_uri_cmp_result
+rrdp_uri_cmp_result_t
db_rrdp_cmp_uri(struct db_rrdp *db, char const *uri, char const *session_id,
unsigned long serial)
{
int db_rrdp_create(struct db_rrdp **);
void db_rddp_destroy(struct db_rrdp *);
-enum rrdp_uri_cmp_result db_rrdp_cmp_uri(struct db_rrdp *, char const *,
+rrdp_uri_cmp_result_t db_rrdp_cmp_uri(struct db_rrdp *, char const *,
char const *, unsigned long);
int db_rrdp_add_uri(struct db_rrdp *, char const *, char const *,
unsigned long);
return 0;
}
-enum rrdp_uri_cmp_result
+rrdp_uri_cmp_result_t
rhandler_uri_cmp(char const *uri, char const *session_id, unsigned long serial)
{
CALL_HANDLER_FUNC(uri_cmp, uri_cmp(uri, session_id, serial))
* Search the RRDP URI, returns the corresponding enum to indicate
* the comparison result.
*/
- enum rrdp_uri_cmp_result (*uri_cmp)(char const *, char const *,
+ rrdp_uri_cmp_result_t (*uri_cmp)(char const *, char const *,
unsigned long);
/* Add or update an RRDP URI */
int (*uri_update)(char const *, char const *, unsigned long);
int (*uri_set_last_update)(char const *);
};
-enum rrdp_uri_cmp_result rhandler_uri_cmp(char const *, char const *, unsigned long);
+rrdp_uri_cmp_result_t rhandler_uri_cmp(char const *, char const *,
+ unsigned long);
int rhandler_uri_update(char const *, char const *, unsigned long);
int rhandler_uri_get_serial(char const *, unsigned long *);
int rhandler_uri_get_last_update(char const *, long *);
rrdp_load(struct rpki_uri *uri)
{
struct update_notification *upd_notification;
+ rrdp_uri_cmp_result_t res;
long last_update;
- enum rrdp_uri_cmp_result res;
int error;
last_update = 0;
res = rhandler_uri_cmp(uri_get_global(uri),
upd_notification->global_data.session_id,
upd_notification->global_data.serial);
- switch(res) {
+ switch (res) {
case RRDP_URI_EQUAL:
goto set_update;
case RRDP_URI_DIFF_SERIAL:
error = process_diff_serial(upd_notification,
uri_get_global(uri));
+ /* Something went wrong, use snapshot */
+ if (!error)
+ break;
+ pr_warn("There was an error processing RRDP deltas, using the snapshot instead.");
+ error = process_snapshot(upd_notification, uri_get_global(uri));
break;
case RRDP_URI_DIFF_SESSION:
/* FIXME (now) delete the old session files */
#include "rrdp_objects.h"
-#include <sys/queue.h>
+#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include "log.h"
-struct delta_head {
- unsigned long serial;
- struct doc_data doc_data;
- unsigned int references;
- SLIST_ENTRY(delta_head) next;
+/*
+ * List of deltas inside an update notification file.
+ *
+ * The structure functions are extended and will have the following meaning:
+ * - capacity : is the size of the array, must be set before using the array
+ * and can't be modified.
+ * - len : number of elements set in the array.
+ *
+ * This struct is a diff version of array_list, utilized to store only the
+ * amount of deltas that may be needed and validate that an update notification
+ * file has a contiguous set of deltas.
+ */
+struct deltas_head {
+ /** Unidimensional array. Initialized lazily. */
+ struct delta_head **array;
+ /** Number of elements in @array. */
+ size_t len;
+ /** Actual allocated slots in @array. */
+ size_t capacity;
};
-/* List of deltas inside an update notification file */
-SLIST_HEAD(deltas_head, delta_head);
-
-int
+void
global_data_init(struct global_data *data)
{
data->session_id = NULL;
- return 0;
}
void
free(data->session_id);
}
-int
+void
doc_data_init(struct doc_data *data)
{
data->hash = NULL;
data->hash_len = 0;
data->uri = NULL;
- return 0;
}
void
return pr_enomem();
doc_data_init(&tmp->doc_data);
- tmp->references = 1;
*result = tmp;
return 0;
}
-unsigned long
-delta_head_get_serial(struct delta_head *delta_head)
+static void
+delta_head_destroy(struct delta_head *delta_head)
{
- return delta_head->serial;
+ if (delta_head) {
+ doc_data_cleanup(&delta_head->doc_data);
+ free(delta_head);
+ }
}
-struct doc_data *
-delta_head_get_doc_data(struct delta_head *delta_head)
+static void
+deltas_head_init(struct deltas_head *list)
{
- return &delta_head->doc_data;
+ list->array = NULL;
+ list->len = 0;
+ list->capacity = 0;
}
-void
-delta_head_refget(struct delta_head *delta_head)
+static void
+deltas_head_cleanup(struct deltas_head *list)
{
- delta_head->references++;
-}
+ size_t i;
-void
-delta_head_refput(struct delta_head *delta_head)
-{
- delta_head->references--;
- if (delta_head->references == 0) {
- doc_data_cleanup(&delta_head->doc_data);
- free(delta_head);
- }
+ for (i = 0; i < list->capacity; i++)
+ delta_head_destroy(list->array[i]);
+ if (list->array)
+ free(list->array);
}
static int
if (tmp == NULL)
return pr_enomem();
- SLIST_INIT(tmp);
+ deltas_head_init(tmp);
*deltas = tmp;
return 0;
static void
deltas_head_destroy(struct deltas_head *deltas)
{
- struct delta_head *head;
-
- while (!SLIST_EMPTY(deltas)) {
- head = deltas->slh_first;
- SLIST_REMOVE_HEAD(deltas, next);
- delta_head_refput(head);
- }
+ deltas_head_cleanup(deltas);
free(deltas);
}
int
-update_notification_create(struct update_notification **file)
+deltas_head_set_size(struct deltas_head *deltas, size_t capacity)
{
- struct update_notification *tmp;
- int error;
+ size_t i;
- tmp = malloc(sizeof(struct update_notification));
- if (tmp == NULL)
- return pr_enomem();
+ if (deltas->array != NULL)
+ pr_crit("Size of this list can't be modified");
- error = deltas_head_create(&tmp->deltas_list);
- if (error) {
- free(tmp);
- return error;
- }
+ deltas->capacity = capacity;
+ if (capacity == 0)
+ return 0; /* Ok, list can have 0 elements */
- global_data_init(&tmp->global_data);
- doc_data_init(&tmp->snapshot);
+ deltas->array = malloc(deltas->capacity
+ * sizeof(struct delta_head *));
+ if (deltas->array == NULL)
+ return pr_enomem();
+
+ /* Point all elements to NULL */
+ for (i = 0; i < deltas->capacity; i++)
+ deltas->array[i] = NULL;
- *file = tmp;
return 0;
}
-void
-update_notification_destroy(struct update_notification *file)
+size_t
+deltas_head_get_size(struct deltas_head *deltas)
{
- doc_data_cleanup(&file->snapshot);
- global_data_cleanup(&file->global_data);
- deltas_head_destroy(file->deltas_list);
- free(file);
+ return deltas->capacity;
}
-/* A new delta_head will be allocated, as well as its URI and HASH */
+/*
+ * A new delta_head will be allocated at the @position inside @deltas (also its
+ * URI and HASH will be allocated).
+ *
+ * The following errors can be returned due to a wrong @position:
+ * -EEXIST: There's already an element at @position.
+ * -EINVAL: @position can't be inside @deltas list, meaning that such element
+ * isn't part of a contiguous list.
+ *
+ * Don't forget to call deltas_head_set_size() before this!!
+ */
int
-deltas_head_add(struct deltas_head *deltas, unsigned long serial,
- char *uri, unsigned char *hash, size_t hash_len)
+deltas_head_add(struct deltas_head *deltas, size_t position,
+ unsigned long serial, char *uri, unsigned char *hash, size_t hash_len)
{
struct delta_head *elem;
int error;
+ if (position < 0 || position > deltas->capacity - 1)
+ return -EINVAL;
+
+ if (deltas->array[position] != NULL)
+ return -EEXIST;
+
elem = NULL;
error = delta_head_create(&elem);
if (error)
}
memcpy(elem->doc_data.hash, hash, hash_len);
- SLIST_INSERT_HEAD(deltas, elem, next);
+ deltas->array[position] = elem;
+ deltas->len++;
return 0;
}
+/* Are all expected values set? */
+bool
+deltas_head_values_set(struct deltas_head *deltas)
+{
+ return deltas->len == deltas->capacity;
+}
+
int
-deltas_head_for_each(struct deltas_head *deltas, delta_head_cb cb, void *arg)
+deltas_head_for_each(struct deltas_head *deltas, size_t from, delta_head_cb cb,
+ void *arg)
{
- struct delta_head *cursor;
+ size_t index;
int error;
- SLIST_FOREACH(cursor, deltas, next) {
- error = cb(cursor, arg);
+ /* No elements, send error so that the snapshot is processed */
+ if (deltas->capacity == 0) {
+ pr_warn("There's no delta list to process.");
+ return -ENOENT;
+ }
+
+ for (index = from; index < deltas->capacity; index++) {
+ error = cb(deltas->array[index], arg);
if (error)
return error;
}
return 0;
}
+int
+update_notification_create(struct update_notification **file)
+{
+ struct update_notification *tmp;
+ struct deltas_head *list;
+ int error;
+
+ tmp = malloc(sizeof(struct update_notification));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ list = NULL;
+ error = deltas_head_create(&list);
+ if (error) {
+ free(tmp);
+ return pr_enomem();
+ }
+ tmp->deltas_list = list;
+
+ global_data_init(&tmp->global_data);
+ doc_data_init(&tmp->snapshot);
+
+ *file = tmp;
+ return 0;
+}
+
+void
+update_notification_destroy(struct update_notification *file)
+{
+ doc_data_cleanup(&file->snapshot);
+ global_data_cleanup(&file->global_data);
+ deltas_head_destroy(file->deltas_list);
+ free(file);
+}
+
int
snapshot_create(struct snapshot **file)
{
#define SRC_RRDP_RRDP_OBJECTS_H_
#include <stddef.h>
+#include <stdbool.h>
/* Possible results for an RRDP URI comparison */
-enum rrdp_uri_cmp_result {
+typedef enum {
/* The URI exists and has the same session ID and serial */
RRDP_URI_EQUAL,
/* The URI doesn't exists */
RRDP_URI_NOTFOUND,
-};
+} rrdp_uri_cmp_result_t;
/* Global RRDP files data */
struct global_data {
struct global_data global_data;
};
-/* Snapshot file content
+/*
+ * Snapshot file content
* Publish list isn't remember, is processed ASAP.
*/
struct snapshot {
};
/* Delta element located at an update notification file */
-struct delta_head;
+struct delta_head {
+ unsigned long serial;
+ struct doc_data doc_data;
+};
/* List of deltas inside an update notification file */
struct deltas_head;
+/* Update notification file content */
struct update_notification {
struct global_data global_data;
struct doc_data snapshot;
struct deltas_head *deltas_list;
};
-int global_data_init(struct global_data *);
+void global_data_init(struct global_data *);
void global_data_cleanup(struct global_data *);
-int doc_data_init(struct doc_data *);
+void doc_data_init(struct doc_data *);
void doc_data_cleanup(struct doc_data *);
int update_notification_create(struct update_notification **);
void update_notification_destroy(struct update_notification *);
-unsigned long delta_head_get_serial(struct delta_head *);
-struct doc_data *delta_head_get_doc_data(struct delta_head *);
-
-void delta_head_refget(struct delta_head *);
-void delta_head_refput(struct delta_head *);
-
typedef int (*delta_head_cb)(struct delta_head *, void *);
-int deltas_head_for_each(struct deltas_head *, delta_head_cb, void *);
-int deltas_head_add(struct deltas_head *, unsigned long, char *,
+int deltas_head_for_each(struct deltas_head *, size_t, delta_head_cb, void *);
+int deltas_head_add(struct deltas_head *, size_t, unsigned long, char *,
unsigned char *, size_t);
+int deltas_head_set_size(struct deltas_head *, size_t);
+size_t deltas_head_get_size(struct deltas_head *);
+bool deltas_head_values_set(struct deltas_head *);
+
int snapshot_create(struct snapshot **);
void snapshot_destroy(struct snapshot *);
#define RRDP_ATTR_URI "uri"
#define RRDP_ATTR_HASH "hash"
-struct deltas_proc_args {
- struct delta_head **deltas;
- unsigned long serial;
- size_t deltas_set;
-};
-
static int
get_root_element(xmlDoc *doc, xmlNode **result)
{
return error;
}
end:
+ /* Function called just to do the validation */
+ if (data == NULL) {
+ doc_data_init(data);
+ free(hash);
+ free(uri);
+ return 0;
+ }
data->uri = uri;
data->hash = hash;
data->hash_len = hash_len;
}
static int
-parse_notification_deltas(xmlNode *root, struct deltas_head *deltas,
- unsigned long *parsed_serial)
+parse_notification_deltas(xmlNode *root, unsigned long max_serial,
+ unsigned long deltas_len, struct deltas_head *deltas)
{
struct doc_data doc_data;
unsigned long serial;
+ size_t position;
int error;
error = parse_long(root, RRDP_ATTR_SERIAL, &serial);
return error;
}
- error = deltas_head_add(deltas, serial, doc_data.uri, doc_data.hash,
- doc_data.hash_len);
+ /*
+ * The delta will be added to the position it belongs, assuring that
+ * the deltas list will be ordered.
+ */
+ position = deltas_len - 1 - (max_serial - serial);
+ error = deltas_head_add(deltas, position, serial, doc_data.uri,
+ doc_data.hash, doc_data.hash_len);
/* Always release data */
doc_data_cleanup(&doc_data);
- if (error)
- return error;
+ if (!error)
+ return 0;
- *parsed_serial = serial;
- return 0;
+ if (error == -EINVAL)
+ return pr_err("Serial '%lu' at delta elements isn't part of a contiguous list of serials.",
+ serial);
+
+ if (error == -EEXIST)
+ return pr_err("Duplicated serial '%lu' at delta elements.",
+ serial);
+
+ return error;
}
/* Get the notification data. In case of error, the caller must cleanup @file */
static int
-parse_notification_data(xmlNode *root, struct update_notification *file)
+parse_notification_data(xmlNode *root, struct update_notification *file,
+ char const *uri)
{
xmlNode *cur_node;
- unsigned long loaded_serial, min_serial;
- unsigned long delta_count;
- int snapshot_count;
+ rrdp_uri_cmp_result_t res;
+ unsigned long from_serial, max_serial;
+ unsigned long deltas_len;
+ bool create_snapshot;
int error;
- snapshot_count = 0;
- delta_count = 0;
- loaded_serial = 0;
- min_serial = ULONG_MAX;
+ create_snapshot = false;
+ from_serial = 0;
+ max_serial = file->global_data.serial;
+
+ /* By schema, at least one snapshot element will be present */
+ deltas_len = xmlChildElementCount(root) - 1;
+
+ error = deltas_head_set_size(file->deltas_list, deltas_len);
+ if (error)
+ return error;
+
+ res = rhandler_uri_cmp(uri, file->global_data.session_id,
+ file->global_data.serial);
+ switch (res) {
+ case RRDP_URI_EQUAL:
+ /* Just validate content */
+ break;
+ case RRDP_URI_DIFF_SERIAL:
+ /* Get only the deltas to process and the snapshot */
+ create_snapshot = true;
+ error = rhandler_uri_get_serial(uri, &from_serial);
+ if (error)
+ return pr_err("Couldn't get serial of '%s'.", uri);
+ break;
+ case RRDP_URI_DIFF_SESSION:
+ /* Get only the snapshot */
+ case RRDP_URI_NOTFOUND:
+ create_snapshot = true;
+ break;
+ default:
+ pr_crit("Unexpected RRDP URI comparison result");
+ }
for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
if (xmlStrEqual(cur_node->name, BAD_CAST RRDP_ELEM_DELTA)) {
- delta_count++;
- error = parse_notification_deltas(cur_node,
- file->deltas_list, &loaded_serial);
- /* Note that the elements may not be ordered. (¬¬) */
- if (!error && loaded_serial < min_serial)
- min_serial = loaded_serial;
+ error = parse_notification_deltas(cur_node, max_serial,
+ deltas_len, file->deltas_list);
} else if (xmlStrEqual(cur_node->name,
BAD_CAST RRDP_ELEM_SNAPSHOT)) {
- /*
- * The Update Notification File MUST contain exactly
- * one 'snapshot' element for the current repository
- * version.
- */
- if (++snapshot_count > 1)
- return pr_err("More than one snapshot element found");
error = parse_doc_data(cur_node, true, true,
- &file->snapshot);
+ (create_snapshot ? &file->snapshot : NULL));
}
if (error)
}
/*
- * If delta elements are included, they MUST form a contiguous
+ * "If delta elements are included, they MUST form a contiguous
* sequence of serial numbers starting at a revision determined by
* the Repository Server, up to the serial number mentioned in the
- * notification element.
+ * notification element."
*
- * FIXME (now) running out of time, this needs an improvement, but why
- * should we validate this? Anyways, leaving it for later.
+ * If all expected elements are set, everything is ok.
*/
- if (delta_count > 0 &&
- file->global_data.serial - min_serial + 1 != delta_count)
+ if (!deltas_head_values_set(file->deltas_list))
return pr_err("Deltas listed don't have a contiguous sequence of serial numbers");
return 0;
if (error)
goto release_update;
- error = parse_notification_data(root, tmp);
+ error = parse_notification_data(root, tmp, uri_get_global(uri));
if (error)
goto release_update;
if (error)
goto release_doc;
- expected_data = delta_head_get_doc_data(parents_data);
+ expected_data = &parents_data->doc_data;
error = hash_validate_file("sha256", uri, expected_data->hash,
expected_data->hash_len);
/* session_id must be the same as the parent */
error = parse_global_data(root, &delta->global_data,
parent->global_data.session_id,
- delta_head_get_serial(parents_data));
+ parents_data->serial);
if (error)
goto release_delta;
}
static int
-get_pending_delta(struct delta_head **delta_head, unsigned long pos,
- struct deltas_proc_args *args)
-{
- /* Ref to the delta element */
- args->deltas[pos] = *delta_head;
- args->deltas_set++;
- delta_head_refget(*delta_head);
-
- return 0;
-}
-
-static int
-__get_pending_delta(struct delta_head *delta_head, void *arg)
-{
- struct deltas_proc_args *args = arg;
- unsigned long serial;
-
- serial = delta_head_get_serial(delta_head);
- if (serial <= args->serial)
- return 0;
-
- return get_pending_delta(&delta_head, serial - args->serial - 1, args);
-}
-
-static int
-process_delta(struct delta_head *delta_head, struct update_notification *parent)
+process_delta(struct delta_head *delta_head, void *arg)
{
+ struct update_notification *parent = arg;
struct rpki_uri *uri;
struct doc_data *head_data;
int error;
- head_data = delta_head_get_doc_data(delta_head);
+ head_data = &delta_head->doc_data;
error = uri_create_https_str(&uri, head_data->uri,
strlen(head_data->uri));
}
int
-rrdp_process_deltas(struct update_notification *parent, unsigned long serial)
+rrdp_process_deltas(struct update_notification *parent,
+ unsigned long cur_serial)
{
- struct delta_head *deltas[parent->global_data.serial - serial];
- struct deltas_proc_args args;
size_t deltas_len;
- size_t index;
- int error;
-
- deltas_len = parent->global_data.serial - serial;
- for (index = 0; index < deltas_len; index++)
- deltas[index] = NULL;
-
- args.deltas = deltas;
- args.serial = serial;
- args.deltas_set = 0;
-
- error = deltas_head_for_each(parent->deltas_list, __get_pending_delta,
- &args);
- if (error)
- goto release_deltas;
-
- /* Check that all expected deltas are set */
- if (args.deltas_set != deltas_len) {
- error = pr_err("Less deltas than expected: should be from serial %lu to %lu (%lu), but got only %lu",
- serial, parent->global_data.serial, deltas_len,
- args.deltas_set);
- goto release_deltas;
- }
+ size_t from;
- /* Now process each delta in order */
- for (index = 0; index < deltas_len; index++) {
- error = process_delta(deltas[index], parent);
- if (error)
- break;
- }
- /* Error 0 it's ok */
-release_deltas:
- for (index = 0; index < deltas_len; index++)
- if (deltas[index] != NULL)
- delta_head_refput(deltas[index]);
+ deltas_len = deltas_head_get_size(parent->deltas_list);
+ from = deltas_len - (parent->global_data.serial - cur_serial);
- return error;
+ return deltas_head_for_each(parent->deltas_list, from, process_delta,
+ parent);
}
rtrhandler_handle_router_key(arg, ski, as, spk))
}
-enum rrdp_uri_cmp_result
+rrdp_uri_cmp_result_t
rrdp_uri_cmp(char const *uri, char const *session_id, unsigned long serial)
{
RLOCK_HANDLER(&state_lock,
#include <stdbool.h>
#include "data_structure/array_list.h"
+#include "rrdp/rrdp_objects.h"
#include "rtr/db/delta.h"
/*
int handle_router_key(unsigned char const *, uint32_t, unsigned char const *,
void *);
-enum rrdp_uri_cmp_result rrdp_uri_cmp(char const *, char const *,
+rrdp_uri_cmp_result_t rrdp_uri_cmp(char const *, char const *,
unsigned long);
int rrdp_uri_update(char const *, char const *, unsigned long);
int rrdp_uri_get_serial(char const *, unsigned long *);