[ostree/wip/packfile-rebase2] lots more pull work



commit 898278caea783202456886c631fb9a39b871cd8e
Author: Colin Walters <walters verbum org>
Date:   Fri Mar 30 16:17:46 2012 -0400

    lots more pull work

 src/libostree/ostree-core.c      |  220 ++++++++++-
 src/libostree/ostree-core.h      |   22 +-
 src/libostree/ostree-repo.c      |  525 ++++++++++++++++++++------
 src/libostree/ostree-repo.h      |   44 ++-
 src/libotutil/ot-gio-utils.c     |   26 ++
 src/libotutil/ot-gio-utils.h     |    2 +
 src/libotutil/ot-variant-utils.c |   45 +++
 src/libotutil/ot-variant-utils.h |   19 +
 src/ostree/ostree-pull.c         |  776 +++++++++++++++++++++++++++-----------
 src/ostree/ot-builtin-fsck.c     |   18 +
 src/ostree/ot-builtin-init.c     |    5 +
 11 files changed, 1350 insertions(+), 352 deletions(-)
---
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index bdf4340..9ca8f83 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -652,6 +652,9 @@ ostree_checksum_to_bytes (const char *sha256)
       big = g_ascii_xdigit_value (sha256[j]);
       little = g_ascii_xdigit_value (sha256[j+1]);
 
+      g_assert (big != -1);
+      g_assert (little != -1);
+
       result[i] = (big << 4) | little;
     }
   
@@ -1311,7 +1314,6 @@ ostree_read_pack_entry_as_stream (GVariant *pack_entry)
   return ret_input;
 }
 
-
 gboolean
 ostree_read_pack_entry_variant (GVariant            *pack_entry,
                                 OstreeObjectType     expected_objtype,
@@ -1322,26 +1324,17 @@ ostree_read_pack_entry_variant (GVariant            *pack_entry,
 {
   gboolean ret = FALSE;
   GInputStream *stream = NULL;
-  GMemoryOutputStream *data_stream = NULL;
   GVariant *container_variant = NULL;
   GVariant *ret_variant = NULL;
   guint32 actual_type;
 
   stream = ostree_read_pack_entry_as_stream (pack_entry);
   
-  data_stream = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
-
-  if (!g_output_stream_splice ((GOutputStream*)data_stream, stream,
-                               G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
-                               cancellable, error))
+  if (!ot_util_variant_from_stream (stream, OSTREE_SERIALIZED_VARIANT_FORMAT,
+                                    trusted, &container_variant, cancellable, error))
     goto out;
 
-  container_variant = g_variant_new_from_data (OSTREE_SERIALIZED_VARIANT_FORMAT,
-                                               g_memory_output_stream_get_data (data_stream),
-                                               g_memory_output_stream_get_data_size (data_stream),
-                                               trusted, (GDestroyNotify) g_object_unref, data_stream);
   g_variant_ref_sink (container_variant);
-  data_stream = NULL; /* Transfer ownership */
 
   g_variant_get (container_variant, "(uv)",
                  &actual_type, &ret_variant);
@@ -1359,10 +1352,211 @@ ostree_read_pack_entry_variant (GVariant            *pack_entry,
   ot_transfer_out_value (out_variant, &ret_variant);
  out:
   g_clear_object (&stream);
-  g_clear_object (&data_stream);
   ot_clear_gvariant (&ret_variant);
   ot_clear_gvariant (&container_variant);
   return ret;
 }
 
+gboolean
+ostree_pack_index_search (GVariant   *index,
+                          GVariant   *csum_bytes,
+                          OstreeObjectType objtype,
+                          guint64    *out_offset)
+{
+  gboolean ret = FALSE;
+  GVariant *index_contents;
+  gsize imax, imin;
+  gsize n;
+  guint32 target_objtype;
+
+  index_contents = g_variant_get_child_value (index, 2);
+
+  target_objtype = (guint32) objtype;
+
+  n = g_variant_n_children (index_contents);
+
+  if (n == 0)
+    goto out;
+
+  imax = n - 1;
+  imin = 0;
+  while (imax >= imin)
+    {
+      GVariant *cur_csum_bytes;
+      guint32 cur_objtype;
+      guint64 cur_offset;
+      gsize imid;
+      int c;
+
+      imid = (imin + imax) / 2;
+
+      g_variant_get_child (index_contents, imid, "(u ayt)", &cur_objtype,
+                           &cur_csum_bytes, &cur_offset);      
+      cur_objtype = GUINT32_FROM_BE (cur_objtype);
+      cur_offset = GUINT64_FROM_BE (cur_offset);
+
+      c = ostree_cmp_checksum_bytes (cur_csum_bytes, csum_bytes);
+      if (c == 0)
+        {
+          if (cur_objtype < target_objtype)
+            c = -1;
+          else if (cur_objtype > target_objtype)
+            c = 1;
+        }
+      g_variant_unref (cur_csum_bytes);
+
+      if (c < 0)
+        imin = imid + 1;
+      else if (c > 0)
+        {
+          if (imid == 0)
+            goto out;
+          imax = imid - 1;
+        }
+      else
+        {
+          *out_offset = cur_offset;
+          break;
+        } 
+    }
+
+  ret = TRUE;
+ out:
+  ot_clear_gvariant (&index_contents);
+  return ret;
+}
+
+gboolean
+ostree_validate_structureof_objtype (guint32    objtype,
+                                     GError   **error)
+{
+  objtype = GUINT32_FROM_BE (objtype);
+  if (objtype < OSTREE_OBJECT_TYPE_RAW_FILE 
+      || objtype > OSTREE_OBJECT_TYPE_COMMIT)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid object type '%u'", objtype);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+gboolean
+ostree_validate_structureof_checksum (GVariant  *checksum,
+                                      GError   **error)
+{
+  gsize n_children = g_variant_n_children (checksum);
+  if (n_children != 32)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid checksum of length %" G_GUINT64_FORMAT
+                   " expected 32", (guint64) n_children);
+      return FALSE;
+    }
+  return TRUE;
+}
 
+static gboolean
+validate_variant (GVariant           *variant,
+                  const GVariantType *variant_type,
+                  GError            **error)
+{
+  if (!g_variant_is_normal_form (variant))
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Not normal form");
+      return FALSE;
+    }
+  if (!g_variant_is_of_type (variant, variant_type))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Doesn't match variant type '%s'",
+                   (char*)variant_type);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+gboolean
+ostree_validate_structureof_pack_index (GVariant      *index,
+                                        GError       **error)
+{
+  gboolean ret = FALSE;
+  const char *header;
+  GVariantIter *content_iter = NULL;
+  guint32 objtype;
+  GVariant *csum_bytes = NULL;
+  guint64 offset;
+
+  if (!validate_variant (index, OSTREE_PACK_INDEX_VARIANT_FORMAT, error))
+    goto out;
+
+  g_variant_get_child (index, 0, "&s", &header);
+
+  if (strcmp (header, "OSTv0PACKINDEX") != 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Invalid pack index; doesn't match header");
+      goto out;
+    }
+
+  g_variant_get_child (index, 2, "a(uayt)", &content_iter);
+
+  while (g_variant_iter_loop (content_iter, "(u ayt)",
+                              &objtype, &csum_bytes, &offset))
+    {
+      if (!ostree_validate_structureof_objtype (objtype, error))
+        goto out;
+      if (!ostree_validate_structureof_checksum (csum_bytes, error))
+        goto out;
+    }
+  csum_bytes = NULL;
+
+  ret = TRUE;
+ out:
+  if (content_iter)
+    g_variant_iter_free (content_iter);
+  ot_clear_gvariant (&csum_bytes);
+  return ret;
+}
+
+gboolean
+ostree_validate_structureof_pack_superindex (GVariant      *superindex,
+                                             GError       **error)
+{
+  gboolean ret = FALSE;
+  const char *header;
+  GVariant *csum_bytes = NULL;
+  GVariant *bloom = NULL;
+  GVariantIter *content_iter = NULL;
+
+  if (!validate_variant (superindex, OSTREE_PACK_SUPER_INDEX_VARIANT_FORMAT, error))
+    goto out;
+
+  g_variant_get_child (superindex, 0, "&s", &header);
+
+  if (strcmp (header, "OSTv0PACKSUPERINDEX") != 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Invalid pack superindex; doesn't match header");
+      goto out;
+    }
+
+  g_variant_get_child (superindex, 2, "a(ayay)", &content_iter);
+
+  while (g_variant_iter_loop (content_iter, "(@ay ay)",
+                              &csum_bytes, &bloom))
+    {
+      if (!ostree_validate_structureof_checksum (csum_bytes, error))
+        goto out;
+    }
+  csum_bytes = NULL;
+
+  ret = TRUE;
+ out:
+  if (content_iter)
+    g_variant_iter_free (content_iter);
+  ot_clear_gvariant (&csum_bytes);
+  ot_clear_gvariant (&bloom);
+  return ret;
+}
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 0e4d154..ad08c50 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -98,7 +98,7 @@ typedef enum {
 #define OSTREE_ARCHIVED_FILE_VARIANT_FORMAT G_VARIANT_TYPE ("(uuuuusa(ayay))")
 
 /* Pack super index
- * s - OSTSUPERPACKINDEX
+ * s - OSTv0SUPERPACKINDEX
  * a{sv} - Metadata
  * a(say) - (pack file checksum, bloom filter)
  */
@@ -160,6 +160,7 @@ void ostree_object_name_deserialize_v2_hex (GVariant         *variant,
                                             char            **out_checksum,
                                             OstreeObjectType *out_objtype);
 
+
 void ostree_object_name_deserialize_v2_bytes (GVariant         *variant,
                                               const guchar    **out_checksum,
                                               OstreeObjectType *out_objtype);
@@ -281,4 +282,23 @@ gboolean ostree_read_pack_entry_variant (GVariant         *pack_entry,
                                          GCancellable     *cancellable,
                                          GError          **error);
 
+gboolean ostree_pack_index_search (GVariant            *index,
+                                   GVariant           *csum_bytes,
+                                   OstreeObjectType    objtype,
+                                   guint64            *out_offset);
+
+/** VALIDATION **/
+
+gboolean ostree_validate_structureof_objtype (guint32    objtype,
+                                              GError   **error);
+
+gboolean ostree_validate_structureof_checksum (GVariant  *checksum,
+                                               GError   **error);
+
+gboolean ostree_validate_structureof_pack_index (GVariant      *index,
+                                                 GError       **error);
+
+gboolean ostree_validate_structureof_pack_superindex (GVariant      *superindex,
+                                                      GError       **error);
+
 #endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index b7fe0d6..b48bd34 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -61,6 +61,7 @@ struct _OstreeRepoPrivate {
   GFile *remote_heads_dir;
   GFile *objects_dir;
   GFile *pack_dir;
+  GFile *remote_cache_dir;
   GFile *config_file;
 
   gboolean inited;
@@ -88,6 +89,7 @@ ostree_repo_finalize (GObject *object)
   g_clear_object (&priv->remote_heads_dir);
   g_clear_object (&priv->objects_dir);
   g_clear_object (&priv->pack_dir);
+  g_clear_object (&priv->remote_cache_dir);
   g_clear_object (&priv->config_file);
   g_hash_table_destroy (priv->pack_index_mappings);
   g_hash_table_destroy (priv->pack_data_mappings);
@@ -162,6 +164,7 @@ ostree_repo_constructor (GType                  gtype,
   
   priv->objects_dir = g_file_get_child (priv->repodir, "objects");
   priv->pack_dir = g_file_get_child (priv->objects_dir, "pack");
+  priv->remote_cache_dir = g_file_get_child (priv->objects_dir, "remote-cache");
   priv->config_file = g_file_get_child (priv->repodir, "config");
 
   return object;
@@ -1602,6 +1605,40 @@ list_files_in_dir_matching (GFile                  *dir,
   return ret;
 }
 
+static gboolean
+map_variant_file_check_header_string (GFile         *path,
+                                      const GVariantType  *variant_type,
+                                      const char    *expected_header,
+                                      GVariant     **out_variant,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  gboolean ret = FALSE;
+  GVariant *ret_variant = NULL;
+  const char *header;
+
+  if (!ot_util_variant_map (path, variant_type, &ret_variant, error))
+    goto out;
+
+  g_variant_get_child (ret_variant, 0, "&s", &header);
+
+  if (strcmp (header, expected_header) != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid variant file '%s', expected header '%s'",
+                   ot_gfile_get_path_cached (path),
+                   expected_header);
+      goto out;
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_variant, &ret_variant);
+ out:
+  ot_clear_gvariant (&ret_variant);
+  return ret;
+}
+
+
 static char *
 get_checksum_from_pack_name (const char *name)
 {
@@ -1654,6 +1691,51 @@ list_pack_indexes_from_dir (OstreeRepo              *self,
   return ret;
 }
 
+static gboolean
+list_pack_checksums_from_superindex_file (GFile         *superindex_path,
+                                          GPtrArray    **out_indexes,
+                                          GCancellable  *cancellable,
+                                          GError       **error)
+{
+  gboolean ret = FALSE;
+  GPtrArray *ret_indexes = NULL;
+  GVariant *superindex_variant = NULL;
+  GVariantIter *variant_iter = NULL;
+  const char *magic;
+  GVariant *checksum;
+  GVariant *bloom;
+
+  if (!ot_util_variant_map (superindex_path, OSTREE_PACK_SUPER_INDEX_VARIANT_FORMAT,
+                            &superindex_variant, error))
+    goto out;
+  
+  g_variant_get (superindex_variant, "(&s a{sv}a(ayay))",
+                 &magic, NULL, &variant_iter);
+  
+  if (strcmp (magic, "OSTv0SUPERPACKINDEX") != 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Invalid header in super pack index");
+      goto out;
+    }
+
+  ret_indexes = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free); 
+  
+  while (g_variant_iter_loop (variant_iter, "(@ay ay)",
+                              &checksum, &bloom))
+    g_ptr_array_add (ret_indexes, ostree_checksum_from_bytes (checksum));
+
+  ret = TRUE;
+  ot_transfer_out_value (out_indexes, &ret_indexes);
+ out:
+  ot_clear_gvariant (&superindex_variant);
+  if (variant_iter)
+    g_variant_iter_free (variant_iter);
+  if (ret_indexes)
+    g_ptr_array_unref (ret_indexes);
+  return ret;
+}
+
 gboolean
 ostree_repo_list_pack_indexes (OstreeRepo              *self,
                                GPtrArray              **out_indexes,
@@ -1664,44 +1746,23 @@ ostree_repo_list_pack_indexes (OstreeRepo              *self,
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
   GFile *superindex_path = NULL;
   GPtrArray *ret_indexes = NULL;
-  GVariant *superindex_variant = NULL;
-  GVariantIter *variant_iter = NULL;
-  const char *magic;
-  GVariant *checksum;
-  GVariant *bloom;
 
   superindex_path = g_file_get_child (priv->pack_dir, "index");
 
-  ret_indexes = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free);
-
   if (g_file_query_exists (superindex_path, cancellable))
     {
-      if (!ot_util_variant_map (superindex_path, OSTREE_PACK_SUPER_INDEX_VARIANT_FORMAT,
-                                &superindex_variant, error))
+      if (!list_pack_checksums_from_superindex_file (superindex_path, &ret_indexes, cancellable, error))
         goto out;
-
-      g_variant_get (superindex_variant, "(&s a{sv}a(ayay))",
-                     &magic, NULL, &variant_iter);
-
-      if (strcmp (magic, "OSTv0SUPERPACKINDEX") != 0)
-        {
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                               "Invalid header in super pack index");
-          goto out;
-        }
-
-      while (g_variant_iter_loop (variant_iter, "(@ay ay)",
-                                  &checksum, &bloom))
-        g_ptr_array_add (ret_indexes, ostree_checksum_from_bytes (checksum));
+    }
+  else
+    {
+      ret_indexes = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free); 
     }
 
   ret = TRUE;
   ot_transfer_out_value (out_indexes, &ret_indexes);
  out:
-  ot_clear_gvariant (&superindex_variant);
   g_clear_object (&superindex_path);
-  if (variant_iter)
-    g_variant_iter_free (variant_iter);
   if (ret_indexes)
     g_ptr_array_unref (ret_indexes);
   return ret;
@@ -1717,7 +1778,7 @@ create_index_bloom (OstreeRepo          *self,
   gboolean ret = FALSE;
   GVariant *ret_bloom;
 
-  /* TODO - compute bloom filter */
+  /* TODO - define and compute bloom filter */
 
   ret_bloom = g_variant_new_fixed_array (G_VARIANT_TYPE ("y"), NULL, 0, 1);
   g_variant_ref_sink (ret_bloom);
@@ -1729,6 +1790,10 @@ create_index_bloom (OstreeRepo          *self,
   return ret;
 }
 
+/**
+ * Regenerate the pack superindex file based on the set of pack
+ * indexes currently in the filesystem.
+ */
 gboolean
 ostree_repo_regenerate_pack_index (OstreeRepo       *self,
                                    GCancellable     *cancellable,
@@ -1786,6 +1851,19 @@ ostree_repo_regenerate_pack_index (OstreeRepo       *self,
   return ret;
 }
 
+
+static GFile *
+get_pack_index_name_from_checksum (GFile *parent, const char *pack_checksum)
+{
+  return ot_gfile_get_child_strconcat (parent, "ostpack-", pack_checksum, ".index", NULL);
+}
+
+static GFile *
+get_pack_data_name_from_checksum (GFile *parent, const char *pack_checksum)
+{
+  return ot_gfile_get_child_strconcat (parent, "ostpack-", pack_checksum, ".data", NULL);
+}
+
 gboolean
 ostree_repo_add_pack_file (OstreeRepo       *self,
                            const char       *pack_checksum,
@@ -1796,24 +1874,17 @@ ostree_repo_add_pack_file (OstreeRepo       *self,
 {
   gboolean ret = FALSE;
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  char *dest_name = NULL;
   GFile *pack_index_path = NULL;
   GFile *pack_data_path = NULL;
 
   if (!ot_gfile_ensure_directory (priv->pack_dir, FALSE, error))
     goto out;
 
-  g_free (dest_name);
-  dest_name = g_strconcat ("ostpack-", pack_checksum, ".data", NULL);
-  pack_data_path = g_file_get_child (priv->pack_dir, dest_name);
-
+  pack_data_path = get_pack_data_name_from_checksum (priv->pack_dir, pack_checksum);
   if (!ot_gfile_rename (data_path, pack_data_path, cancellable, error))
     goto out;
 
-  g_free (dest_name);
-  dest_name = g_strconcat ("ostpack-", pack_checksum, ".index", NULL);
-  pack_index_path = g_file_get_child (priv->pack_dir, dest_name);
-
+  pack_index_path = get_pack_index_name_from_checksum (priv->pack_dir, pack_checksum);
   if (!ot_gfile_rename (index_path, pack_index_path, cancellable, error))
     goto out;
 
@@ -1821,7 +1892,303 @@ ostree_repo_add_pack_file (OstreeRepo       *self,
  out:
   g_clear_object (&pack_index_path);
   g_clear_object (&pack_data_path);
-  g_free (dest_name);
+  return ret;
+}
+
+static gboolean
+ensure_remote_cache_dir (OstreeRepo       *self,
+                         const char       *remote_name,
+                         GFile           **out_cache_dir,
+                         GCancellable     *cancellable,
+                         GError          **error)
+{
+  gboolean ret = FALSE;
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GFile *path = NULL;
+
+  path = g_file_get_child (priv->remote_cache_dir, remote_name);
+  
+  if (!ot_gfile_ensure_directory (path, FALSE, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&path);
+  return ret;
+}
+
+/**
+ * Take a pack superindex file @superindex_path, and clean up any
+ * no-longer-referenced pack files in the lookaside cache for
+ * @remote_name.  The updated index file will also be saved into the
+ * cache.
+ *
+ * Upon successful return, @out_cached_indexes will hold checksum
+ * strings for indexes which are already in the cache, and
+ * @out_uncached_indexes will hold strings for those which are not.
+ */
+gboolean
+ostree_repo_resync_cached_remote_pack_indexes (OstreeRepo       *self,
+                                               const char       *remote_name,
+                                               GFile            *superindex_path,
+                                               GPtrArray       **out_cached_indexes,
+                                               GPtrArray       **out_uncached_indexes,
+                                               GCancellable     *cancellable,
+                                               GError          **error)
+{
+  gboolean ret = FALSE;
+  GVariant *superindex_variant = NULL;
+  GVariantIter *superindex_contents_iter = NULL;
+  GFile *cache_path = NULL;
+  GFile *superindex_cache_path = NULL;
+  GPtrArray *index_files = NULL;
+  GHashTable *new_pack_indexes = NULL;
+  GHashTableIter hash_iter;
+  gpointer key, value;
+  GPtrArray *ret_cached_indexes = NULL;
+  GPtrArray *ret_uncached_indexes = NULL;
+  guint i;
+  GVariant *csum_bytes = NULL;
+  GVariant *bloom = NULL;
+  char *pack_checksum = NULL;
+
+  if (!ensure_remote_cache_dir (self, remote_name, &cache_path, cancellable, error))
+    goto out;
+
+  ret_cached_indexes = g_ptr_array_new_with_free_func (g_free);
+  ret_uncached_indexes = g_ptr_array_new_with_free_func (g_free);
+
+  if (!ot_util_variant_map (superindex_path, OSTREE_PACK_SUPER_INDEX_VARIANT_FORMAT,
+                            &superindex_variant, error))
+    goto out;
+
+  if (!ostree_validate_structureof_pack_superindex (superindex_variant, error))
+    goto out;
+
+  new_pack_indexes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  g_variant_get_child (superindex_variant, 2, "a(ayay)",
+                       &superindex_contents_iter);
+
+  while (g_variant_iter_loop (superindex_contents_iter,
+                              "(@ay ay)", &csum_bytes, &bloom))
+    {
+      pack_checksum = ostree_checksum_from_bytes (csum_bytes);
+      g_hash_table_insert (new_pack_indexes, pack_checksum, pack_checksum);
+      pack_checksum = NULL; /* transfer ownership */
+    }
+      
+  if (!list_files_in_dir_matching (cache_path,
+                                   "ostpack-", ".index",
+                                   &index_files, 
+                                   cancellable, error))
+    goto out;
+  
+  for (i = 0; i < index_files->len; i++)
+    {
+      GFile *index_file = index_files->pdata[i];
+      
+      g_free (pack_checksum);
+      pack_checksum = get_checksum_from_pack_name (ot_gfile_get_basename_cached (index_file));
+      
+      if (!g_hash_table_lookup (new_pack_indexes, pack_checksum))
+        {
+          if (!ot_gfile_unlink (index_file, cancellable, error))
+            goto out;
+        }
+      
+      g_ptr_array_add (ret_cached_indexes, pack_checksum);
+      pack_checksum = NULL; /* transfer ownership */
+    }
+
+  g_hash_table_iter_init (&hash_iter, new_pack_indexes);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *cur_pack_checksum = key;
+      gboolean found = FALSE;
+
+      for (i = 0; i < ret_cached_indexes->len; i++)
+        {
+          if (strcmp (cur_pack_checksum, ret_cached_indexes->pdata[i]) == 0)
+            {
+              found = TRUE;
+              break;
+            }
+        }
+      
+      if (!found)
+        g_ptr_array_add (ret_uncached_indexes, g_strdup (cur_pack_checksum));
+    }
+  
+  superindex_cache_path = g_file_get_child (cache_path, "index");
+  if (!ot_util_variant_save (superindex_cache_path, superindex_variant, cancellable, error))
+    goto out;
+      
+  ret = TRUE;
+  ot_transfer_out_value (out_cached_indexes, &ret_cached_indexes);
+  ot_transfer_out_value (out_uncached_indexes, &ret_uncached_indexes);
+ out:
+  g_free (pack_checksum);
+  g_clear_object (&cache_path);
+  g_clear_object (&superindex_cache_path);
+  if (superindex_contents_iter)
+    g_variant_iter_free (superindex_contents_iter);
+  ot_clear_ptrarray (&ret_cached_indexes);
+  ot_clear_ptrarray (&ret_uncached_indexes);
+  ot_clear_hashtable (&new_pack_indexes);
+  ot_clear_ptrarray (&index_files);
+  return ret;
+}
+
+/**
+ * Load the index for pack @pack_checksum from cache directory for
+ * @remote_name.
+ */
+gboolean
+ostree_repo_map_cached_remote_pack_index (OstreeRepo       *self,
+                                          const char       *remote_name,
+                                          const char       *pack_checksum,
+                                          GVariant        **out_variant,
+                                          GCancellable     *cancellable,
+                                          GError          **error)
+{
+  gboolean ret = FALSE;
+  GVariant *ret_variant = NULL;
+  GFile *cache_dir = NULL;
+  GFile *cached_pack_path = NULL;
+
+  if (!ensure_remote_cache_dir (self, remote_name, &cache_dir,
+                                cancellable, error))
+    goto out;
+
+  cached_pack_path = get_pack_index_name_from_checksum (cache_dir, pack_checksum);
+  if (!ot_util_variant_map (cached_pack_path, OSTREE_PACK_INDEX_VARIANT_FORMAT,
+                            &ret_variant, error))
+    goto out;
+
+  ret = TRUE;
+  ot_transfer_out_value (out_variant, &ret_variant);
+ out:
+  g_clear_object (&cache_dir);
+  g_clear_object (&cached_pack_path);
+  ot_clear_gvariant (&ret_variant);
+  return ret;
+}
+
+/**
+ * The variable @cached_path should refer to a file containing a pack
+ * index.  It will be validated and added to the cache directory for
+ * @remote_name.
+ */
+gboolean
+ostree_repo_add_cached_remote_pack_index (OstreeRepo       *self,
+                                          const char       *remote_name,
+                                          const char       *pack_checksum,
+                                          GFile            *cached_path,
+                                          GCancellable     *cancellable,
+                                          GError          **error)
+{
+  gboolean ret = FALSE;
+  GFile *cachedir = NULL;
+  GFile *target_path = NULL;
+  GVariant *input_index_variant = NULL;
+  GVariant *output_index_variant = NULL;
+
+  if (!map_variant_file_check_header_string (cached_path,
+                                             OSTREE_PACK_INDEX_VARIANT_FORMAT,
+                                             "OSTv0PACKINDEX",
+                                             &input_index_variant,
+                                             cancellable, error))
+    goto out;
+
+  if (!ostree_validate_structureof_pack_index (input_index_variant, error))
+    goto out;
+
+  output_index_variant = g_variant_get_normal_form (input_index_variant);
+  
+  if (!ensure_remote_cache_dir (self, remote_name, &cachedir, cancellable, error))
+    goto out;
+  
+  cached_path = get_pack_index_name_from_checksum (cachedir, pack_checksum);
+  if (!ot_util_variant_save (target_path, output_index_variant, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&cachedir);
+  g_clear_object (&target_path);
+  ot_clear_gvariant (&input_index_variant);
+  ot_clear_gvariant (&output_index_variant);
+  return ret;
+}
+
+/**
+ * Check for availability of the pack index pointing to @pack_checksum
+ * in the lookaside cache for @remote_name.  If not found, then the
+ * output parameter @out_cached_path will be %NULL.
+ */
+gboolean
+ostree_repo_get_cached_remote_pack_data (OstreeRepo       *self,
+                                         const char       *remote_name,
+                                         const char       *pack_checksum,
+                                         GFile           **out_cached_path,
+                                         GCancellable     *cancellable,
+                                         GError          **error)
+{
+  gboolean ret = FALSE;
+  GFile *cache_dir = NULL;
+  GFile *cached_pack_path = NULL;
+  GFile *ret_cached_path = NULL;
+
+  if (!ensure_remote_cache_dir (self, remote_name, &cache_dir,
+                                cancellable, error))
+    goto out;
+
+  cached_pack_path = get_pack_data_name_from_checksum (cache_dir, pack_checksum);
+  if (g_file_query_exists (cached_pack_path, cancellable))
+    {
+      ret_cached_path = cached_pack_path;
+      cached_pack_path = NULL;
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_cached_path, &ret_cached_path);
+ out:
+  g_clear_object (&cache_dir);
+  g_clear_object (&cached_pack_path);
+  return ret;
+}
+
+/**
+ * Add file @cached_path into the cache for given @remote_name.
+ *
+ * <note>
+ *   This unlinks @cached_path.
+ * </note>
+ */
+gboolean
+ostree_repo_take_cached_remote_pack_data (OstreeRepo       *self,
+                                          const char       *remote_name,
+                                          const char       *pack_checksum,
+                                          GFile            *cached_path,
+                                          GCancellable     *cancellable,
+                                          GError          **error)
+{
+  gboolean ret = FALSE;
+  GFile *cachedir = NULL;
+  GFile *target_path = NULL;
+
+  if (!ensure_remote_cache_dir (self, remote_name, &cachedir, cancellable, error))
+    goto out;
+
+  target_path = get_pack_data_name_from_checksum (cachedir, pack_checksum);
+  
+  if (!ot_gfile_rename (cached_path, target_path, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&cachedir);
+  g_clear_object (&target_path);
   return ret;
 }
 
@@ -2735,7 +3102,6 @@ ostree_repo_load_pack_index (OstreeRepo    *self,
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
   GVariant *ret_variant = NULL;
   GFile *path = NULL;
-  const char *header;
   
   ret_variant = g_hash_table_lookup (priv->pack_index_mappings, sha256);
   if (ret_variant)
@@ -2745,23 +3111,16 @@ ostree_repo_load_pack_index (OstreeRepo    *self,
   else
     {
       path = ostree_repo_get_pack_index_path (self, sha256);
-      if (!ot_util_variant_map (path,
-                                OSTREE_PACK_INDEX_VARIANT_FORMAT,
-                                &ret_variant, error))
+      if (!map_variant_file_check_header_string (path,
+                                                 OSTREE_PACK_INDEX_VARIANT_FORMAT,
+                                                 "OSTv0PACKINDEX",
+                                                 &ret_variant,
+                                                 cancellable, error))
         goto out;
       g_hash_table_insert (priv->pack_index_mappings, g_strdup (sha256),
                            g_variant_ref (ret_variant));
     }
 
-  g_variant_get_child (ret_variant, 0, "&s", &header);
-
-  if (strcmp (header, "OSTv0PACKINDEX") != 0)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Invalid pack version, expected 'OSTv0PACKINDEX'");
-      goto out;
-    }
-
   ret = TRUE;
   ot_transfer_out_value (out_variant, &ret_variant);
  out:
@@ -2822,65 +3181,6 @@ ostree_repo_map_pack_file (OstreeRepo    *self,
   return ret;
 }
 
-static gboolean
-bsearch_in_pack_index (GVariant   *index_contents,
-                       GVariant   *csum_bytes,
-                       OstreeObjectType objtype,
-                       guint64    *out_offset)
-{
-  gsize imax, imin;
-  gsize n;
-  guint32 target_objtype = (guint32) objtype;
-
-  n = g_variant_n_children (index_contents);
-
-  if (n == 0)
-    return FALSE;
-
-  imax = n - 1;
-  imin = 0;
-  while (imax >= imin)
-    {
-      GVariant *cur_csum_bytes;
-      guint32 cur_objtype;
-      guint64 cur_offset;
-      gsize imid;
-      int c;
-
-      imid = (imin + imax) / 2;
-
-      g_variant_get_child (index_contents, imid, "(u ayt)", &cur_objtype,
-                           &cur_csum_bytes, &cur_offset);      
-      cur_objtype = GUINT32_FROM_BE (cur_objtype);
-      cur_offset = GUINT64_FROM_BE (cur_offset);
-
-      c = ostree_cmp_checksum_bytes (cur_csum_bytes, csum_bytes);
-      if (c == 0)
-        {
-          if (cur_objtype < target_objtype)
-            c = -1;
-          else if (cur_objtype > target_objtype)
-            c = 1;
-        }
-      g_variant_unref (cur_csum_bytes);
-
-      if (c < 0)
-        imin = imid + 1;
-      else if (c > 0)
-        {
-          if (imid == 0)
-            break;
-          imax = imid - 1;
-        }
-      else
-        {
-          *out_offset = cur_offset;
-          return TRUE;
-        } 
-    }
-
-  return FALSE;
-}
 
 gboolean
 ostree_repo_load_file (OstreeRepo         *self,
@@ -3119,7 +3419,6 @@ find_object_in_packs (OstreeRepo        *self,
   GFile *index_path = NULL;
   GVariant *csum_bytes = NULL;
   GVariant *index_variant = NULL;
-  GVariant *index_contents = NULL;
   guint i;
 
   csum_bytes = ostree_checksum_to_bytes (checksum);
@@ -3139,10 +3438,7 @@ find_object_in_packs (OstreeRepo        *self,
       if (!ostree_repo_load_pack_index (self, pack_checksum, &index_variant, cancellable, error))
         goto out;
 
-      ot_clear_gvariant (&index_contents);
-      index_contents = g_variant_get_child_value (index_variant, 2);
-      
-      if (!bsearch_in_pack_index (index_contents, csum_bytes, objtype, &offset))
+      if (!ostree_pack_index_search (index_variant, csum_bytes, objtype, &offset))
         continue;
 
       ret_pack_checksum = g_strdup (pack_checksum);
@@ -3161,7 +3457,6 @@ find_object_in_packs (OstreeRepo        *self,
   g_clear_object (&index_path);
   ot_clear_gvariant (&index_variant);
   ot_clear_gvariant (&csum_bytes);
-  ot_clear_gvariant (&index_contents);
   return ret;
 }
 
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index e293cfb..55c918e 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -230,11 +230,9 @@ gboolean      ostree_repo_stage_commit (OstreeRepo   *self,
                                         GCancellable *cancellable,
                                         GError      **error);
 
-gboolean
-ostree_repo_regenerate_pack_index (OstreeRepo       *self,
-                                   GCancellable     *cancellable,
-                                   GError          **error);
-
+gboolean ostree_repo_regenerate_pack_index (OstreeRepo       *self,
+                                            GCancellable     *cancellable,
+                                            GError          **error);
 
 gboolean     ostree_repo_add_pack_file (OstreeRepo       *self,
                                         const char       *checksum,
@@ -243,6 +241,42 @@ gboolean     ostree_repo_add_pack_file (OstreeRepo       *self,
                                         GCancellable     *cancellable,
                                         GError          **error);
 
+gboolean     ostree_repo_resync_cached_remote_pack_indexes (OstreeRepo       *self,
+                                                            const char       *remote_name,
+                                                            GFile            *superindex_path,
+                                                            GPtrArray       **out_cached_indexes,
+                                                            GPtrArray       **out_uncached_indexes,
+                                                            GCancellable     *cancellable,
+                                                            GError          **error);
+
+gboolean     ostree_repo_map_cached_remote_pack_index (OstreeRepo       *self,
+                                                       const char       *remote_name,
+                                                       const char       *pack_checksum,
+                                                       GVariant        **out_variant,
+                                                       GCancellable     *cancellable,
+                                                       GError          **error);
+
+gboolean     ostree_repo_add_cached_remote_pack_index (OstreeRepo       *self,
+                                                       const char       *remote_name,
+                                                       const char       *pack_checksum,
+                                                       GFile            *cached_path,
+                                                       GCancellable     *cancellable,
+                                                       GError          **error);
+
+gboolean     ostree_repo_get_cached_remote_pack_data (OstreeRepo       *self,
+                                                      const char       *remote_name,
+                                                      const char       *pack_checksum,
+                                                      GFile           **out_cached_path,
+                                                      GCancellable     *cancellable,
+                                                      GError          **error);
+
+gboolean     ostree_repo_take_cached_remote_pack_data (OstreeRepo       *self,
+                                                       const char       *remote_name,
+                                                       const char       *pack_checksum,
+                                                       GFile            *cached_path,
+                                                       GCancellable     *cancellable,
+                                                       GError          **error);
+
 typedef enum {
   OSTREE_REPO_CHECKOUT_MODE_NONE = 0,
   OSTREE_REPO_CHECKOUT_MODE_USER = 1
diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c
index 6d2ca73..15dddc7 100644
--- a/src/libotutil/ot-gio-utils.c
+++ b/src/libotutil/ot-gio-utils.c
@@ -77,6 +77,32 @@ ot_gfile_ensure_directory (GFile     *dir,
   return ret;
 }
 
+GFile *
+ot_gfile_get_child_strconcat (GFile *parent,
+                              const char *first,
+                              ...) 
+{
+  va_list args;
+  GFile *ret;
+  GString *buf;
+  const char *arg;
+
+  g_return_val_if_fail (first != NULL, NULL);
+
+  va_start (args, first);
+  
+  buf = g_string_new (first);
+  
+  while ((arg = va_arg (args, const char *)) != NULL)
+    g_string_append (buf, arg);
+
+  ret = g_file_get_child (parent, buf->str);
+  
+  g_string_free (buf, TRUE);
+
+  return ret;
+}
+
 /**
  * ot_gfile_unlink:
  * @path: Path to file
diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h
index c5831d3..5fda5da 100644
--- a/src/libotutil/ot-gio-utils.h
+++ b/src/libotutil/ot-gio-utils.h
@@ -36,6 +36,8 @@ G_BEGIN_DECLS
 
 GFileType ot_gfile_type_for_mode (guint32 mode);
 
+GFile *ot_gfile_get_child_strconcat (GFile *parent, const char *first, ...) G_GNUC_NULL_TERMINATED;
+
 GFile *ot_gfile_new_for_path (const char *path);
 
 const char *ot_gfile_get_path_cached (GFile *file);
diff --git a/src/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c
index cb616f3..73cadca 100644
--- a/src/libotutil/ot-variant-utils.c
+++ b/src/libotutil/ot-variant-utils.c
@@ -88,6 +88,12 @@ ot_util_variant_take_ref (GVariant *variant)
 #endif
 }
 
+/**
+ * Return in @out_variant the result of memory-mapping the entire
+ * contents of file @src.
+ *
+ * Note the returned @out_variant is not floating.
+ */
 gboolean
 ot_util_variant_map (GFile *src,
                      const GVariantType *type,
@@ -121,3 +127,42 @@ ot_util_variant_map (GFile *src,
     g_mapped_file_unref (mfile);
   return ret;
 }
+
+/**
+ * Read all input from @src, allocating a new #GVariant from it into
+ * output variable @out_variant.  @src will be closed as a result.
+ *
+ * Note the returned @out_variant is not floating.
+ */
+gboolean
+ot_util_variant_from_stream (GInputStream         *src,
+                             const GVariantType   *type,
+                             gboolean              trusted,
+                             GVariant            **out_variant,
+                             GCancellable         *cancellable,
+                             GError              **error)
+{
+  gboolean ret = FALSE;
+  GMemoryOutputStream *data_stream = NULL;
+  GVariant *ret_variant = NULL;
+
+  data_stream = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+  if (!g_output_stream_splice ((GOutputStream*)data_stream, src,
+                               G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                               cancellable, error))
+    goto out;
+
+  ret_variant = g_variant_new_from_data (type, g_memory_output_stream_get_data (data_stream),
+                                         g_memory_output_stream_get_data_size (data_stream),
+                                         trusted, (GDestroyNotify) g_object_unref, data_stream);
+  data_stream = NULL; /* Transfer ownership */
+  g_variant_ref_sink (ret_variant);
+
+  ret = TRUE;
+  ot_transfer_out_value (out_variant, &ret_variant);
+ out:
+  g_clear_object (&data_stream);
+  ot_clear_gvariant (&ret_variant);
+  return ret;
+}
diff --git a/src/libotutil/ot-variant-utils.h b/src/libotutil/ot-variant-utils.h
index 69f3111..e443a74 100644
--- a/src/libotutil/ot-variant-utils.h
+++ b/src/libotutil/ot-variant-utils.h
@@ -33,6 +33,18 @@ G_BEGIN_DECLS
   *a_v = NULL;                      \
   } while (0);
 
+#define ot_clear_ptrarray(a_v) do { \
+  if (*a_v)                         \
+    g_ptr_array_unref (*a_v);         \
+  *a_v = NULL;                      \
+  } while (0);
+
+#define ot_clear_hashtable(a_v) do { \
+  if (*a_v)                         \
+    g_hash_table_unref (*a_v);         \
+  *a_v = NULL;                      \
+  } while (0);
+
 GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
 
 GVariant * ot_util_variant_take_ref (GVariant *variant);
@@ -47,6 +59,13 @@ gboolean ot_util_variant_map (GFile *src,
                               GVariant **out_variant,
                               GError  **error);
 
+gboolean ot_util_variant_from_stream (GInputStream         *src,
+                                      const GVariantType   *type,
+                                      gboolean              trusted,
+                                      GVariant            **out_variant,
+                                      GCancellable         *cancellable,
+                                      GError              **error);
+
 G_END_DECLS
 
 #endif
diff --git a/src/ostree/ostree-pull.c b/src/ostree/ostree-pull.c
index b9a76a5..7c32737 100644
--- a/src/ostree/ostree-pull.c
+++ b/src/ostree/ostree-pull.c
@@ -55,6 +55,50 @@ log_verbose (const char  *fmt,
 }
 
 typedef struct {
+  OstreeRepo   *repo;
+  char         *remote_name;
+  SoupSession  *session;
+  SoupURI      *base_uri;
+
+  gboolean      fetched_packs;
+  GPtrArray    *cached_pack_indexes;
+} OtPullData;
+
+static SoupURI *
+suburi_new (SoupURI   *base,
+            const char *first,
+            ...) G_GNUC_NULL_TERMINATED;
+
+static SoupURI *
+suburi_new (SoupURI   *base,
+            const char *first,
+            ...)
+{
+  va_list args;
+  GPtrArray *arg_array;
+  const char *arg;
+  char *subpath;
+  SoupURI *ret;
+
+  arg_array = g_ptr_array_new ();
+  g_ptr_array_add (arg_array, (char*)soup_uri_get_path (base));
+  g_ptr_array_add (arg_array, (char*)first);
+  
+  while ((arg = va_arg (args, const char *)) != NULL)
+    g_ptr_array_add (arg_array, (char*)arg);
+
+  subpath = g_build_filenamev ((char**)arg_array->pdata);
+  g_ptr_array_unref (arg_array);
+  
+  ret = soup_uri_copy (base);
+  soup_uri_set_path (ret, subpath);
+  g_free (subpath);
+  
+  return ret;
+}
+
+
+typedef struct {
   SoupSession    *session;
   GOutputStream  *stream;
   gboolean        had_error;
@@ -78,8 +122,7 @@ on_got_chunk (SoupMessage   *msg,
 }
 
 static gboolean
-fetch_uri (OstreeRepo  *repo,
-           SoupSession *soup,
+fetch_uri (OtPullData  *pull_data,
            SoupURI     *uri,
            const char  *tmp_prefix,
            GFile      **out_temp_filename,
@@ -94,14 +137,14 @@ fetch_uri (OstreeRepo  *repo,
   GOutputStream *output_stream = NULL;
   OstreeSoupChunkData chunkdata;
 
-  if (!ostree_create_temp_regular_file (ostree_repo_get_tmpdir (repo),
+  if (!ostree_create_temp_regular_file (ostree_repo_get_tmpdir (pull_data->repo),
                                         tmp_prefix, NULL,
                                         &ret_temp_filename,
                                         &output_stream,
                                         NULL, error))
     goto out;
 
-  chunkdata.session = soup;
+  chunkdata.session = pull_data->session;
   chunkdata.stream = output_stream;
   chunkdata.had_error = FALSE;
   chunkdata.error = error;
@@ -114,7 +157,7 @@ fetch_uri (OstreeRepo  *repo,
 
   g_signal_connect (msg, "got-chunk", G_CALLBACK (on_got_chunk), &chunkdata);
   
-  response = soup_session_send_message (soup, msg);
+  response = soup_session_send_message (pull_data->session, msg);
   if (response != 200)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -138,8 +181,7 @@ fetch_uri (OstreeRepo  *repo,
 }
 
 static gboolean
-fetch_uri_contents_utf8 (OstreeRepo  *repo,
-                         SoupSession *soup,
+fetch_uri_contents_utf8 (OtPullData  *pull_data,
                          SoupURI     *uri,
                          char       **out_contents,
                          GCancellable  *cancellable,
@@ -150,7 +192,7 @@ fetch_uri_contents_utf8 (OstreeRepo  *repo,
   char *ret_contents = NULL;
   gsize len;
 
-  if (!fetch_uri (repo, soup, uri, "tmp-", &tmpf, cancellable, error))
+  if (!fetch_uri (pull_data, uri, "tmp-", &tmpf, cancellable, error))
     goto out;
 
   if (!g_file_load_contents (tmpf, cancellable, &ret_contents, &len, NULL, error))
@@ -173,29 +215,209 @@ fetch_uri_contents_utf8 (OstreeRepo  *repo,
   return ret;
 }
 
+static gboolean
+fetch_one_pack_file (OtPullData            *pull_data,
+                     const char            *pack_checksum,
+                     GFile                **out_cached_path,
+                     GCancellable          *cancellable,
+                     GError               **error)
+{
+  gboolean ret = FALSE;
+  GFile *ret_cached_path = NULL;
+  GFile *tmp_path = NULL;
+  char *pack_name = NULL;
+  SoupURI *pack_uri = NULL;
+
+  if (!ostree_repo_get_cached_remote_pack_data (pull_data->repo, pull_data->remote_name,
+                                                pack_checksum, &ret_cached_path,
+                                                cancellable, error))
+    goto out;
+
+  if (ret_cached_path == NULL)
+    {
+      pack_name = g_strconcat ("ostpack-", pack_checksum, ".data", NULL);
+      pack_uri = suburi_new (pull_data->base_uri, "objects", "pack", pack_name, NULL);
+      
+      if (!fetch_uri (pull_data, pack_uri, "packdata-", &tmp_path, cancellable, error))
+        goto out;
+
+      if (!ostree_repo_take_cached_remote_pack_data (pull_data->repo, pull_data->remote_name,
+                                                     pack_checksum, tmp_path,
+                                                     cancellable, error))
+        goto out;
+    }
+
+  if (!ostree_repo_get_cached_remote_pack_data (pull_data->repo, pull_data->remote_name,
+                                                pack_checksum, &ret_cached_path,
+                                                cancellable, error))
+    goto out;
+
+  g_assert (ret_cached_path != NULL);
+
+  ret = TRUE;
+  ot_transfer_out_value (out_cached_path, &ret_cached_path);
+ out:
+  g_clear_object (&ret_cached_path);
+  g_clear_object (&tmp_path);
+  g_free (pack_name);
+  if (pack_uri)
+    soup_uri_free (pack_uri);
+  return ret;
+}
+
+static gboolean
+find_object_in_remote_packs (OtPullData       *pull_data,
+                             const char       *checksum,
+                             OstreeObjectType  objtype,
+                             char            **out_pack_checksum,
+                             guint64          *out_offset,
+                             GCancellable     *cancellable,
+                             GError          **error)
+{
+  gboolean ret = FALSE;
+  GVariant *mapped_pack = NULL;
+  GVariant *csum_bytes = NULL;
+  char *ret_pack_checksum = NULL;
+  guint64 offset;
+  guint i;
+
+  csum_bytes = ostree_checksum_to_bytes (checksum);
+
+  for (i = 0; i < pull_data->cached_pack_indexes->len; i++)
+    {
+      const char *pack_checksum = pull_data->cached_pack_indexes->pdata[i];
+
+      ot_clear_gvariant (&mapped_pack);
+      if (!ostree_repo_map_cached_remote_pack_index (pull_data->repo, pull_data->remote_name,
+                                                     pack_checksum, &mapped_pack,
+                                                     cancellable, error))
+        goto out;
+
+      if (ostree_pack_index_search (mapped_pack, csum_bytes, objtype, &offset))
+        {
+          ret_pack_checksum = g_strdup (pack_checksum);
+          break;
+        }
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_pack_checksum, &ret_pack_checksum);
+  if (out_offset)
+    *out_offset = offset;
+ out:
+  ot_clear_gvariant (&mapped_pack);
+  g_free (ret_pack_checksum);
+  ot_clear_gvariant (&csum_bytes);
+  return ret;
+}
+
+static gboolean
+fetch_one_cache_index (OtPullData          *pull_data,
+                      const char           *pack_checksum,
+                      GCancellable         *cancellable,
+                      GError              **error)
+{
+  gboolean ret = FALSE;
+  SoupURI *index_uri = NULL;
+  GFile *tmp_path = NULL;
+  char *pack_index_name = NULL;
+
+  pack_index_name = g_strconcat ("ostpack-", pack_checksum, ".index", NULL);
+  index_uri = suburi_new (pull_data->base_uri, "objects", "pack", pack_index_name, NULL);
+  
+  if (!fetch_uri (pull_data, index_uri, "packindex-", &tmp_path,
+                  cancellable, error))
+    goto out;
+  
+  if (!ostree_repo_add_cached_remote_pack_index (pull_data->repo, pull_data->remote_name,
+                                                 pack_checksum, tmp_path,
+                                                 cancellable, error))
+    goto out;
+  
+  if (!ot_gfile_unlink (tmp_path, cancellable, error))
+    goto out;
+      
+  g_clear_object (&tmp_path);
+
+  ret = TRUE;
+ out:
+  if (tmp_path != NULL)
+    (void) ot_gfile_unlink (tmp_path, NULL, NULL);
+  g_clear_object (&tmp_path);
+  g_free (pack_index_name);
+  if (index_uri)
+    soup_uri_free (index_uri);
+  return ret;
+}
 
 static gboolean
-fetch_object (OstreeRepo  *repo,
-              SoupSession *soup,
-              SoupURI     *baseuri,
-              const char  *checksum,
-              OstreeObjectType objtype,
-              GFile           **out_temp_path,
-              GCancellable *cancellable,
-              GError     **error)
+fetch_and_cache_pack_indexes (OtPullData        *pull_data,
+                              GCancellable      *cancellable,
+                              GError           **error)
+{
+  gboolean ret = FALSE;
+  SoupURI *superindex_uri = NULL;
+  GFile *superindex_tmppath = NULL;
+  GPtrArray *cached_indexes = NULL;
+  GPtrArray *uncached_indexes = NULL;
+  GVariant *superindex_variant = NULL;
+  GVariantIter *contents_iter = NULL;
+  guint i;
+
+  superindex_uri = suburi_new (pull_data->base_uri, "objects", "pack", "index", NULL);
+  
+  if (!fetch_uri (pull_data, superindex_uri, "index-",
+                  &superindex_tmppath, cancellable, error))
+    goto out;
+
+  if (!ostree_repo_resync_cached_remote_pack_indexes (pull_data->repo, pull_data->remote_name,
+                                                      superindex_tmppath,
+                                                      &cached_indexes, &uncached_indexes,
+                                                      cancellable, error))
+    goto out;
+
+  for (i = 0; i < cached_indexes->len; i++)
+    g_ptr_array_add (pull_data->cached_pack_indexes,
+                     g_strdup (cached_indexes->pdata[i]));
+
+  for (i = 0; i < uncached_indexes->len; i++)
+    {
+      const char *pack_checksum = uncached_indexes->pdata[i];
+
+      if (!fetch_one_cache_index (pull_data, pack_checksum, cancellable, error))
+        goto out;
+      
+      g_ptr_array_add (pull_data->cached_pack_indexes, g_strdup (pack_checksum));
+    }
+
+  ret = TRUE;
+ out:
+  if (superindex_uri)
+    soup_uri_free (superindex_uri);
+  g_clear_object (&superindex_tmppath);
+  ot_clear_gvariant (&superindex_variant);
+  if (contents_iter)
+    g_variant_iter_free (contents_iter);
+  return ret;
+}
+
+static gboolean
+fetch_loose_object (OtPullData  *pull_data,
+                    const char  *checksum,
+                    OstreeObjectType objtype,
+                    GFile           **out_temp_path,
+                    GCancellable *cancellable,
+                    GError     **error)
 {
   gboolean ret = FALSE;
   char *objpath = NULL;
-  char *relpath = NULL;
   SoupURI *obj_uri = NULL;
   GFile *ret_temp_path = NULL;
 
   objpath = ostree_get_relative_object_path (checksum, objtype);
-  obj_uri = soup_uri_copy (baseuri);
-  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
-  soup_uri_set_path (obj_uri, relpath);
+  obj_uri = suburi_new (pull_data->base_uri, objpath, NULL);
   
-  if (!fetch_uri (repo, soup, obj_uri, ostree_object_type_to_string (objtype), &ret_temp_path,
+  if (!fetch_uri (pull_data, obj_uri, ostree_object_type_to_string (objtype), &ret_temp_path,
                   cancellable, error))
     goto out;
 
@@ -206,97 +428,309 @@ fetch_object (OstreeRepo  *repo,
     soup_uri_free (obj_uri);
   g_clear_object (&ret_temp_path);
   g_free (objpath);
-  g_free (relpath);
   return ret;
 }
 
 static gboolean
-fetch_and_store_object (OstreeRepo  *repo,
-                        SoupSession *soup,
-                        SoupURI     *baseuri,
-                        const char  *checksum,
-                        OstreeObjectType objtype,
-                        gboolean         *out_is_pending,
-                        GVariant        **out_metadata,
-                        GCancellable *cancellable,
-                        GError     **error)
+find_object (OtPullData        *pull_data,
+             const char        *checksum,
+             OstreeObjectType   objtype,
+             gboolean          *out_is_stored,
+             gboolean          *out_is_pending,
+             char             **out_remote_pack_checksum,
+             guint64           *out_offset,
+             GCancellable      *cancellable,
+             GError           **error)
 {
   gboolean ret = FALSE;
-  GFileInfo *file_info = NULL;
-  GInputStream *input = NULL;
+  gboolean ret_is_stored;
+  gboolean ret_is_pending;
   GFile *stored_path = NULL;
   GFile *pending_path = NULL;
-  char *pack_checksum = NULL;
-  GFile *temp_path = NULL;
-  GVariant *ret_metadata = NULL;
-  gboolean ret_is_pending;
+  char *local_pack_checksum = NULL;
+  char *ret_remote_pack_checksum = NULL;
+  guint64 offset;
 
-  g_assert (objtype != OSTREE_OBJECT_TYPE_RAW_FILE);
-
-  if (!ostree_repo_find_object (repo, objtype, checksum,
+  if (!ostree_repo_find_object (pull_data->repo, objtype, checksum,
                                 &stored_path, &pending_path,
-                                &pack_checksum, NULL,
+                                &local_pack_checksum, NULL,
                                 cancellable, error))
     goto out;
-      
-  if (!(stored_path || pending_path || pack_checksum))
+
+  ret_is_stored = (stored_path != NULL || local_pack_checksum != NULL);
+  ret_is_pending = pending_path != NULL;
+
+  if (!(ret_is_stored || ret_is_pending))
     {
-      if (!fetch_object (repo, soup, baseuri, checksum, objtype, &temp_path, cancellable, error))
+      if (!find_object_in_remote_packs (pull_data, checksum, objtype, 
+                                        &ret_remote_pack_checksum, &offset,
+                                        cancellable, error))
         goto out;
     }
 
-  if (temp_path)
+  ret = TRUE;
+  if (out_is_stored)
+    *out_is_stored = ret_is_stored;
+  if (out_is_pending)
+    *out_is_pending = ret_is_pending;
+  ot_transfer_out_value (out_remote_pack_checksum, &ret_remote_pack_checksum);
+ out:
+  g_free (local_pack_checksum);
+  g_free (ret_remote_pack_checksum);
+  g_clear_object (&stored_path);
+  return ret;
+}
+
+static void
+unlink_file_on_unref (GFile *f)
+{
+  (void) ot_gfile_unlink (f, NULL, NULL);
+  g_object_unref (f);
+}
+
+static gboolean
+fetch_object_if_not_stored (OtPullData           *pull_data,
+                            const char           *checksum,
+                            OstreeObjectType      objtype,
+                            gboolean             *out_is_stored,
+                            gboolean             *out_is_pending,
+                            GInputStream        **out_input,
+                            GCancellable         *cancellable,
+                            GError              **error)
+{
+  gboolean ret = FALSE;
+  gboolean ret_is_stored = FALSE;
+  gboolean ret_is_pending = FALSE;
+  GInputStream *ret_input = NULL;
+  GFile *temp_path = NULL;
+  GFile *pack_path = NULL;
+  GMappedFile *pack_map = NULL;
+  char *remote_pack_checksum = NULL;
+  guint64 pack_offset;
+  GVariant *pack_entry = NULL;
+
+  if (!find_object (pull_data, checksum, objtype, &ret_is_stored,
+                    &ret_is_pending, &remote_pack_checksum,
+                    &pack_offset, cancellable, error))
+    goto out;
+      
+  if (remote_pack_checksum != NULL)
     {
-      file_info = g_file_query_info (temp_path, OSTREE_GIO_FAST_QUERYINFO,
-                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
-      if (!file_info)
+      g_assert (!(ret_is_stored || ret_is_pending));
+
+      if (!fetch_one_pack_file (pull_data, remote_pack_checksum, &pack_path,
+                                cancellable, error))
+        goto out;
+
+      pack_map = g_mapped_file_new (ot_gfile_get_path_cached (pack_path), FALSE, error);
+      if (!pack_map)
+        goto out;
+
+      if (!ostree_read_pack_entry_raw ((guchar*)g_mapped_file_get_contents (pack_map),
+                                       g_mapped_file_get_length (pack_map),
+                                       pack_offset, FALSE, &pack_entry,
+                                       cancellable, error))
+        goto out;
+
+      ret_input = ostree_read_pack_entry_as_stream (pack_entry);
+    }
+  else if (!(ret_is_stored || ret_is_pending))
+    {
+      if (!fetch_loose_object (pull_data, checksum, objtype, &temp_path, cancellable, error))
         goto out;
       
-      input = (GInputStream*)g_file_read (temp_path, cancellable, error);
-      if (!input)
+      ret_input = (GInputStream*)g_file_read (temp_path, cancellable, error);
+      if (!ret_input)
         goto out;
+      g_object_set_data_full ((GObject*)ret_input, "ostree-tmpfile-unlink",
+                              g_object_ref (temp_path),
+                              (GDestroyNotify)unlink_file_on_unref);
     }
-  
-  if (pending_path || temp_path)
+
+  ret = TRUE;
+  ot_transfer_out_value (out_input, &ret_input);
+  if (out_is_stored)
+    *out_is_stored = ret_is_stored;
+  if (out_is_pending)
+    *out_is_pending = ret_is_pending;
+ out:
+  g_clear_object (&temp_path);
+  g_clear_object (&pack_path);
+  if (pack_map)
+    g_mapped_file_unref (pack_map);
+  ot_clear_gvariant (&pack_entry);
+  g_clear_object (&pack_path);
+  g_clear_object (&ret_input);
+  return ret;
+}
+
+static gboolean
+fetch_and_store_object (OtPullData       *pull_data,
+                        const char       *checksum,
+                        OstreeObjectType objtype,
+                        gboolean         *out_is_pending,
+                        GCancellable     *cancellable,
+                        GError          **error)
+{
+  gboolean ret = FALSE;
+  GFileInfo *file_info = NULL;
+  GInputStream *input = NULL;
+  GFile *stored_path = NULL;
+  GFile *pending_path = NULL;
+  char *pack_checksum = NULL;
+  gboolean is_stored;
+  gboolean ret_is_pending;
+
+  g_assert (objtype != OSTREE_OBJECT_TYPE_RAW_FILE);
+
+  if (!fetch_object_if_not_stored (pull_data, checksum, objtype,
+                                   &is_stored, &ret_is_pending, &input,
+                                   cancellable, error))
+    goto out;
+
+  if (ret_is_pending || input)
     {
-      if (!ostree_repo_stage_object (repo, objtype, checksum, file_info, NULL, input, cancellable, error))
+      if (!ostree_repo_stage_object (pull_data->repo, objtype, checksum, NULL, NULL,
+                                     input, cancellable, error))
         goto out;
 
       log_verbose ("Staged object: %s.%s", checksum, ostree_object_type_to_string (objtype));
+    }
 
-      ret_is_pending = TRUE;
-      if (out_metadata)
+  ret = TRUE;
+  if (out_is_pending)
+    *out_is_pending = ret_is_pending;
+ out:
+  g_clear_object (&file_info);
+  g_clear_object (&input);
+  g_clear_object (&stored_path);
+  g_clear_object (&pending_path);
+  g_free (pack_checksum);
+  return ret;
+}
+
+static gboolean
+fetch_and_store_metadata (OtPullData          *pull_data,
+                          const char          *checksum,
+                          OstreeObjectType     objtype,
+                          gboolean            *out_is_pending,
+                          GVariant           **out_variant,
+                          GCancellable        *cancellable,
+                          GError             **error)
+{
+  gboolean ret = FALSE;
+  gboolean ret_is_pending;
+  GVariant *ret_variant = NULL;
+
+  if (!fetch_and_store_object (pull_data, checksum, objtype,
+                               &ret_is_pending, cancellable, error))
+    goto out;
+
+  if (!ostree_repo_load_variant (pull_data->repo, objtype, checksum,
+                                 &ret_variant, error))
+    goto out;
+
+  ret = TRUE;
+  ot_transfer_out_value (out_variant, &ret_variant);
+  if (out_is_pending)
+    *out_is_pending = ret_is_pending;
+ out:
+  ot_clear_gvariant (&ret_variant);
+  return ret;
+}
+
+static gboolean
+fetch_and_store_file (OtPullData          *pull_data,
+                      const char          *checksum,
+                      GCancellable        *cancellable,
+                      GError             **error)
+{
+  gboolean ret = FALSE;
+  GInputStream *input = NULL;
+  GFile *stored_path = NULL;
+  GFile *pending_path = NULL;
+  char *pack_checksum = NULL;
+  GVariant *archive_metadata = NULL;
+  GFileInfo *archive_file_info = NULL;
+  GVariant *archive_xattrs = NULL;
+  gboolean skip_archive_fetch;
+
+  /* If we're fetching from an archive into a bare repository, we need
+   * to explicitly check for raw file types locally.
+   */
+  if (ostree_repo_get_mode (pull_data->repo) == OSTREE_REPO_MODE_BARE)
+    {
+      if (!ostree_repo_find_object (pull_data->repo, OSTREE_OBJECT_TYPE_RAW_FILE,
+                                    checksum, &stored_path, &pending_path, &pack_checksum,
+                                    NULL, cancellable, error))
+        goto out;
+      
+      if (stored_path || pack_checksum)
+        skip_archive_fetch = TRUE;
+      else if (pending_path != NULL)
         {
-          if (!ostree_map_metadata_file (pending_path ? pending_path : temp_path, objtype, &ret_metadata, error))
+          skip_archive_fetch = TRUE;
+          if (!ostree_repo_stage_object (pull_data->repo, OSTREE_OBJECT_TYPE_RAW_FILE,
+                                         checksum, NULL, NULL, NULL, cancellable, error))
             goto out;
         }
+      else
+        skip_archive_fetch = FALSE;
+      
+      g_clear_object (&stored_path);
     }
   else
     {
-      ret_is_pending = FALSE;
+      skip_archive_fetch = FALSE;
     }
 
+  if (!skip_archive_fetch)
+    {
+      if (!fetch_object_if_not_stored (pull_data, checksum,
+                                       OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META,
+                                       NULL, NULL, &input, cancellable, error))
+        goto out;
+
+      if (input != NULL)
+        {
+          if (!ot_util_variant_from_stream (input, OSTREE_SERIALIZED_VARIANT_FORMAT,
+                                            FALSE, &archive_metadata, cancellable, error))
+            goto out;
+  
+          if (!ostree_parse_archived_file_meta (archive_metadata, &archive_file_info,
+                                                &archive_xattrs, error))
+            goto out;
+
+          g_clear_object (&input);
+          if (g_file_info_get_file_type (archive_file_info) == G_FILE_TYPE_REGULAR)
+            {
+              if (!fetch_object_if_not_stored (pull_data, checksum,
+                                               OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT,
+                                               NULL, NULL, &input,
+                                               cancellable, error))
+                goto out;
+            }
+
+          if (!ostree_repo_stage_object (pull_data->repo, OSTREE_OBJECT_TYPE_RAW_FILE, checksum,
+                                         archive_file_info, archive_xattrs, input,
+                                         cancellable, error))
+            goto out;
+        }
+    }              
+
   ret = TRUE;
-  if (out_is_pending)
-    *out_is_pending = ret_is_pending;
-  ot_transfer_out_value (out_metadata, &ret_metadata);
  out:
-  if (temp_path)
-    (void) unlink (ot_gfile_get_path_cached (temp_path));
-  ot_clear_gvariant (&ret_metadata);
-  g_clear_object (&temp_path);
-  g_clear_object (&file_info);
-  g_clear_object (&input);
+  g_free (pack_checksum);
   g_clear_object (&stored_path);
   g_clear_object (&pending_path);
-  g_free (pack_checksum);
+  g_clear_object (&input);
+  ot_clear_gvariant (&archive_xattrs);
+  g_clear_object (&archive_file_info);
   return ret;
 }
 
 static gboolean
-fetch_and_store_tree_recurse (OstreeRepo   *repo,
-                              SoupSession  *soup,
-                              SoupURI      *base_uri,
+fetch_and_store_tree_recurse (OtPullData   *pull_data,
                               const char   *rev,
                               GCancellable *cancellable,
                               GError      **error)
@@ -307,18 +741,12 @@ fetch_and_store_tree_recurse (OstreeRepo   *repo,
   GVariant *dirs_variant = NULL;
   gboolean is_pending;
   int i, n;
-  GVariant *archive_metadata = NULL;
-  GFileInfo *archive_file_info = NULL;
-  GVariant *archive_xattrs = NULL;
-  GFile *meta_temp_path = NULL;
-  GFile *content_temp_path = NULL;
   GFile *stored_path = NULL;
   GFile *pending_path = NULL;
   char *pack_checksum = NULL;
-  GInputStream *input = NULL;
 
-  if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_DIR_TREE,
-                               &is_pending, &tree, cancellable, error))
+  if (!fetch_and_store_metadata (pull_data, rev, OSTREE_OBJECT_TYPE_DIR_TREE,
+                                 &is_pending, &tree, cancellable, error))
     goto out;
 
   if (!is_pending)
@@ -342,85 +770,8 @@ fetch_and_store_tree_recurse (OstreeRepo   *repo,
           if (!ostree_validate_checksum_string (checksum, error))
             goto out;
 
-          g_clear_object (&stored_path);
-          g_clear_object (&pending_path);
-          g_free (pack_checksum);
-          pack_checksum = NULL;
-          /* If we're fetching from an archive into a bare repository, we need
-           * to explicitly check for raw file types locally.
-           */
-          if (ostree_repo_get_mode (repo) == OSTREE_REPO_MODE_BARE)
-            {
-              if (!ostree_repo_find_object (repo, OSTREE_OBJECT_TYPE_RAW_FILE, checksum,
-                                            &stored_path, &pending_path, &pack_checksum, NULL,
-                                            cancellable, error))
-                goto out;
-            }
-          else
-            {
-              if (!ostree_repo_find_object (repo, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT, checksum,
-                                            &stored_path, &pending_path, &pack_checksum, NULL,
-                                            cancellable, error))
-                goto out;
-            }
-
-          g_clear_object (&input);
-          g_clear_object (&archive_file_info);
-          ot_clear_gvariant (&archive_xattrs);
-          if (!(stored_path || pending_path))
-            {
-              g_clear_object (&meta_temp_path);
-              if (!fetch_object (repo, soup, base_uri, checksum,
-                                 OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META,
-                                 &meta_temp_path,
-                                 cancellable,
-                                 error))
-                goto out;
-
-              if (!ostree_map_metadata_file (meta_temp_path,
-                                             OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META,
-                                             &archive_metadata, error))
-                goto out;
-
-              if (!ostree_parse_archived_file_meta (archive_metadata, &archive_file_info, &archive_xattrs, error))
-                goto out;
-
-              if (g_file_info_get_file_type (archive_file_info) == G_FILE_TYPE_REGULAR)
-                {
-                  if (!fetch_object (repo, soup, base_uri, checksum,
-                                     OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT,
-                                     &content_temp_path,
-                                     cancellable,
-                                     error))
-                    goto out;
-                  
-                  input = (GInputStream*)g_file_read (content_temp_path, cancellable, error);
-                  if (!input)
-                    goto out;
-                }
-            }
-
-          if (!stored_path)
-            {
-              log_verbose ("Staged file object: %s", checksum);
-
-              if (!ostree_repo_stage_object (repo, OSTREE_OBJECT_TYPE_RAW_FILE,
-                                             checksum,
-                                             archive_file_info, archive_xattrs, input,
-                                             cancellable, error))
-                goto out;
-            }
-              
-          if (meta_temp_path)
-            {
-              (void) unlink (ot_gfile_get_path_cached (meta_temp_path));
-              g_clear_object (&meta_temp_path);
-            }
-          if (content_temp_path)
-            {
-              (void) unlink (ot_gfile_get_path_cached (content_temp_path));
-              g_clear_object (&content_temp_path);
-            }
+          if (!fetch_and_store_file (pull_data, checksum, cancellable, error))
+            goto out;
         }
       
       n = g_variant_n_children (dirs_variant);
@@ -440,11 +791,11 @@ fetch_and_store_tree_recurse (OstreeRepo   *repo,
           if (!ostree_validate_checksum_string (meta_checksum, error))
             goto out;
 
-          if (!fetch_and_store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
-                                       NULL, NULL, cancellable, error))
+          if (!fetch_and_store_object (pull_data, meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
+                                       NULL, cancellable, error))
             goto out;
 
-          if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_checksum, cancellable, error))
+          if (!fetch_and_store_tree_recurse (pull_data, tree_checksum, cancellable, error))
             goto out;
         }
     }
@@ -454,30 +805,14 @@ fetch_and_store_tree_recurse (OstreeRepo   *repo,
   ot_clear_gvariant (&tree);
   ot_clear_gvariant (&files_variant);
   ot_clear_gvariant (&dirs_variant);
-  ot_clear_gvariant (&archive_metadata);
-  ot_clear_gvariant (&archive_xattrs);
-  g_clear_object (&archive_file_info);
-  g_clear_object (&input);
   g_clear_object (&stored_path);
   g_clear_object (&pending_path);
   g_free (pack_checksum);
-  if (content_temp_path)
-    {
-      (void) unlink (ot_gfile_get_path_cached (content_temp_path));
-      g_clear_object (&content_temp_path);
-    }
-  if (meta_temp_path)
-    {
-      (void) unlink (ot_gfile_get_path_cached (meta_temp_path));
-      g_clear_object (&meta_temp_path);
-    }
   return ret;
 }
 
 static gboolean
-fetch_and_store_commit_recurse (OstreeRepo   *repo,
-                                SoupSession  *soup,
-                                SoupURI      *base_uri,
+fetch_and_store_commit_recurse (OtPullData   *pull_data,
                                 const char   *rev,
                                 GCancellable *cancellable,
                                 GError      **error)
@@ -488,8 +823,8 @@ fetch_and_store_commit_recurse (OstreeRepo   *repo,
   const char *tree_meta_checksum;
   gboolean is_pending;
 
-  if (!fetch_and_store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_COMMIT,
-                               &is_pending, &commit, cancellable, error))
+  if (!fetch_and_store_metadata (pull_data, rev, OSTREE_OBJECT_TYPE_COMMIT,
+                                 &is_pending, &commit, cancellable, error))
     goto out;
 
   if (!is_pending)
@@ -500,11 +835,11 @@ fetch_and_store_commit_recurse (OstreeRepo   *repo,
       g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
       g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
       
-      if (!fetch_and_store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
-                                   NULL, NULL, cancellable, error))
+      if (!fetch_and_store_object (pull_data, tree_meta_checksum, OSTREE_OBJECT_TYPE_DIR_META,
+                                   NULL, cancellable, error))
         goto out;
       
-      if (!fetch_and_store_tree_recurse (repo, soup, base_uri, tree_contents_checksum,
+      if (!fetch_and_store_tree_recurse (pull_data, tree_contents_checksum,
                                          cancellable, error))
         goto out;
     }
@@ -516,9 +851,7 @@ fetch_and_store_commit_recurse (OstreeRepo   *repo,
 }
 
 static gboolean
-fetch_ref_contents (OstreeRepo    *repo,
-                    SoupSession   *soup,
-                    SoupURI       *base_uri,
+fetch_ref_contents (OtPullData    *pull_data,
                     const char    *ref,
                     char         **out_contents,
                     GCancellable  *cancellable,
@@ -526,14 +859,11 @@ fetch_ref_contents (OstreeRepo    *repo,
 {
   gboolean ret = FALSE;
   char *ret_contents = NULL;
-  char *refpath = NULL;
   SoupURI *target_uri = NULL;
 
-  target_uri = soup_uri_copy (base_uri);
-  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", ref, NULL);
-  soup_uri_set_path (target_uri, refpath);
+  target_uri = suburi_new (pull_data->base_uri, "refs", "heads", ref, NULL);
   
-  if (!fetch_uri_contents_utf8 (repo, soup, target_uri, &ret_contents, cancellable, error))
+  if (!fetch_uri_contents_utf8 (pull_data, target_uri, &ret_contents, cancellable, error))
     goto out;
 
   g_strchomp (ret_contents);
@@ -544,7 +874,6 @@ fetch_ref_contents (OstreeRepo    *repo,
   ret = TRUE;
   ot_transfer_out_value (out_contents, &ret_contents);
  out:
-  g_free (refpath);
   g_free (ret_contents);
   if (target_uri)
     soup_uri_free (target_uri);
@@ -552,12 +881,9 @@ fetch_ref_contents (OstreeRepo    *repo,
 }
 
 static gboolean
-pull_one_commit (OstreeRepo       *repo,
-                 const char       *remote,
+pull_one_commit (OtPullData       *pull_data,
                  const char       *branch,
                  const char       *rev,
-                 SoupSession      *soup,
-                 SoupURI          *base_uri,
                  GCancellable     *cancellable,
                  GError          **error)
 {
@@ -567,9 +893,9 @@ pull_one_commit (OstreeRepo       *repo,
   char *baseurl = NULL;
   char *original_rev = NULL;
 
-  remote_ref = g_strdup_printf ("%s/%s", remote, branch);
+  remote_ref = g_strdup_printf ("%s/%s", pull_data->remote_name, branch);
 
-  if (!ostree_repo_resolve_rev (repo, remote_ref, TRUE, &original_rev, error))
+  if (!ostree_repo_resolve_rev (pull_data->repo, remote_ref, TRUE, &original_rev, error))
     goto out;
 
   if (original_rev && strcmp (rev, original_rev) == 0)
@@ -581,16 +907,27 @@ pull_one_commit (OstreeRepo       *repo,
       if (!ostree_validate_checksum_string (rev, error))
         goto out;
 
-      if (!ostree_repo_prepare_transaction (repo, NULL, error))
+      if (!pull_data->fetched_packs)
+        {
+          pull_data->fetched_packs = TRUE;
+          pull_data->cached_pack_indexes = g_ptr_array_new_with_free_func (g_free);
+
+          g_print ("Fetching packs\n");
+
+          if (!fetch_and_cache_pack_indexes (pull_data, cancellable, error))
+            goto out;
+        }
+
+      if (!ostree_repo_prepare_transaction (pull_data->repo, NULL, error))
         goto out;
       
-      if (!fetch_and_store_commit_recurse (repo, soup, base_uri, rev, cancellable, error))
+      if (!fetch_and_store_commit_recurse (pull_data, rev, cancellable, error))
         goto out;
 
-      if (!ostree_repo_commit_transaction (repo, cancellable, error))
+      if (!ostree_repo_commit_transaction (pull_data->repo, cancellable, error))
         goto out;
       
-      if (!ostree_repo_write_ref (repo, remote, branch, rev, error))
+      if (!ostree_repo_write_ref (pull_data->repo, pull_data->remote_name, branch, rev, error))
         goto out;
       
       g_print ("remote %s is now %s\n", remote_ref, rev);
@@ -666,9 +1003,9 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
 {
   GOptionContext *context;
   gboolean ret = FALSE;
+  OtPullData pull_data_real;
+  OtPullData *pull_data = &pull_data_real;
   OstreeRepo *repo = NULL;
-  const char *remote;
-  SoupSession *soup = NULL;
   char *path = NULL;
   char *baseurl = NULL;
   char *summary_data = NULL;
@@ -692,27 +1029,29 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
   if (!ostree_repo_check (repo, error))
     goto out;
 
+  memset (pull_data, 0, sizeof (*pull_data));
+  pull_data->repo = repo;
+
   if (argc < 2)
     {
       ot_util_usage_error (context, "REMOTE must be specified", error);
       goto out;
     }
 
-  remote = argv[1];
-
-  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
-                                             NULL);
+  pull_data->remote_name = g_strdup (argv[1]);
+  pull_data->session = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+                                                           SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+                                                           NULL);
 
   config = ostree_repo_get_config (repo);
 
-  key = g_strdup_printf ("remote \"%s\"", remote);
+  key = g_strdup_printf ("remote \"%s\"", pull_data->remote_name);
   baseurl = g_key_file_get_string (config, key, "url", error);
   if (!baseurl)
     goto out;
-  base_uri = soup_uri_new (baseurl);
+  pull_data->base_uri = soup_uri_new (baseurl);
 
-  if (!base_uri)
+  if (!pull_data->base_uri)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "Failed to parse url '%s'", baseurl);
@@ -727,7 +1066,7 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
           const char *branch = argv[i];
           char *contents;
           
-          if (!fetch_ref_contents (repo, soup, base_uri, branch, &contents, cancellable, error))
+          if (!fetch_ref_contents (pull_data, branch, &contents, cancellable, error))
             goto out;
       
           /* Transfer ownership of contents */
@@ -740,7 +1079,7 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
       path = g_build_filename (soup_uri_get_path (summary_uri), "refs", "summary", NULL);
       soup_uri_set_path (summary_uri, path);
 
-      if (!fetch_uri_contents_utf8 (repo, soup, summary_uri, &summary_data, cancellable, error))
+      if (!fetch_uri_contents_utf8 (pull_data, summary_uri, &summary_data, cancellable, error))
         goto out;
 
       if (!parse_ref_summary (summary_data, &refs_to_fetch, error))
@@ -754,7 +1093,7 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
       const char *ref = key;
       const char *sha256 = value;
       
-      if (!pull_one_commit (repo, remote, ref, sha256, soup, base_uri, cancellable, error))
+      if (!pull_one_commit (pull_data, ref, sha256, cancellable, error))
         goto out;
     }
 
@@ -768,13 +1107,14 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
   g_free (branch_rev);
   if (context)
     g_option_context_free (context);
-  g_clear_object (&soup);
-  if (base_uri)
-    soup_uri_free (base_uri);
+  g_clear_object (&pull_data->session);
+  if (pull_data->base_uri)
+    soup_uri_free (pull_data->base_uri);
+  if (pull_data->cached_pack_indexes)
+    g_ptr_array_unref (pull_data->cached_pack_indexes);
   if (summary_uri)
     soup_uri_free (summary_uri);
   g_clear_object (&repo);
-  g_clear_object (&soup);
   return ret;
 }
 
diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c
index 06a305c..41876c4 100644
--- a/src/ostree/ot-builtin-fsck.c
+++ b/src/ostree/ot-builtin-fsck.c
@@ -204,6 +204,8 @@ fsck_pack_files (OtFsckData  *data,
 {
   gboolean ret = FALSE;
   GPtrArray *pack_indexes = NULL;
+  GVariant *index_variant = NULL;
+  GFile *pack_index_path = NULL;
   GFile *pack_data_path = NULL;
   GInputStream *input = NULL;
   GChecksum *pack_content_checksum = NULL;
@@ -216,6 +218,22 @@ fsck_pack_files (OtFsckData  *data,
     {
       const char *checksum = pack_indexes->pdata[i];
 
+      g_clear_object (&pack_index_path);
+      pack_index_path = ostree_repo_get_pack_index_path (data->repo, checksum);
+
+      ot_clear_gvariant (&index_variant);
+      if (!ot_util_variant_map (pack_index_path,
+                                OSTREE_PACK_INDEX_VARIANT_FORMAT,
+                                &index_variant, error))
+        goto out;
+      
+      if (!ostree_validate_structureof_pack_index (index_variant, error))
+        {
+          g_prefix_error (error, "Corrupted pack index '%s': ",
+                          ot_gfile_get_path_cached (pack_index_path));
+          goto out;
+        }
+
       g_clear_object (&pack_data_path);
       pack_data_path = ostree_repo_get_pack_data_path (data->repo, checksum);
       
diff --git a/src/ostree/ot-builtin-init.c b/src/ostree/ot-builtin-init.c
index 1bc5392..b194097 100644
--- a/src/ostree/ot-builtin-init.c
+++ b/src/ostree/ot-builtin-init.c
@@ -99,6 +99,11 @@ ostree_builtin_init (int argc, char **argv, GFile *repo_path, GError **error)
   if (!g_file_make_directory (child, NULL, error))
     goto out;
 
+  g_clear_object (&child);
+  child = g_file_get_child (repo_path, "remote-cache");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+
   ret = TRUE;
  out:
   if (context)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]