*/
#include <stdio.h>
+#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <glib.h>
log->data = bluesky_string_new_from_gstring(buf);
return log;
}
+
+/* Reconstruct the inode map from data stored in the cloud. */
+static void bluesky_inode_map_deserialize(BlueSkyFS *fs, BlueSkyCloudLog *imap)
+{
+ g_mutex_lock(imap->lock);
+ bluesky_cloudlog_fetch(imap);
+ g_assert(imap->data != NULL);
+ g_assert(imap->data->len == 16 * imap->links->len);
+ //uint64_t *inum_range = (uint64_t *)imap->data->data;
+ for (int i = 0; i < imap->links->len; i++) {
+ //int64_t start = GUINT64_FROM_LE(*inum_range++);
+ //int64_t end = GUINT64_FROM_LE(*inum_range++);
+ BlueSkyCloudLog *section = g_array_index(imap->links,
+ BlueSkyCloudLog *, i);
+ g_mutex_lock(section->lock);
+ bluesky_cloudlog_fetch(section);
+ g_print("Loaded cloudlog item (%zd bytes)\n", section->data->len);
+
+ uint64_t *inum = (uint64_t *)section->data->data;
+ for (int j = 0; j < section->links->len; j++) {
+ InodeMapEntry *entry;
+ entry = bluesky_inode_map_lookup(fs->inode_map, *inum, 1);
+ entry->inum = GUINT64_FROM_LE(*inum);
+ entry->item = g_array_index(section->links,
+ BlueSkyCloudLog *, j);
+ bluesky_cloudlog_ref(entry->item);
+ entry->id = entry->item->id;
+ entry->location = entry->item->location;
+ fs->next_inum = MAX(fs->next_inum, entry->inum + 1);
+ inum++;
+ }
+ g_mutex_unlock(section->lock);
+ }
+ g_mutex_unlock(imap->lock);
+}
+
+/* Find the most recent checkpoint record in the cloud and reload inode map
+ * data from it to initialize the filesystem. Returns a boolean indicating
+ * whether a checkpoint was found and loaded or not. */
+gboolean bluesky_checkpoint_load(BlueSkyFS *fs)
+{
+ char *last_segment = bluesky_store_lookup_last(fs->store, "log-");
+ if (last_segment == NULL)
+ return FALSE;
+
+ g_print("Last cloud log segment: %s\n", last_segment);
+ int seq = atoi(last_segment + 13);
+ fs->log_state->location.sequence = seq + 1;
+
+ BlueSkyRCStr *last = bluesky_store_get(fs->store, last_segment);
+ g_free(last_segment);
+ if (last == NULL) {
+ g_warning("Unable to fetch last log segment from cloud!");
+ return FALSE;
+ }
+
+ /* Scan through the contents of the last log segment to find a checkpoint
+ * record. We need to do a linear scan since at this point we don't have a
+ * direct pointer; once we have the last commit record then all other data
+ * can be loaded by directly following pointers. */
+ const char *buf = last->data;
+ size_t len = last->len;
+ const char *checkpoint = NULL;
+ size_t checkpoint_size = 0;
+ while (len > sizeof(struct cloudlog_header)) {
+ struct cloudlog_header *header = (struct cloudlog_header *)buf;
+ if (memcmp(header->magic, CLOUDLOG_MAGIC, 4) != 0) {
+ g_warning("Could not parse cloudlog entry!");
+ break;
+ }
+ int size = sizeof(struct cloudlog_header);
+ size += GUINT32_FROM_LE(header->size1);
+ size += GUINT32_FROM_LE(header->size2);
+ size += GUINT32_FROM_LE(header->size3);
+ if (size > len) {
+ g_warning("Cloudlog entry is malformed (size too large)!");
+ break;
+ }
+ if (header->type - '0' == LOGTYPE_CHECKPOINT) {
+ checkpoint = buf;
+ checkpoint_size = size;
+ }
+ buf += size;
+ len -= size;
+ }
+
+ g_print("Found checkpoint record at %zd (size %zd)\n",
+ checkpoint - last->data, checkpoint_size);
+
+ /* Bootstrap the loading process by manually setting the location of this
+ * log item. */
+ BlueSkyCloudLog *commit;
+ commit = bluesky_cloudlog_get(fs,
+ ((struct cloudlog_header *)checkpoint)->id);
+ g_mutex_lock(commit->lock);
+ commit->location_flags |= CLOUDLOG_CLOUD;
+ commit->location.directory = 0;
+ commit->location.sequence = seq;
+ commit->location.offset = checkpoint - last->data;
+ commit->location.size = checkpoint_size;
+ g_mutex_unlock(commit->lock);
+
+ bluesky_inode_map_deserialize(fs, commit);
+ //bluesky_cloudlog_unref(commit);
+
+ return TRUE;
+}