From 13feab587ff754ed0a9e3e93639ecd5c43f3ec40 Mon Sep 17 00:00:00 2001 From: Vivien Kraus Date: Wed, 15 Mar 2023 20:08:53 +0100 Subject: Rework the cache entry file API. A cache entry does not hold a reference to a file descriptor anymore. They are loaded in memory, and can be updated with a system of backups. --- bootstrap.conf | 3 + include/disfluid.h | 27 +- src/adwaita/disfluid-adwaita.h | 3 + src/adwaita/disfluid-cache-entry.c | 52 ++- src/adwaita/disfluid-window.c | 2 - src/libdisfluid/disfluid-cache-entry.h | 688 ++++++++++++++------------------- src/libdisfluid/disfluid-tests.h | 514 ++++++++++++++++-------- src/libdisfluid/main.c | 42 +- tests/run-unit-tests.c | 1 + 9 files changed, 692 insertions(+), 640 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 4b8a2e6..8de9c9d 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -23,10 +23,12 @@ assert assert-h attribute close +copy-file-range do-release-commit-and-tag errno fclose free-posix +ftruncate gettext-h git-version-gen gitlog-to-changelog @@ -42,6 +44,7 @@ realloc-gnu relocatable-lib-lgpl remove stdbool +stdint stdio strdup sys_stat diff --git a/include/disfluid.h b/include/disfluid.h index 8c52922..51f096a 100644 --- a/include/disfluid.h +++ b/include/disfluid.h @@ -134,14 +134,6 @@ extern "C" LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY extern struct disfluid_cache_entry *disfluid_cache_entry_alloc (void); - LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY - extern struct disfluid_cache_entry - *disfluid_cache_entry_from_file_name (const char *filename); - - LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY - LIBDISFLUID_FD_ARG_READ_1 extern struct disfluid_cache_entry - *disfluid_cache_entry_from_fd (int fd); - LIBDISFLUID_API LIBDISFLUID_DEALLOC_AS_CACHE_ENTRY extern struct disfluid_cache_entry *disfluid_cache_entry_dup (const struct disfluid_cache_entry *entry); @@ -164,9 +156,9 @@ extern "C" disfluid_cache_entry_set_invalidated (struct disfluid_cache_entry *entry, int invalidated); - LIBDISFLUID_API extern void - disfluid_cache_entry_set_file_name (struct disfluid_cache_entry *entry, - const char *filename); + LIBDISFLUID_API extern int + disfluid_cache_entry_load (struct disfluid_cache_entry *entry, + const char *filename); LIBDISFLUID_API extern void disfluid_cache_entry_get_request_date (const struct disfluid_cache_entry @@ -180,18 +172,9 @@ extern "C" disfluid_cache_entry_invalidated (const struct disfluid_cache_entry *entry); - LIBDISFLUID_API LIBDISFLUID_DEALLOC_WITH_FREE extern char - *disfluid_cache_entry_get_file_name (const struct disfluid_cache_entry - *entry); - LIBDISFLUID_API extern int - disfluid_cache_entry_save_other_file_name (const struct - disfluid_cache_entry *entry, - const char *filename); - - LIBDISFLUID_API LIBDISFLUID_FD_ARG_WRITE_2 extern int - disfluid_cache_entry_save_other_fd (const struct disfluid_cache_entry - *entry, int fd); + disfluid_cache_entry_save (const struct disfluid_cache_entry *entry, + const char *filename); # ifdef __cplusplus } diff --git a/src/adwaita/disfluid-adwaita.h b/src/adwaita/disfluid-adwaita.h index e589c75..9ed0304 100644 --- a/src/adwaita/disfluid-adwaita.h +++ b/src/adwaita/disfluid-adwaita.h @@ -53,6 +53,9 @@ GMenuItem *disfluid_adw_menu_item_about (void); GMenu *disfluid_adw_main_menu (void); +void disfluid_adw_cache_entry_set_file_name (DisfluidAdwCacheEntry * + cache_entry, const char *fname); + void disfluid_adw_cache_entry_set_cache_entry (DisfluidAdwCacheEntry * cache_entry, struct disfluid_cache_entry diff --git a/src/adwaita/disfluid-cache-entry.c b/src/adwaita/disfluid-cache-entry.c index 060fb96..d1da6b0 100644 --- a/src/adwaita/disfluid-cache-entry.c +++ b/src/adwaita/disfluid-cache-entry.c @@ -24,6 +24,7 @@ struct _DisfluidAdwCacheEntry { AdwPreferencesGroup parent_instance; + char *file_name; struct disfluid_cache_entry *cache_entry; GtkLabel *row_request_date; GtkLabel *row_response_date; @@ -43,7 +44,8 @@ G_DEFINE_TYPE (DisfluidAdwCacheEntry, typedef enum { - PROP_CACHE_ENTRY = 1, + PROP_FILE_NAME = 1, + PROP_CACHE_ENTRY = 2, /* ... */ N_PROPERTIES @@ -60,6 +62,11 @@ disfluid_adw_cache_entry_set_property (GObject * object, DisfluidAdwCacheEntry *self = DISFLUID_ADW_CACHE_ENTRY (object); switch ((DisfluidAdwCacheEntryProperty) property_id) { + case PROP_FILE_NAME: + disfluid_adw_cache_entry_set_file_name (self, + g_value_get_string (value)); + break; + case PROP_CACHE_ENTRY: assert (G_VALUE_TYPE (value) == DISFLUID_TYPE_CACHE_ENTRY); disfluid_adw_cache_entry_set_cache_entry (self, @@ -80,6 +87,9 @@ disfluid_adw_cache_entry_get_property (GObject * object, DisfluidAdwCacheEntry *self = DISFLUID_ADW_CACHE_ENTRY (object); switch ((DisfluidAdwCacheEntryProperty) property_id) { + case PROP_FILE_NAME: + g_value_set_string (value, self->file_name); + break; case PROP_CACHE_ENTRY: if (self->cache_entry != NULL) { @@ -101,6 +111,7 @@ static void disfluid_adw_cache_entry_finalize (GObject * obj) { DisfluidAdwCacheEntry *self = DISFLUID_ADW_CACHE_ENTRY (obj); + free (self->file_name); disfluid_cache_entry_free (self->cache_entry); G_OBJECT_CLASS (disfluid_adw_cache_entry_parent_class)->finalize (obj); } @@ -112,6 +123,9 @@ disfluid_adw_cache_entry_class_init (DisfluidAdwCacheEntryClass * klass) object_class->set_property = disfluid_adw_cache_entry_set_property; object_class->get_property = disfluid_adw_cache_entry_get_property; object_class->finalize = disfluid_adw_cache_entry_finalize; + obj_properties[PROP_FILE_NAME] = + g_param_spec_string ("file-name", _("File name"), _("Name of the file \ +of this cache entry"), NULL, G_PARAM_READWRITE); obj_properties[PROP_CACHE_ENTRY] = g_param_spec_boxed ("cache-entry", _("Cache entry"), _("Value of the cache entry \ @@ -124,6 +138,7 @@ static void disfluid_adw_cache_entry_init (DisfluidAdwCacheEntry * self) { /* Perform default initialization of self. */ + self->file_name = NULL; self->cache_entry = NULL; self->row_request_date = GTK_LABEL (gtk_label_new (_("Unset"))); self->row_response_date = GTK_LABEL (gtk_label_new (_("Unset"))); @@ -170,6 +185,28 @@ disfluid_adw_cache_entry_init (DisfluidAdwCacheEntry * self) GTK_WIDGET (row_invalidation_status)); } +void +disfluid_adw_cache_entry_set_file_name (DisfluidAdwCacheEntry * self, + const char *fname) +{ + free (self->file_name); + self->file_name = NULL; + if (fname) + { + self->file_name = g_strdup (fname); + } + if (self->file_name == NULL) + { + adw_preferences_group_set_title (ADW_PREFERENCES_GROUP (self), + _("Unknown cache entry")); + } + else + { + adw_preferences_group_set_title (ADW_PREFERENCES_GROUP (self), + self->file_name); + } +} + void disfluid_adw_cache_entry_set_cache_entry (DisfluidAdwCacheEntry * self, struct disfluid_cache_entry *value) @@ -235,19 +272,6 @@ disfluid_adw_cache_entry_set_cache_entry (DisfluidAdwCacheEntry * self, gtk_label_set_label (self->row_response_date, response_title); gtk_label_set_label (self->row_invalidation_status, invalidation_status_title); - char *file_name = - disfluid_cache_entry_get_file_name (self->cache_entry); - if (file_name == NULL) - { - adw_preferences_group_set_title (ADW_PREFERENCES_GROUP (self), - _("Unknown cache entry")); - } - else - { - adw_preferences_group_set_title (ADW_PREFERENCES_GROUP (self), - file_name); - } - free (file_name); g_free (request_title); g_free (response_title); g_free (invalidation_status_title); diff --git a/src/adwaita/disfluid-window.c b/src/adwaita/disfluid-window.c index bb8d592..8f9d87f 100644 --- a/src/adwaita/disfluid-window.c +++ b/src/adwaita/disfluid-window.c @@ -58,8 +58,6 @@ disfluid_adw_window_init (DisfluidAdwWindow * self) { abort (); } - disfluid_cache_entry_set_file_name (cache_entry, - "/tmp/test.disfluid-cache-entry"); GtkWidget *unique_content = g_object_new (DISFLUID_ADW_TYPE_CACHE_ENTRY, NULL); disfluid_adw_cache_entry_set_cache_entry (DISFLUID_ADW_CACHE_ENTRY diff --git a/src/libdisfluid/disfluid-cache-entry.h b/src/libdisfluid/disfluid-cache-entry.h index 272c9e8..ae91390 100644 --- a/src/libdisfluid/disfluid-cache-entry.h +++ b/src/libdisfluid/disfluid-cache-entry.h @@ -6,21 +6,9 @@ MAYBE_UNUSED static struct disfluid_cache_entry *cache_entry_alloc (void); MAYBE_UNUSED static void cache_entry_free (struct disfluid_cache_entry *entry); -MAYBE_UNUSED static struct disfluid_cache_entry - *cache_entry_from_file_name (const char *file_name); - -MAYBE_UNUSED static struct disfluid_cache_entry *cache_entry_from_fd (int fd); - MAYBE_UNUSED static struct disfluid_cache_entry *cache_entry_dup (const struct disfluid_cache_entry *entry); -MAYBE_UNUSED static int -cache_entry_save_other_file_name (const struct disfluid_cache_entry *entry, - const char *file_name); - -MAYBE_UNUSED static int -cache_entry_save_other_fd (const struct disfluid_cache_entry *entry, int fd); - MAYBE_UNUSED static void cache_entry_set_request_date (struct disfluid_cache_entry *entry, const struct timespec *date); @@ -36,9 +24,8 @@ MAYBE_UNUSED static void cache_entry_set_invalidated (struct disfluid_cache_entry *entry, bool invalidated); -MAYBE_UNUSED static void -cache_entry_set_file_name (struct disfluid_cache_entry *entry, - const char *file_name); +MAYBE_UNUSED static int +cache_entry_load (struct disfluid_cache_entry *entry, const char *filename); MAYBE_UNUSED static void cache_entry_get_request_date (const struct disfluid_cache_entry *entry, @@ -51,9 +38,9 @@ cache_entry_get_response_date (const struct disfluid_cache_entry *entry, MAYBE_UNUSED static bool cache_entry_invalidated (const struct disfluid_cache_entry *entry); -MAYBE_UNUSED static char *cache_entry_get_file_name (const struct - disfluid_cache_entry - *entry); +MAYBE_UNUSED static int +cache_entry_save (const struct disfluid_cache_entry *entry, + const char *filename); # include @@ -61,10 +48,6 @@ MAYBE_UNUSED static char *cache_entry_get_file_name (const struct struct disfluid_cache_entry { - char *file_name; /* Might be NULL */ - int fd; /* Might be < 0, but must be >= 0 if there is a file_name. */ - off_t start; - off_t header_stop; struct timespec request_date; struct timespec response_date; bool invalidated; @@ -80,10 +63,6 @@ cache_entry_alloc (void) { abort (); } - ret->file_name = NULL; - ret->fd = -1; - ret->start = 0; - ret->header_stop = 0; ret->request_date.tv_sec = 0; ret->request_date.tv_nsec = 0; ret->response_date.tv_sec = 0; @@ -95,60 +74,15 @@ cache_entry_alloc (void) static void cache_entry_free (struct disfluid_cache_entry *entry) { - if (entry != NULL) - { - free (entry->file_name); - if (entry->fd >= 0) - { - close (entry->fd); - } - } free (entry); } -static struct disfluid_cache_entry * -cache_entry_from_file_name (const char *file_name) -{ - int fd = open (file_name, O_RDONLY, S_IRUSR); - if (fd < 0) - { - abort (); - } - struct disfluid_cache_entry *ret = cache_entry_from_fd (fd); - if (ret == NULL) - { - abort (); - } - cache_entry_set_file_name (ret, file_name); - return ret; -} - static struct disfluid_cache_entry * cache_entry_dup (const struct disfluid_cache_entry *entry) { struct disfluid_cache_entry *ret = cache_entry_alloc (); - if (ret != NULL && entry->fd >= 0) - { - ret->fd = dup (entry->fd); - if (ret->fd < 0) - { - cache_entry_free (ret); - ret = NULL; - } - } - if (ret != NULL && entry->file_name != NULL) - { - ret->file_name = strdup (entry->file_name); - if ((ret->file_name == NULL) != (entry->file_name == NULL)) - { - cache_entry_free (ret); - ret = NULL; - } - } if (ret != NULL) { - ret->start = entry->start; - ret->header_stop = entry->header_stop; ret->request_date.tv_sec = entry->request_date.tv_sec; ret->request_date.tv_nsec = entry->request_date.tv_nsec; ret->response_date.tv_sec = entry->response_date.tv_sec; @@ -158,391 +92,203 @@ cache_entry_dup (const struct disfluid_cache_entry *entry) return ret; } -static inline void -cache_entry_string_append (char **str, size_t *max, size_t *n, - size_t suffix_length, const char *suffix) +static void +cache_entry_set_request_date (struct disfluid_cache_entry *entry, + const struct timespec *date) { - if (*n + suffix_length > *max) - { - *str = realloc (*str, 2 * *max); - if (*str == NULL) - { - abort (); - } - *max *= 2; - cache_entry_string_append (str, max, n, suffix_length, suffix); - } - else - { - memcpy (&((*str)[*n]), suffix, suffix_length); - *n += suffix_length; - } + entry->request_date.tv_sec = date->tv_sec; + entry->request_date.tv_nsec = date->tv_nsec; } -static inline ssize_t -cache_entry_read_next_line_aux (int fd, size_t *restrict buffer_max, - size_t *restrict buffer_length, char **buffer, - size_t checked) +static void +cache_entry_set_response_date (struct disfluid_cache_entry *entry, + const struct timespec *date) { - while (checked + 1 < *buffer_length - && ((*buffer)[checked] != '\r' || (*buffer)[checked + 1] != '\n')) - { - checked++; - } - if (checked + 1 < *buffer_length) - { - return checked + 2; - } - if (*buffer_length == *buffer_max) - { - *buffer = realloc (*buffer, 2 * *buffer_max); - if (*buffer == NULL) - { - abort (); - } - *buffer_max *= 2; - } - assert (*buffer_max > *buffer_length); - ssize_t n_added = - read (fd, &((*buffer)[*buffer_length]), *buffer_max - *buffer_length); - if (n_added < 0) - { - abort (); - } - else if (n_added == 0) - { - return *buffer_length; - } - else - { - assert (*buffer_length + n_added <= *buffer_max); - *buffer_length += n_added; - } - return cache_entry_read_next_line_aux (fd, buffer_max, buffer_length, - buffer, checked); + entry->response_date.tv_sec = date->tv_sec; + entry->response_date.tv_nsec = date->tv_nsec; } -static inline void -cache_entry_read_next_line (int fd, size_t *restrict buffer_max, - size_t *restrict buffer_length, char **buffer, - char **line, size_t *line_length) +static void +cache_entry_invalidate (struct disfluid_cache_entry *entry) { - ssize_t length = - cache_entry_read_next_line_aux (fd, buffer_max, buffer_length, buffer, 0); - if (length < 0) - { - abort (); - } - *line = malloc (length + 1); - if (*line == NULL) - { - abort (); - } - *line_length = length; - memcpy (*line, *buffer, length); - (*line)[length] = '\0'; - memmove (*buffer, &((*buffer)[length]), *buffer_length - length); - *buffer_length -= length; + entry->invalidated = true; } -static inline void -cache_entry_enumerate_header_lines (const struct disfluid_cache_entry *entry, - void (*cb) (void *, const char *, - const char *), void *context) +static void +cache_entry_set_invalidated (struct disfluid_cache_entry *entry, + bool invalidated) { - off_t start_offset = lseek (entry->fd, 0, SEEK_CUR); - if (start_offset < 0) - { - abort (); - } - size_t buffer_max = 1; - size_t buffer_length = 0; - char *buffer = malloc (buffer_max); - if (buffer == NULL) - { - abort (); - } - char *line; - size_t line_length; - while (true) - { - cache_entry_read_next_line (entry->fd, &buffer_max, &buffer_length, - &buffer, &line, &line_length); - if (line == NULL) - { - /* Memory error */ - abort (); - } - if (STREQ (line, "\r\n")) - { - /* End of parse */ - ssize_t undo = buffer_length; - lseek (entry->fd, -undo, SEEK_CUR); - free (buffer); - free (line); - return; - } - char *header_name = malloc (line_length + 1); - if (header_name == NULL) - { - abort (); - } - size_t header_name_length = 0; - for (header_name_length = 0; - header_name_length < line_length - && line[header_name_length] != ':'; header_name_length++) - { - if (line[header_name_length] >= 'A' - && line[header_name_length] <= 'Z') - { - line[header_name_length] = - (line[header_name_length] - 'A') + 'a'; - } - header_name[header_name_length] = line[header_name_length]; - } - header_name[header_name_length] = '\0'; - if (line[header_name_length] != ':') - { - /* HTTP bad header, restore position */ - lseek (entry->fd, start_offset, SEEK_SET); - free (buffer); - free (line); - free (header_name); - return; - } - header_name_length++; - cb (context, header_name, &(line[header_name_length])); - free (line); - free (header_name); - } + entry->invalidated = invalidated; } -static inline void -cache_entry_parse_timespec (const char *data, struct timespec *ts) -{ - /* A timespec is represented as SSS.NNN where SSS is the number of seconds, and NNN the number of nanoseconds. */ - while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n') - { - data++; - } - struct timespec r = {.tv_sec = 0,.tv_nsec = 0 }; - size_t n_seconds_digits = 0; - for (n_seconds_digits = 0; - data[n_seconds_digits] >= '0' && data[n_seconds_digits] <= '9'; - n_seconds_digits++) - { - r.tv_sec *= 10; - r.tv_sec += (data[n_seconds_digits] - '0'); - } - data += n_seconds_digits; - const bool has_dot = (data[0] == '.'); - if (has_dot) - { - data++; - } - int nano_digits[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - for (size_t i = 0; i < 9 && data[i] >= '0' && data[i] <= '9'; i++) - { - nano_digits[i] = (data[i] - '0'); - } - for (size_t i = 0; i < 9; i++) - { - r.tv_nsec *= 10; - r.tv_nsec += nano_digits[i]; - } - ts->tv_sec = r.tv_sec; - ts->tv_nsec = r.tv_nsec; -} +/* The cache entry file is composed as: */ -static inline void -cache_entry_iterate_metadata (void *ctx, const char *header, - const char *value) +/* "disfluid c.entry" as the magic number on 16 bytes. */ +/* The request date on 12 bytes: 8 bytes, big endian, unsigned, for + the tv_sec field, and then 4 bytes, big endian, unsigned, for the + tv_nsec field (must be < 1e9). */ +/* The response date on 12 bytes. */ +/* On 1 byte: a value >= 128 if invalidated, < 128 if still valid. */ + +static int +cache_entry_really_read_n (int fd, size_t n, uint8_t * bytes, + bool *file_incomplete) { - struct disfluid_cache_entry *entry = ctx; - if (STREQ (header, "request-date")) - { - cache_entry_parse_timespec (value, &(entry->request_date)); - } - else if (STREQ (header, "response-date")) - { - cache_entry_parse_timespec (value, &(entry->response_date)); - } - else if (STREQ (header, "invalidated")) + size_t n_read = 0; + *file_incomplete = false; + while (n_read < n) { - while (*value == ' ' || *value == '\t' || *value == '\r' - || *value == '\n') + ssize_t n_this_time = read (fd, &(bytes[n_read]), n - n_read); + if (n_this_time < 0) { - value++; + return 1; } - if (STREQ (value, "true\r\n")) + else if (n_this_time == 0) { - entry->invalidated = true; + *file_incomplete = true; + return 1; } - else if (STREQ (value, "false\r\n")) + else { - entry->invalidated = false; + assert (n_this_time > 0); + n_read += n_this_time; } } + return 0; } -static inline void -cache_entry_scan (struct disfluid_cache_entry *entry) +static int +cache_entry_read_magic (int fd, bool *magic_ok, bool *file_incomplete) { - entry->start = lseek (entry->fd, 0, SEEK_CUR); - cache_entry_enumerate_header_lines (entry, cache_entry_iterate_metadata, - entry); - entry->header_stop = lseek (entry->fd, 0, SEEK_CUR); - /* Reset the file descriptor position, in case it is shared */ - lseek (entry->fd, entry->start, SEEK_SET); + uint8_t magic[16]; + *magic_ok = false; + *file_incomplete = false; + int err = cache_entry_really_read_n (fd, 16, magic, file_incomplete); + if (err) + { + return err; + } + if (memcmp (magic, "disfluid c.entry", 16) != 0) + { + *magic_ok = false; + return 1; + } + *magic_ok = true; + return 0; } -static struct disfluid_cache_entry * -cache_entry_from_fd (int fd) +static int +cache_entry_read_timespec (int fd, struct timespec *timespec, + bool *file_incomplete) { - struct disfluid_cache_entry *ret = cache_entry_alloc (); - if (ret == NULL) + uint8_t data[12]; + *file_incomplete = false; + int err = cache_entry_really_read_n (fd, 12, data, file_incomplete); + if (err) { - abort (); + return err; } - ret->fd = dup (fd); - if (ret->fd < 0) + timespec->tv_sec = 0; + timespec->tv_nsec = 0; + for (size_t i = 0; i < 8; i++) { - abort (); + timespec->tv_sec *= 256; + timespec->tv_sec += data[i]; } - cache_entry_scan (ret); - assert (ret->start >= 0); - assert (ret->header_stop >= 0); - if (ret->header_stop == ret->start) + for (size_t i = 8; i < 12; i++) { - /* Assume that reading failed. */ - cache_entry_free (ret); - ret = NULL; + timespec->tv_nsec *= 256; + timespec->tv_nsec += data[i]; } - return ret; + return 0; } static int -cache_entry_save_other_file_name (const struct disfluid_cache_entry *entry, - const char *file_name) +cache_entry_load_aux (struct disfluid_cache_entry *entry, + const char *filename, bool *file_incomplete) { - int fd = open (file_name, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP); + *file_incomplete = false; + int fd = open (filename, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd < 0) { - return errno; + return -1; } - return cache_entry_save_other_fd (entry, fd); -} - -static int -cache_entry_save_other_fd (const struct disfluid_cache_entry *entry, int fd) -{ - char *set_request_date; - char *set_response_date; - char *set_invalidated; - char *meta_header; - int status; - status = - asprintf (&set_request_date, "Request-Date: %lld.%09ld\r\n", - (long long) entry->request_date.tv_sec, - (long) entry->request_date.tv_nsec); - if (status < 0) + bool magic_ok = false; + struct timespec request_date, response_date; + bool invalidated; + int error = 0; + error = cache_entry_read_magic (fd, &magic_ok, file_incomplete); + if (error) { - abort (); + goto cleanup; } - status = - asprintf (&set_response_date, "Response-Date: %lld.%09ld\r\n", - (long long) entry->response_date.tv_sec, - (long) entry->response_date.tv_nsec); - if (status < 0) + if (!magic_ok) { - abort (); + error = -1; + goto cleanup; } - if (entry->invalidated) + error = cache_entry_read_timespec (fd, &request_date, file_incomplete); + if (error) { - status = asprintf (&set_invalidated, "Invalidated: true\r\n"); - if (status < 0) - { - abort (); - } + goto cleanup; } - else + error = cache_entry_read_timespec (fd, &response_date, file_incomplete); + if (error) { - set_invalidated = strdup (""); - if (set_invalidated == NULL) - { - abort (); - } + goto cleanup; } - status = - asprintf (&meta_header, "%s%s%s\r\n", set_request_date, set_response_date, - set_invalidated); - free (set_request_date); - free (set_response_date); - free (set_invalidated); - if (status < 0) + uint8_t flags; + ssize_t n_flags_read = read (fd, &flags, 1); + if (n_flags_read == 0) { - abort (); + *file_incomplete = true; } - size_t n_written = 0; - while (n_written < strlen (meta_header)) + if (n_flags_read < 0) { - ssize_t n_written_this_time = write (fd, &(meta_header[n_written]), - strlen (meta_header) - n_written); - if (n_written_this_time <= 0) - { - free (meta_header); - return 2; - } - n_written += n_written_this_time; + error = -1; + goto cleanup; } - free (meta_header); - return 0; + invalidated = ((flags & 0x80) != 0); + cache_entry_set_request_date (entry, &request_date); + cache_entry_set_response_date (entry, &response_date); + cache_entry_set_invalidated (entry, invalidated); +cleanup: + close (fd); + return error; } -static void -cache_entry_set_request_date (struct disfluid_cache_entry *entry, - const struct timespec *date) -{ - entry->request_date.tv_sec = date->tv_sec; - entry->request_date.tv_nsec = date->tv_nsec; -} - -static void -cache_entry_set_response_date (struct disfluid_cache_entry *entry, - const struct timespec *date) -{ - entry->response_date.tv_sec = date->tv_sec; - entry->response_date.tv_nsec = date->tv_nsec; -} - -static void -cache_entry_invalidate (struct disfluid_cache_entry *entry) -{ - cache_entry_set_invalidated (entry, true); -} - -static void -cache_entry_set_invalidated (struct disfluid_cache_entry *entry, - bool invalidated) +static char * +cache_entry_backup_file_name (const char *filename) { - entry->invalidated = invalidated; + char *backup_file = malloc (strlen (filename) + strlen ("~") + 1); + if (backup_file == NULL) + { + return NULL; + } + strcpy (backup_file, filename); + strcat (backup_file, "~"); + return backup_file; } -static void -cache_entry_set_file_name (struct disfluid_cache_entry *entry, - const char *file_name) +static int +cache_entry_load (struct disfluid_cache_entry *entry, const char *filename) { - free (entry->file_name); - entry->file_name = NULL; - if (file_name != NULL) - { - entry->file_name = strdup (file_name); - if (entry->file_name == NULL) + /* Try to read from filename. If the file does not exist, do not + even try to read a backup. If the file exists but is incomplete, + read the backup file. */ + bool file_incomplete = false; + int error = cache_entry_load_aux (entry, filename, &file_incomplete); + if (file_incomplete) + { + char *backup_file = cache_entry_backup_file_name (filename); + if (backup_file == NULL) { abort (); } + error = cache_entry_load_aux (entry, backup_file, &file_incomplete); + free (backup_file); } + return error; } static void @@ -567,19 +313,161 @@ cache_entry_invalidated (const struct disfluid_cache_entry *entry) return entry->invalidated; } -static char * -cache_entry_get_file_name (const struct disfluid_cache_entry *entry) +static int +cache_entry_really_write_n (int fd, size_t n, const uint8_t * data) { - if (entry->file_name == NULL) + while (n != 0) { - return NULL; + ssize_t n_this_time = write (fd, data, n); + if (n_this_time < 0) + { + return -1; + } + else if (n_this_time == 0) + { + /* Impossible */ + abort (); + } + else + { + assert (n_this_time > 0); + n -= n_this_time; + data = &(data[n_this_time]); + } } - char *ret = strdup (entry->file_name); - if (ret == NULL) + return 0; +} + +static int +cache_entry_write_magic (int fd) +{ + static const uint8_t magic[16] = "disfluid c.entry"; + return cache_entry_really_write_n (fd, 16, magic); +} + +static int +cache_entry_write_timespec (int fd, const struct timespec *ts) +{ + uint8_t data[12]; + time_t tv_sec = ts->tv_sec; + long tv_nsec = ts->tv_nsec; + assert (tv_nsec >= 0); + for (size_t i = 8; i-- > 0;) + { + data[i] = tv_sec % 256; + tv_sec /= 256; + } + for (size_t i = 12; i-- > 8;) + { + data[i] = tv_nsec % 256; + tv_nsec /= 256; + } + return cache_entry_really_write_n (fd, 12, data); +} + +static int +cache_entry_save (const struct disfluid_cache_entry *entry, + const char *filename) +{ + /* To save a file: */ + /* 1. Lock it. */ + /* 2. Copy it to `filename~' with copy_file_range. */ + /* 3. Overwrite `filename'. */ + /* 4. Unlock it. */ + int error = 0; + int actual_fd, backup_fd; + actual_fd = open (filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); + char *backup_file = cache_entry_backup_file_name (filename); + if (backup_file == NULL) { abort (); } - return ret; + backup_fd = + open (backup_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); + free (backup_file); + if (actual_fd < 0 || backup_fd < 0) + { + error = -1; + goto cleanup; + } + struct flock lock = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 + }; + int lock_error = fcntl (actual_fd, F_SETLKW, &lock); + if (lock_error) + { + error = -2; + goto cleanup; + } + ssize_t n_copied = 0; + do + { + /* Copy by blocks of 2 MiB. I don’t care the size, I don’t have + to allocate it anyway. */ + n_copied = + copy_file_range (actual_fd, NULL, backup_fd, NULL, 2 * 1024 * 1024, + 0); + if (n_copied < 0) + { + error = -3; + goto cleanup; + } + } + while (n_copied != 0); + off_t to_start = lseek (actual_fd, SEEK_SET, 0); + if (to_start != 0) + { + error = -4; + goto cleanup; + } + if (ftruncate (actual_fd, 0) != 0) + { + error = -5; + goto cleanup; + } + assert (error == 0); + error = cache_entry_write_magic (actual_fd); + if (error) + { + error = -6; + goto cleanup; + } + error = cache_entry_write_timespec (actual_fd, &(entry->request_date)); + if (error) + { + error = -7; + goto cleanup; + } + error = cache_entry_write_timespec (actual_fd, &(entry->response_date)); + if (error) + { + error = -8; + goto cleanup; + } + uint8_t flags = 0; + if (entry->invalidated) + { + flags = 0x80; + } + error = cache_entry_really_write_n (actual_fd, 1, &flags); + if (error) + { + error = -9; + goto cleanup; + } +cleanup: + if (actual_fd >= 0) + { + close (actual_fd); + } + if (backup_fd >= 0) + { + close (backup_fd); + } + return error; } #endif /* DISFLUID_DISFLUID_CACHE_ENTRY_INCLUDED */ diff --git a/src/libdisfluid/disfluid-tests.h b/src/libdisfluid/disfluid-tests.h index 0a40f20..0819bb0 100644 --- a/src/libdisfluid/disfluid-tests.h +++ b/src/libdisfluid/disfluid-tests.h @@ -11,8 +11,8 @@ static inline char *run_tests (size_t *n_tests, size_t *n_errors); # include "disfluid-version.h" # include "disfluid-cache-entry.h" -static inline int -test_open_file_with_content (const char *content) +static inline char * +test_tmp_file_name_with_content (size_t n, const uint8_t * content) { char name[] = "/tmp/disfluid-cache-entry-test-XXXXXX"; int fd = mkstemp (name); @@ -20,27 +20,34 @@ test_open_file_with_content (const char *content) { abort (); } - size_t n_written = 0; - while (n_written < strlen (content)) + while (n > 0) { - ssize_t next = - write (fd, &(content[n_written]), strlen (content) - n_written); + ssize_t next = write (fd, content, n); if (next <= 0) { abort (); } - n_written += next; + else if (next == 0) + { + /* Impossible */ + abort (); + } + else + { + assert (next > 0); + assert ((size_t) next <= n); + n -= next; + content = &(content[next]); + } } - remove (name); - lseek (fd, 0, SEEK_SET); - return fd; -} - -static inline void -fwrite_text (FILE * file, const char *text) -{ - fwrite (text, 1, strlen (text), file); - fflush (file); + close (fd); + char *ret = malloc (strlen (name) + 1); + if (ret == NULL) + { + abort (); + } + strcpy (ret, name); + return ret; } /* *INDENT-OFF* */ @@ -56,204 +63,312 @@ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_invalid_cache_entry_file) +START_TEST (test_read_invalid_magic_number) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("Hello, world!"); - if (fd_source < 0) + uint8_t data[16 + 12 + 12 + 1] = { 0 }; + memcpy (data, "invalid magic no", 16); + char *filename = + test_tmp_file_name_with_content (sizeof (data) / sizeof (data[0]), data); + if (filename == NULL) + { + abort (); + } + struct disfluid_cache_entry *entry = cache_entry_alloc (); + if (entry == NULL) { abort (); } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - ck_assert_ptr_null (entry); - close (fd_source); + int error = cache_entry_load (entry, filename); + cache_entry_free (entry); + remove (filename); + free (filename); + ck_assert_int_ne (error, 0); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_no_date) +START_TEST (test_read_inexistent_file) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("Invalidated: true\r\n\r\n"); - if (fd_source < 0) + static const char *filename = + "/disfluid-this-file-should-not-exist-to-pass-tests"; + struct disfluid_cache_entry *entry = cache_entry_alloc (); + if (entry == NULL) { abort (); } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - ck_assert_ptr_ne (entry, NULL); - close (fd_source); - struct timespec request_date = {.tv_sec = 42,.tv_nsec = 69 }; - struct timespec response_date = {.tv_sec = 42,.tv_nsec = 69 }; - disfluid_cache_entry_get_request_date (entry, &request_date); - disfluid_cache_entry_get_request_date (entry, &response_date); - ck_assert_int_eq (request_date.tv_sec, 0); - ck_assert_int_eq (request_date.tv_nsec, 0); - ck_assert_int_eq (response_date.tv_sec, 0); - ck_assert_int_eq (response_date.tv_nsec, 0); - ck_assert ((bool) disfluid_cache_entry_invalidated (entry)); - disfluid_cache_entry_free (entry); + int error = cache_entry_load (entry, filename); + cache_entry_free (entry); + ck_assert_int_ne (error, 0); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_whole_seconds) +START_TEST (test_read_inexistent_file_with_valid_backup) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("\ -Request-Date: 42\r\n\ -Response-Date: 69\r\n\ -Invalidated: true\r\n\ -\r\n"); - if (fd_source < 0) + uint8_t data[16 + 12 + 12 + 1] = { 0 }; + memcpy (data, "disfluid c.entry", 16); + char *filename = + test_tmp_file_name_with_content (sizeof (data) / sizeof (data[0]), data); + if (filename == NULL) + { + abort (); + } + char *filename_backup = malloc (strlen (filename) + strlen ("~") + 1); + if (filename_backup == NULL) + { + abort (); + } + strcpy (filename_backup, filename); + strcat (filename_backup, "~"); + rename (filename, filename_backup); + struct disfluid_cache_entry *entry = cache_entry_alloc (); + if (entry == NULL) { abort (); } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - close (fd_source); - ck_assert_ptr_ne (entry, NULL); - struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 }; - struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 }; - disfluid_cache_entry_get_request_date (entry, &request_date); - disfluid_cache_entry_get_response_date (entry, &response_date); - ck_assert_int_eq (request_date.tv_sec, 42); - ck_assert_int_eq (request_date.tv_nsec, 0); - ck_assert_int_eq (response_date.tv_sec, 69); - ck_assert_int_eq (response_date.tv_nsec, 0); - ck_assert ((bool) disfluid_cache_entry_invalidated (entry)); - disfluid_cache_entry_free (entry); + int error = cache_entry_load (entry, filename); + cache_entry_free (entry); + remove (filename_backup); + free (filename_backup); + free (filename); + ck_assert_int_ne (error, 0); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ +static inline void +test_check_partial_situation (size_t n_partial) +{ + uint8_t data[16 + 12 + 12 + 1] = { 0 }; + memcpy (data, "disfluid c.entry", 16); + /* The data is all zeros */ + uint8_t backup_data[16 + 12 + 12 + 1] = { 0 }; + memcpy (backup_data, "disfluid c.entry", 16); + backup_data[16 + 8 - 1] = 1; + backup_data[16 + 12 - 1] = 2; + backup_data[16 + 12 + 8 - 1] = 3; + backup_data[16 + 12 + 12 - 1] = 4; + backup_data[16 + 12 + 12 + 1 - 1] = 0x80; + /* The backup has a request date of 1s + 2ns, response of 3s + 4ns, + and is invalidated. */ + assert (n_partial <= sizeof (data) / sizeof (data[0])); + char *filename = test_tmp_file_name_with_content (n_partial, data); + if (filename == NULL) + { + abort (); + } + char *filename_backup = malloc (strlen (filename) + strlen ("~") + 1); + if (filename_backup == NULL) + { + abort (); + } + strcpy (filename_backup, filename); + strcat (filename_backup, "~"); + char *backup_content_filename = + test_tmp_file_name_with_content (sizeof (backup_data) / + sizeof (backup_data[0]), backup_data); + if (backup_content_filename == NULL) + { + abort (); + } + rename (backup_content_filename, filename_backup); + /* Now, filename is a partial file, and filename~ is a valid + backup. */ + struct disfluid_cache_entry *entry = cache_entry_alloc (); + if (entry == NULL) + { + abort (); + } + int error = cache_entry_load (entry, filename); + ck_assert_int_eq (error, 0); + if (n_partial < sizeof (data) / sizeof (data[0])) + { + /* This should be the content of the backup file. */ + struct timespec request, response; + cache_entry_get_request_date (entry, &request); + cache_entry_get_response_date (entry, &response); + bool invalidated = cache_entry_invalidated (entry); + ck_assert_int_eq (request.tv_sec, 1); + ck_assert_int_eq (request.tv_nsec, 2); + ck_assert_int_eq (response.tv_sec, 3); + ck_assert_int_eq (response.tv_nsec, 4); + ck_assert (invalidated); + } + else + { + /* This should be the content of the full file. */ + struct timespec request, response; + cache_entry_get_request_date (entry, &request); + cache_entry_get_response_date (entry, &response); + bool invalidated = cache_entry_invalidated (entry); + ck_assert_int_eq (request.tv_sec, 0); + ck_assert_int_eq (request.tv_nsec, 0); + ck_assert_int_eq (response.tv_sec, 0); + ck_assert_int_eq (response.tv_nsec, 0); + ck_assert (!invalidated); + } + cache_entry_free (entry); + free (backup_content_filename); + free (filename_backup); + free (filename); +} + /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_whole_seconds_dot) +START_TEST (test_read_file_with_valid_backup) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("\ -Request-Date: 42.\r\n\ -Response-Date: 69.\r\n\ -Invalidated: true\r\n\ -\r\n"); - if (fd_source < 0) + uint8_t data[16 + 12 + 12 + 1] = { 0 }; + memcpy (data, "disfluid c.entry", 16); + /* The data is all zeros */ + uint8_t backup_data[16 + 12 + 12 + 1] = { 0 }; + memcpy (backup_data, "disfluid c.entry", 16); + backup_data[16 + 8 - 1] = 1; + backup_data[16 + 12 - 1] = 2; + backup_data[16 + 12 + 8 - 1] = 3; + backup_data[16 + 12 + 12 - 1] = 4; + backup_data[16 + 12 + 12 + 1 - 1] = 0x80; + /* The backup has a request date of 1s + 2ns, response of 3s + 4ns, + and is invalidated. */ + char *filename = + test_tmp_file_name_with_content (sizeof (data) / sizeof (data[0]), data); + if (filename == NULL) + { + abort (); + } + char *filename_backup = malloc (strlen (filename) + strlen ("~") + 1); + if (filename_backup == NULL) + { + abort (); + } + strcpy (filename_backup, filename); + strcat (filename_backup, "~"); + rename (filename, filename_backup); + struct disfluid_cache_entry *entry = cache_entry_alloc (); + if (entry == NULL) { abort (); } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - close (fd_source); - ck_assert_ptr_ne (entry, NULL); - struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 }; - struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 }; - disfluid_cache_entry_get_request_date (entry, &request_date); - disfluid_cache_entry_get_response_date (entry, &response_date); - ck_assert_int_eq (request_date.tv_sec, 42); - ck_assert_int_eq (request_date.tv_nsec, 0); - ck_assert_int_eq (response_date.tv_sec, 69); - ck_assert_int_eq (response_date.tv_nsec, 0); - ck_assert ((bool) disfluid_cache_entry_invalidated (entry)); - disfluid_cache_entry_free (entry); + int error = cache_entry_load (entry, filename); + remove (filename_backup); + free (filename_backup); + free (filename); + cache_entry_free (entry); + ck_assert_int_ne (error, 0); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_milliseconds) +START_TEST (test_read_cache_file_partial_0) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("\ -Request-Date: 42.123\r\n\ -Response-Date: 69.456\r\n\ -Invalidated: true\r\n\ -\r\n"); - if (fd_source < 0) - { - abort (); - } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - close (fd_source); - ck_assert_ptr_ne (entry, NULL); - struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 }; - struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 }; - disfluid_cache_entry_get_request_date (entry, &request_date); - disfluid_cache_entry_get_response_date (entry, &response_date); - ck_assert_int_eq (request_date.tv_sec, 42); - ck_assert_int_eq (request_date.tv_nsec, 123000000); - ck_assert_int_eq (response_date.tv_sec, 69); - ck_assert_int_eq (response_date.tv_nsec, 456000000); - ck_assert ((bool) disfluid_cache_entry_invalidated (entry)); - disfluid_cache_entry_free (entry); + test_check_partial_situation (0); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_precise) +START_TEST (test_read_cache_file_partial_1) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("\ -Request-Date: 42.123456789\r\n\ -Response-Date: 69.456789123\r\n\ -Invalidated: true\r\n\ -\r\n"); - if (fd_source < 0) - { - abort (); - } - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - close (fd_source); - ck_assert_ptr_ne (entry, NULL); - struct timespec request_date = {.tv_sec = 1,.tv_nsec = 3 }; - struct timespec response_date = {.tv_sec = 2,.tv_nsec = 4 }; - disfluid_cache_entry_get_request_date (entry, &request_date); - disfluid_cache_entry_get_response_date (entry, &response_date); - ck_assert_int_eq (request_date.tv_sec, 42); - ck_assert_int_eq (request_date.tv_nsec, 123456789); - ck_assert_int_eq (response_date.tv_sec, 69); - ck_assert_int_eq (response_date.tv_nsec, 456789123); - ck_assert ((bool) disfluid_cache_entry_invalidated (entry)); - disfluid_cache_entry_free (entry); + test_check_partial_situation (1); } /* *INDENT-OFF* */ END_TEST /* *INDENT-ON* */ /* *INDENT-OFF* */ -START_TEST (test_read_cache_entry_ambiguous) +START_TEST (test_read_cache_file_partial_16) /* *INDENT-ON* */ { - int fd_source = test_open_file_with_content ("\ -Request-Date: 42.123456789\r\n\ -Response-Date: 69.456789123\r\n"); - /* Notice how we don’t know if we’re at the end because of a missing - \r\n. */ - if (fd_source < 0) - { - abort (); - } - /* Is it the end? */ - struct disfluid_cache_entry *entry = - disfluid_cache_entry_from_fd (fd_source); - ck_assert_ptr_eq (entry, NULL); - close (fd_source); + test_check_partial_situation (16); +} +/* *INDENT-OFF* */ +END_TEST + +/* *INDENT-ON* */ + +START_TEST (test_read_cache_file_partial_17) +/* *INDENT-ON* */ +{ + test_check_partial_situation (17); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_28) +/* *INDENT-ON* */ +{ + test_check_partial_situation (28); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_29) +/* *INDENT-ON* */ +{ + test_check_partial_situation (29); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_36) +/* *INDENT-ON* */ +{ + test_check_partial_situation (36); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_37) +/* *INDENT-ON* */ +{ + test_check_partial_situation (37); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_40) +/* *INDENT-ON* */ +{ + test_check_partial_situation (40); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +START_TEST (test_read_cache_file_partial_41) +/* *INDENT-ON* */ +{ + test_check_partial_situation (41); } /* *INDENT-OFF* */ END_TEST @@ -264,30 +379,84 @@ START_TEST (test_write_cache_entry) /* *INDENT-ON* */ { - int fd_dest = test_open_file_with_content ("hehe"); - if (fd_dest < 0) + uint8_t old_data[16 + 12 + 12 + 1] = { 0 }; + memcpy (old_data, "disfluid c.entry", 16); + old_data[16 + 8 - 1] = 1; + old_data[16 + 12 - 1] = 2; + old_data[16 + 12 + 8 - 1] = 3; + old_data[16 + 12 + 12 - 1] = 4; + old_data[16 + 12 + 12 + 1 - 1] = 0x80; + /* The old data has a request date of 1s + 2ns, response of 3s + + 4ns, and is invalidated. */ + char *filename = + test_tmp_file_name_with_content (sizeof (old_data) / sizeof (old_data[0]), + old_data); + if (filename == NULL) + { + abort (); + } + char *filename_backup = malloc (strlen (filename) + strlen ("~") + 1); + if (filename_backup == NULL) { abort (); } - struct disfluid_cache_entry *entry = disfluid_cache_entry_alloc (); + strcpy (filename_backup, filename); + strcat (filename_backup, "~"); + struct disfluid_cache_entry *entry = cache_entry_alloc (); if (entry == NULL) { abort (); } - const struct timespec rq = { - .tv_sec = 123, - .tv_nsec = 456 - }; - const struct timespec rs = { - .tv_sec = 789, - .tv_nsec = 101112 - }; - disfluid_cache_entry_set_request_date (entry, &rq); - disfluid_cache_entry_set_response_date (entry, &rs); - int error = disfluid_cache_entry_save_other_fd (entry, fd_dest); + struct timespec request, response; + request.tv_sec = 10; + request.tv_nsec = 11; + response.tv_sec = 12; + response.tv_nsec = 13; + cache_entry_set_request_date (entry, &request); + cache_entry_set_response_date (entry, &response); + cache_entry_set_invalidated (entry, false); + int error = cache_entry_save (entry, filename); ck_assert_int_eq (error, 0); - disfluid_cache_entry_free (entry); - close (fd_dest); + uint8_t check_backup_data[16 + 12 + 12 + 1]; + uint8_t check_data[16 + 12 + 12 + 1]; + assert (sizeof (check_data) == sizeof (old_data)); + assert (sizeof (check_backup_data) == sizeof (old_data)); + bool file_incomplete, backup_file_incomplete; + int fd = open (filename, O_RDONLY, 0); + int fd_backup = open (filename_backup, O_RDONLY, 0); + if (fd < 0 || fd_backup < 0) + { + abort (); + } + int check_error = + (cache_entry_really_read_n + (fd, sizeof (check_data) / sizeof (check_data[0]), check_data, + &file_incomplete) != 0); + int check_backup_error = + (cache_entry_really_read_n + (fd_backup, sizeof (check_backup_data) / sizeof (check_backup_data[0]), + check_backup_data, &backup_file_incomplete) != 0); + ck_assert_int_eq (check_error, 0); + ck_assert_int_eq (check_backup_error, 0); + ck_assert (!file_incomplete); + ck_assert (!backup_file_incomplete); + ck_assert_int_eq (memcmp (check_data, "disfluid c.entry", 16), 0); + ck_assert_int_eq (check_data[16 + 8 - 1], 10); + ck_assert_int_eq (check_data[16 + 12 - 1], 11); + ck_assert_int_eq (check_data[16 + 12 + 8 - 1], 12); + ck_assert_int_eq (check_data[16 + 12 + 12 - 1], 13); + ck_assert_int_eq (check_data[16 + 12 + 12 + 1 - 1], 0); + ck_assert_int_eq (memcmp + (check_backup_data, old_data, + sizeof (check_backup_data) / + sizeof (check_backup_data[0])), 0); + close (fd); + close (fd_backup); + remove (filename); + remove (filename_backup); + cache_entry_free (entry); + free (filename_backup); + free (filename); } /* *INDENT-OFF* */ END_TEST @@ -327,13 +496,20 @@ run_tests (size_t *n_tests, size_t *n_errors) tcase_add_test (general, test_check_version); suite_add_tcase (suite, general); TCase *cache_entry = tcase_create (_("disfluid cache entry files")); - tcase_add_test (cache_entry, test_read_invalid_cache_entry_file); - tcase_add_test (cache_entry, test_read_cache_entry_no_date); - tcase_add_test (cache_entry, test_read_cache_entry_whole_seconds); - tcase_add_test (cache_entry, test_read_cache_entry_whole_seconds_dot); - tcase_add_test (cache_entry, test_read_cache_entry_milliseconds); - tcase_add_test (cache_entry, test_read_cache_entry_precise); - tcase_add_test (cache_entry, test_read_cache_entry_ambiguous); + tcase_add_test (cache_entry, test_read_invalid_magic_number); + tcase_add_test (cache_entry, test_read_inexistent_file); + tcase_add_test (cache_entry, test_read_inexistent_file_with_valid_backup); + tcase_add_test (cache_entry, test_read_file_with_valid_backup); + tcase_add_test (cache_entry, test_read_cache_file_partial_0); + tcase_add_test (cache_entry, test_read_cache_file_partial_1); + tcase_add_test (cache_entry, test_read_cache_file_partial_16); + tcase_add_test (cache_entry, test_read_cache_file_partial_17); + tcase_add_test (cache_entry, test_read_cache_file_partial_28); + tcase_add_test (cache_entry, test_read_cache_file_partial_29); + tcase_add_test (cache_entry, test_read_cache_file_partial_36); + tcase_add_test (cache_entry, test_read_cache_file_partial_37); + tcase_add_test (cache_entry, test_read_cache_file_partial_40); + tcase_add_test (cache_entry, test_read_cache_file_partial_41); tcase_add_test (cache_entry, test_write_cache_entry); suite_add_tcase (suite, cache_entry); SRunner *runner = srunner_create (suite); diff --git a/src/libdisfluid/main.c b/src/libdisfluid/main.c index c254a4a..24efb51 100644 --- a/src/libdisfluid/main.c +++ b/src/libdisfluid/main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -126,38 +127,12 @@ disfluid_cache_entry_free (struct disfluid_cache_entry *entry) return cache_entry_free (entry); } -struct disfluid_cache_entry * -disfluid_cache_entry_from_file_name (const char *file_name) -{ - return cache_entry_from_file_name (file_name); -} - -struct disfluid_cache_entry * -disfluid_cache_entry_from_fd (int fd) -{ - return cache_entry_from_fd (fd); -} - struct disfluid_cache_entry * disfluid_cache_entry_dup (const struct disfluid_cache_entry *entry) { return cache_entry_dup (entry); } -int -disfluid_cache_entry_save_other_file_name (const struct disfluid_cache_entry - *entry, const char *file_name) -{ - return cache_entry_save_other_file_name (entry, file_name); -} - -int -disfluid_cache_entry_save_other_fd (const struct disfluid_cache_entry *entry, - int fd) -{ - return cache_entry_save_other_fd (entry, fd); -} - void disfluid_cache_entry_set_request_date (struct disfluid_cache_entry *entry, const struct timespec *date) @@ -185,11 +160,11 @@ disfluid_cache_entry_set_invalidated (struct disfluid_cache_entry *entry, cache_entry_set_invalidated (entry, invalidated); } -void -disfluid_cache_entry_set_file_name (struct disfluid_cache_entry *entry, - const char *file_name) +int +disfluid_cache_entry_load (struct disfluid_cache_entry *entry, + const char *filename) { - return cache_entry_set_file_name (entry, file_name); + return cache_entry_load (entry, filename); } void @@ -212,8 +187,9 @@ disfluid_cache_entry_invalidated (const struct disfluid_cache_entry *entry) return cache_entry_invalidated (entry); } -char * -disfluid_cache_entry_get_file_name (const struct disfluid_cache_entry *entry) +int +disfluid_cache_entry_save (const struct disfluid_cache_entry *entry, + const char *filename) { - return cache_entry_get_file_name (entry); + return cache_entry_save (entry, filename); } diff --git a/tests/run-unit-tests.c b/tests/run-unit-tests.c index 5361b72..96fa35e 100644 --- a/tests/run-unit-tests.c +++ b/tests/run-unit-tests.c @@ -9,6 +9,7 @@ int main (int argc, char *argv[]) { + (void) argc; set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, relocate (LOCALEDIR)); -- cgit v1.2.3