In-progress work to implement inode map loading at server start.
[bluesky.git] / bluesky / imap.c
index dc24b58..0cdf9c5 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <stdint.h>
 #include <inttypes.h>
 #include <glib.h>
@@ -180,10 +181,106 @@ BlueSkyCloudLog *bluesky_inode_map_serialize(BlueSkyFS *fs)
 }
 
 /* Reconstruct the inode map from data stored in the cloud. */
-void bluesky_inode_map_deserialize(BlueSkyFS *fs, BlueSkyCloudLog *imap)
+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 = *inum;
+            entry->item = g_array_index(section->links,
+                                        BlueSkyCloudLog *, i);
+            bluesky_cloudlog_ref(entry->item);
+            entry->id = entry->item->id;
+            entry->location = entry->item->location;
+            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);
+
+    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;
+}