[ostree/wip/packfile-rebase2] core: More progress on pack files



commit 1a1c11dd3f4ed9bfc35d20712498baf5f97cbbf9
Author: Colin Walters <walters verbum org>
Date:   Thu Mar 22 20:31:17 2012 -0400

    core: More progress on pack files

 src/libostree/README.md          |   17 ++-
 src/libostree/ostree-core.c      |   94 ++++++++
 src/libostree/ostree-core.h      |   43 +++--
 src/libostree/ostree-repo-file.c |   45 +----
 src/libostree/ostree-repo.c      |  480 ++++++++++++++++++++++++--------------
 src/libostree/ostree-repo.h      |   16 +-
 src/ostree/ostree-pull.c         |    7 +-
 src/ostree/ot-builtin-repack.c   |  128 ++++++-----
 tests/t0000-basic.sh             |   13 +-
 tests/t0001-archive.sh           |   13 +-
 10 files changed, 546 insertions(+), 310 deletions(-)
---
diff --git a/src/libostree/README.md b/src/libostree/README.md
index aa1425b..c1d5f68 100644
--- a/src/libostree/README.md
+++ b/src/libostree/README.md
@@ -18,6 +18,7 @@ Key differences versus git
 --------------------------
 
  * As mentioned above, extended attributes and owner uid/gid are versioned
+ * Optimized for Unix hardlinks between repository and checkout
  * SHA256 instead of SHA1
  * Support for empty directories
 
@@ -28,9 +29,16 @@ While this is still in planning, I plan to heavily optimize OSTree for
 versioning ELF operating systems.  In industry jargon, this would be
 "content-aware storage".
 
+Trimming history
+----------------
+
+OSTree will also be optimized to trim intermediate history; in theory
+one can regenerate binaries from corresponding (git) source code, so
+we don't need to keep all possible builds over time.
+
 MILESTONE 1
 -----------
-* Basic pack files
+* Basic pack files (like git)
 
 MILESTONE 2
 -----------
@@ -38,12 +46,17 @@ MILESTONE 2
 * Drop version/metadata from tree/dirmeta objects
 * Restructure repository so that links can be generated as a cache;
   i.e. objects/raw, pack files are now the canonical
-* Commits generate a pack?
 * For files, checksum combination of metadata variant + raw data 
 
 MILESTONE 3
 -----------
 
+* Drop archive/raw distinction - archive repositories always generate
+  packfiles per commit
+* Include git packv4 ideas:
+  - split packfile implementations between metadata and data
+  - metadata packfiles have string dictionary (tree filenames and checksums)
+  - data packfiles match up similar objects
 * Rolling checksums for partitioning large files?  Kernel debuginfo
 * Improved pack clustering
   - file fingerprinting?
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index 376504c..d57dca3 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -597,6 +597,24 @@ ostree_hash_object_name (gconstpointer a)
   return g_str_hash (checksum) + g_int_hash (&objtype_int);
 }
 
+int
+ostree_cmp_checksum_bytes (GVariant *a,
+                           GVariant *b)
+{
+  gconstpointer a_data;
+  gconstpointer b_data;
+  gsize a_n_elts;
+  gsize b_n_elts;
+  
+  a_data = g_variant_get_fixed_array (a, &a_n_elts, 1);
+  g_assert (a_n_elts == 32);
+  b_data = g_variant_get_fixed_array (b, &b_n_elts, 1);
+  g_assert (b_n_elts == 32);
+
+  return memcmp (a_data, b_data, 32);
+}
+
+
 GVariant *
 ostree_object_name_serialize (const char *checksum,
                               OstreeObjectType objtype)
@@ -614,6 +632,82 @@ ostree_object_name_deserialize (GVariant         *variant,
   *out_objtype = (OstreeObjectType)objtype_u32;
 }
 
+GVariant *
+ostree_checksum_to_bytes (const char *sha256)
+{
+  guchar result[32];
+  guint i = 0;
+
+  for (i = 0; i < 32; i += 2)
+    {
+      gint big, little;
+
+      g_assert (sha256[i]);
+      g_assert (sha256[i+1]);
+
+      big = g_ascii_xdigit_value (sha256[i]);
+      little = g_ascii_xdigit_value (sha256[i+1]);
+
+      result[i] += (big << 4) | little;
+    }
+  
+  return g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
+                                    result, 32, 1);
+}
+
+char *
+ostree_checksum_from_bytes (GVariant *csum_bytes)
+{
+  GString *ret = g_string_new ("");
+  const guchar *bytes;
+  gsize n_elts;
+  guint i;
+
+  bytes = g_variant_get_fixed_array (csum_bytes, &n_elts, 1);
+  g_assert (n_elts == 32);
+  
+  for (i = 0; i < 32; i++)
+    g_string_append_printf (ret, "%x", bytes[i]);
+
+  return g_string_free (ret, FALSE);
+}
+
+GVariant *
+ostree_object_name_serialize_v2 (const char        *checksum,
+                                 OstreeObjectType   objtype)
+{
+  return g_variant_new ("(u ay)", (guint32)objtype, ostree_checksum_to_bytes (checksum));
+}
+
+void
+ostree_object_name_deserialize_v2_hex (GVariant         *variant,
+                                       char            **out_checksum,
+                                       OstreeObjectType *out_objtype)
+{
+  GVariant *csum_bytes;
+  guint32 objtype_u32;
+
+  g_variant_get (variant, "(u ay)", &objtype_u32, &csum_bytes);
+  g_variant_ref_sink (csum_bytes);
+  *out_checksum = ostree_checksum_from_bytes (csum_bytes);
+  g_variant_unref (csum_bytes);
+  *out_objtype = (OstreeObjectType)objtype_u32;
+}
+
+void
+ostree_object_name_deserialize_v2_bytes (GVariant         *variant,
+                                         const guchar    **out_checksum,
+                                         OstreeObjectType *out_objtype)
+{
+  GVariant *csum_bytes;
+  guint32 objtype_u32;
+  gsize n_elts;
+
+  g_variant_get (variant, "(u ay)", &objtype_u32, &csum_bytes);
+  *out_checksum = (guchar*)g_variant_get_fixed_array (csum_bytes, &n_elts, 1);
+  *out_objtype = (OstreeObjectType)objtype_u32;
+}
+
 char *
 ostree_get_relative_object_path (const char *checksum,
                                  OstreeObjectType type)
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 5feee23..447e338 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -106,36 +106,35 @@ typedef enum {
 #define OSTREE_PACK_SUPER_INDEX_VARIANT_FORMAT G_VARIANT_TYPE ("(sua{sv}a(say))")
 
 /* Pack index
- * s - OSTPACKINDEX
- * u - Version
+ * s - OSTv0PACKINDEX
  * a{sv} - Metadata
- * a(sut) - (checksum, objtype, offset into packfile)
+ * a(uayt) - (objtype, checksum, offset into packfile)
  */
-#define OSTREE_PACK_INDEX_VARIANT_FORMAT G_VARIANT_TYPE ("(sua{sv}a(sut))")
+#define OSTREE_PACK_INDEX_VARIANT_FORMAT G_VARIANT_TYPE ("(sa{sv}a(uayt))")
 
 typedef enum {
-  OSTREE_PACK_FILE_ENTRY_TYPE_GZIP_RAW = (1 << 0)
+  OSTREE_PACK_FILE_ENTRY_FLAG_NONE = 0,
+  OSTREE_PACK_FILE_ENTRY_FLAG_GZIP = (1 << 0)
 } OstreePackFileEntryFlag;
 
 /* Pack files
- * s - OSTPACKFILE
- * u - Version
+ * s - OSTv0PACKFILE
  * a{sv} - Metadata
- * u - number of entries
+ * t - number of entries
  *
- * Repeating tuple of:
- * <padding to alignment of 4>
- * <32 bit BE integer containing variant length>
+ * Repeating pair of:
  * <padding to alignment of 8>
- * ( tuys ) - content_length, objtype, flags, checksum
+ * ( uyayay ) - objtype, flags, checksum, data
  */
-#define OSTREE_PACK_FILE_VARIANT_FORMAT G_VARIANT_TYPE ("(sua{sv}t)")
+#define OSTREE_PACK_FILE_VARIANT_FORMAT G_VARIANT_TYPE ("(sa{sv}t)")
 
-#define OSTREE_PACK_FILE_CONTENT_VARIANT_FORMAT G_VARIANT_TYPE ("(tuys)")
+#define OSTREE_PACK_FILE_CONTENT_VARIANT_FORMAT G_VARIANT_TYPE ("(uyayay)")
 
 gboolean ostree_validate_checksum_string (const char *sha256,
                                           GError    **error);
 
+GVariant *ostree_checksum_to_bytes (const char *sha256);
+
 gboolean ostree_validate_rev (const char *rev, GError **error);
 
 void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
@@ -146,6 +145,8 @@ OstreeObjectType ostree_object_type_from_string (const char *str);
 
 guint ostree_hash_object_name (gconstpointer a);
 
+int ostree_cmp_checksum_bytes (GVariant *a, GVariant *b);
+
 GVariant *ostree_object_name_serialize (const char *checksum,
                                         OstreeObjectType objtype);
 
@@ -153,6 +154,20 @@ void ostree_object_name_deserialize (GVariant         *variant,
                                      const char      **out_checksum,
                                      OstreeObjectType *out_objtype);
 
+GVariant *ostree_object_name_serialize_v2 (const char *checksum,
+                                           OstreeObjectType objtype);
+
+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);
+
+GVariant * ostree_checksum_to_bytes (const char *sha256);
+char * ostree_checksum_from_bytes (GVariant *bytes);
+
 char * ostree_object_to_string (const char *checksum,
                                 OstreeObjectType objtype);
 
diff --git a/src/libostree/ostree-repo-file.c b/src/libostree/ostree-repo-file.c
index 9cc7b82..0370bcc 100644
--- a/src/libostree/ostree-repo-file.c
+++ b/src/libostree/ostree-repo-file.c
@@ -817,13 +817,9 @@ ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
   const char *name = NULL;
   gboolean ret = FALSE;
   GFileInfo *ret_info = NULL;
-  GFile *archive_data_path = NULL;
-  GFileInfo *archive_data_info = NULL;
-  GVariant *archive_metadata = NULL;
   GVariant *files_variant = NULL;
   GVariant *dirs_variant = NULL;
   GVariant *tree_child_metadata = NULL;
-  GFile *local_child = NULL;
   GFileAttributeMatcher *matcher = NULL;
   int c;
 
@@ -844,40 +840,9 @@ ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
 
       g_variant_get_child (files_variant, n, "(&s&s)", &name, &checksum);
 
-      local_child = ostree_repo_get_file_object_path (self->repo, checksum);
-
-      if (ostree_repo_get_mode (self->repo) == OSTREE_REPO_MODE_ARCHIVE)
-	{
-          if (!ostree_map_metadata_file (local_child, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META,
-                                         &archive_metadata, error))
-            goto out;
-          if (!ostree_parse_archived_file_meta (archive_metadata, &ret_info, NULL, error))
-            goto out;
-
-          archive_data_path = ostree_repo_get_object_path (self->repo, checksum,
-                                                           OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
-          archive_data_info = g_file_query_info (archive_data_path,
-                                                 OSTREE_GIO_FAST_QUERYINFO,
-                                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                                 cancellable,
-                                                 error);
-          if (!archive_data_info)
-            goto out;
-          
-          g_file_info_set_attribute_uint64 (ret_info, "standard::size",
-                                            g_file_info_get_attribute_uint64 (archive_data_info,
-                                                                              "standard::size"));
-	}
-      else
-	{
-          ret_info = g_file_query_info (local_child,
-                                        OSTREE_GIO_FAST_QUERYINFO,
-                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                        cancellable,
-                                        error);
-          if (!ret_info)
-            goto out;
-	}
+      if (!ostree_repo_load_file (self->repo, checksum, NULL, &ret_info, NULL,
+                                  cancellable, error))
+        goto out;
     }
   else
     {
@@ -918,12 +883,8 @@ ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
   ot_transfer_out_value(out_info, &ret_info);
  out:
   g_clear_object (&ret_info);
-  g_clear_object (&local_child);
-  g_clear_object (&archive_data_path);
-  g_clear_object (&archive_data_info);
   if (matcher)
     g_file_attribute_matcher_unref (matcher);
-  ot_clear_gvariant (&archive_metadata);
   ot_clear_gvariant (&tree_child_metadata);
   ot_clear_gvariant (&files_variant);
   ot_clear_gvariant (&dirs_variant);
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index ff2486f..323c6da 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -967,6 +967,7 @@ stage_object_impl (OstreeRepo         *self,
   GFile *stored_path = NULL;
   GFile *pending_path = NULL;
   char *pack_checksum = NULL;
+  guint64 pack_offset;
   const char *actual_checksum;
 
   g_return_val_if_fail (priv->in_transaction, FALSE);
@@ -980,7 +981,7 @@ stage_object_impl (OstreeRepo         *self,
     {
       if (!ostree_repo_find_object (self, objtype, expected_checksum,
                                     &stored_path, &pending_path,
-                                    &pack_checksum,
+                                    &pack_checksum, &pack_offset,
                                     cancellable, error))
         goto out;
     }
@@ -1208,33 +1209,6 @@ stage_gvariant_object (OstreeRepo         *self,
   return ret;
 }
 
-gboolean
-ostree_repo_load_variant (OstreeRepo  *self,
-                          OstreeObjectType  expected_type,
-                          const char    *sha256, 
-                          GVariant     **out_variant,
-                          GError       **error)
-{
-  gboolean ret = FALSE;
-  GFile *object_path = NULL;
-  GFile *tmpfile = NULL;
-  GVariant *ret_variant = NULL;
-
-  g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (expected_type), FALSE);
-
-  object_path = ostree_repo_get_object_path (self, sha256, expected_type);
-  if (!ostree_map_metadata_file (object_path, expected_type, &ret_variant, error))
-    goto out;
-
-  ret = TRUE;
-  ot_transfer_out_value (out_variant, &ret_variant);
- out:
-  g_clear_object (&object_path);
-  g_clear_object (&tmpfile);
-  ot_clear_gvariant (&ret_variant);
-  return ret;
-}
-
 static gboolean
 stage_directory_meta (OstreeRepo   *self,
                       GFileInfo    *file_info,
@@ -2618,7 +2592,7 @@ ostree_repo_map_pack_file (OstreeRepo    *self,
 
 static gboolean
 bsearch_in_pack_index (GVariant   *index_contents,
-                       const char *checksum,
+                       GVariant   *csum_bytes,
                        OstreeObjectType objtype,
                        guint64    *out_offset)
 {
@@ -2633,15 +2607,15 @@ bsearch_in_pack_index (GVariant   *index_contents,
 
   while (i <= n)
     {
-      const char *cur_checksum;
+      GVariant *cur_csum_bytes;
       guint32 cur_objtype;
       guint64 cur_offset;
       int c;
 
       m = i + ((n - i) / 2);
 
-      g_variant_get_child (index_contents, m, "&sut", &cur_checksum, &cur_objtype, &cur_offset);      
-      c = strcmp (cur_checksum, checksum);
+      g_variant_get_child (index_contents, m, "(u ayt)", &cur_objtype, &cur_csum_bytes, &cur_offset);      
+      c = ostree_cmp_checksum_bytes (cur_csum_bytes, csum_bytes);
       if (c == 0)
         {
           if (cur_objtype < target_objtype)
@@ -2649,6 +2623,7 @@ bsearch_in_pack_index (GVariant   *index_contents,
           else if (cur_objtype > target_objtype)
             c = 1;
         }
+      g_variant_unref (cur_csum_bytes);
 
       if (c < 0)
         i = m + 1;
@@ -2665,25 +2640,19 @@ bsearch_in_pack_index (GVariant   *index_contents,
 }
 
 static gboolean
-parse_pack_entry (gboolean       trusted,
-                  guchar        *pack_data,
-                  guint64        pack_len,
-                  guint64        offset,
-                  GVariant     **out_header,
-                  GInputStream **out_input,
-                  GCancellable  *cancellable,
-                  GError       **error)
+read_pack_entry (gboolean       trusted,
+                 guchar        *pack_data,
+                 guint64        pack_len,
+                 guint64        offset,
+                 GVariant     **out_entry,
+                 GCancellable  *cancellable,
+                 GError       **error)
 {
   gboolean ret = FALSE;
-  GVariant *ret_header = NULL;
-  GConverter *decompressor = NULL;
-  GInputStream *raw_input = NULL;
-  GInputStream *ret_input = NULL;
-  guint64 data_offset;
-  guint64 header_start;
-  guint64 header_end;
-  guint32 header_len;
-  guchar entry_type;
+  GVariant *ret_entry = NULL;
+  guint64 entry_start;
+  guint64 entry_end;
+  guint32 entry_len;
 
   if (G_UNLIKELY (!(offset < pack_len)))
     {
@@ -2701,117 +2670,201 @@ parse_pack_entry (gboolean       trusted,
     }
 
   g_assert ((((guint64)pack_data+offset) & 0x3) == 0);
-  header_len = GUINT32_FROM_BE (*((guint32*)(pack_data+offset)));
-  header_end = offset + header_len;
-  if (G_UNLIKELY (!(header_end < pack_len)))
+  entry_len = GUINT32_FROM_BE (*((guint32*)(pack_data+offset)));
+  entry_end = offset + entry_len;
+  if (G_UNLIKELY (!(entry_end < pack_len)))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted pack index; out of range header length %u",
-                   header_len);
+                   "Corrupted pack index; out of range entry length %u",
+                   entry_len);
       goto out;
     }
 
-  header_start = offset + 4;
-  if (G_UNLIKELY (!(header_start < pack_len)))
+  entry_start = offset + 4;
+  if (G_UNLIKELY (!(entry_start < pack_len)))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "Corrupted pack index; out of range data offset %" G_GUINT64_FORMAT,
-                   header_start);
+                   entry_start);
       goto out;
     }
 
-  ret_header = g_variant_new_from_data (OSTREE_PACK_FILE_CONTENT_VARIANT_FORMAT,
-                                        pack_data+header_start, header_len,
-                                        trusted, NULL, NULL);
+  ret_entry = g_variant_new_from_data (OSTREE_PACK_FILE_CONTENT_VARIANT_FORMAT,
+                                       pack_data+entry_start, entry_len,
+                                       trusted, NULL, NULL);
+  ret = TRUE;
+  ot_transfer_out_value (out_entry, &ret_entry);
+ out:
+  ot_clear_gvariant (&ret_entry);
+  return ret;
+}
 
-  g_variant_get_child (ret_header, 2, "y", &entry_type);
-  
-  if (entry_type != OSTREE_PACK_FILE_ENTRY_TYPE_GZIP_RAW)
+static GInputStream *
+get_pack_entry_stream (GVariant        *pack_entry)
+{
+  GInputStream *memory_input;
+  GInputStream *ret_input = NULL;
+  GVariant *pack_data = NULL;
+  guchar entry_flags;
+  gconstpointer data_ptr;
+  gsize data_len;
+
+  g_variant_get_child (pack_entry, 1, "y", &entry_flags);
+  g_variant_get_child (pack_entry, 3, "@ay", &pack_data);
+  g_variant_ref_sink (pack_data);
+
+  data_ptr = g_variant_get_fixed_array (pack_data, &data_len, 1);
+  memory_input = g_memory_input_stream_new_from_data (data_ptr, data_len, NULL);
+  g_object_set_data_full ((GObject*)memory_input, "ostree-mem-gvariant",
+                          pack_data, (GDestroyNotify) g_variant_unref);
+
+  if (entry_flags & OSTREE_PACK_FILE_ENTRY_FLAG_GZIP)
     {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted pack; invalid entry type %u",
-                   entry_type);
-      goto out;
-    }
+      GConverter *decompressor;
 
-  /* Skip 4 bytes for the header len, the actual header, then align to
-   * 8.
-   */
-  data_offset = (offset + 4 + header_len + 7) & ~0x7;
-  if (G_UNLIKELY (!(data_offset < pack_len)))
+      decompressor = (GConverter*)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+      ret_input = (GInputStream*)g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
+                                               "converter", decompressor,
+                                               "base-stream", memory_input,
+                                               "close-base-stream", TRUE,
+                                               NULL);
+      g_object_unref (decompressor);
+    }
+  else
     {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted pack index; out of range offset %" G_GUINT64_FORMAT,
-                   offset);
-      goto out;
+      ret_input = memory_input;
+      memory_input = NULL;
     }
 
-  raw_input = (GInputStream*)g_memory_input_stream_new_from_data (pack_data + data_offset,
-                                                                  pack_len - data_offset,
-                                                                  NULL);
+  return ret_input;
+}
 
-  decompressor = (GConverter*)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
-  ret_input = (GInputStream*)g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
-                                           "converter", decompressor,
-                                           "base-stream", raw_input,
-                                           "close-base-stream", TRUE,
-                                           NULL);
+static GVariant *
+get_pack_entry_as_variant (GVariant            *pack_entry,
+                           const GVariantType  *type,
+                           gboolean             trusted)
+{
+  GVariant *pack_data;
+  GVariant *ret_variant;
+  gconstpointer data_ptr;
+  gsize data_len;
 
-  ret = TRUE;
-  ot_transfer_out_value (out_header, &ret_header);
-  ot_transfer_out_value (out_input, &ret_input);
- out:
-  ot_clear_gvariant (&ret_header);
-  g_clear_object (&raw_input);
-  g_clear_object (&decompressor);
-  g_clear_object (&ret_input);
-  return ret;
+  g_variant_get_child (pack_entry, 3, "@ay", &pack_data);
+  data_ptr = g_variant_get_fixed_array (pack_data, &data_len, 1);
+  ret_variant = g_variant_new_from_data (type, data_ptr, data_len, trusted,
+                                         (GDestroyNotify)g_variant_unref,
+                                         pack_data);
+  return ret_variant;
 }
 
 gboolean
-ostree_repo_load_pack_entry (OstreeRepo         *self,
-                             const char         *pack_sha256,
-                             const char         *entry_sha256,
-                             OstreeObjectType    objtype,
-                             GInputStream      **out_input,
-                             GCancellable       *cancellable,
-                             GError            **error)
+ostree_repo_load_file (OstreeRepo         *self,
+                       const char         *checksum,
+                       GInputStream      **out_input,
+                       GFileInfo         **out_file_info,
+                       GVariant          **out_xattrs,
+                       GCancellable       *cancellable,
+                       GError            **error)
 {
   gboolean ret = FALSE;
-  guint64 offset;
-  guchar *pack_data;
-  guint64 pack_len;
-  GVariant *index = NULL;
-  GVariant *index_contents = NULL;
+  GVariant *archive_meta = NULL;
+  GFile *content_loose_path = NULL;
+  GFileInfo *content_loose_info = NULL;
+  char *content_pack_checksum = NULL;
+  guint64 content_pack_offset;
+  guchar *content_pack_data;
+  guint64 content_pack_len;
+  GVariant *packed_object = NULL;
   GInputStream *ret_input = NULL;
+  GFileInfo *ret_file_info = NULL;
+  GVariant *ret_xattrs = NULL;
 
-  if (!ostree_repo_load_pack_index (self, pack_sha256, &index, cancellable, error))
-    goto out;
-
-  index_contents = g_variant_get_child_value (index, 3);
-  
-  if (!bsearch_in_pack_index (index_contents, entry_sha256, objtype, &offset))
+  if (ostree_repo_get_mode (self) == OSTREE_REPO_MODE_ARCHIVE)
     {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Object '%s' of type %u not in pack",
-                   entry_sha256, (guint32)objtype);
-      goto out;
-    }
+      /* First, read the metadata */
+      if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, checksum,
+                                     &archive_meta, error))
+        goto out;
+      if (!ostree_parse_archived_file_meta (archive_meta, 
+                                            &ret_file_info,
+                                            &ret_xattrs,
+                                            error))
+        goto out;
 
-  if (!ostree_repo_map_pack_file (self, pack_sha256, &pack_data, &pack_len,
-                                  cancellable, error))
-    goto out;
+      /* Blah, right now we need to look up the content too to get the file size */
+      if (!ostree_repo_find_object (self, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT,
+                                    checksum, &content_loose_path, NULL,
+                                    &content_pack_checksum, &content_pack_offset,
+                                    cancellable, error))
+        goto out;
 
-  if (!parse_pack_entry (TRUE, pack_data, pack_len, offset, NULL, &ret_input,
-                         cancellable, error))
-    goto out;
+      if (content_loose_path)
+        {
+          content_loose_info = g_file_query_info (content_loose_path, OSTREE_GIO_FAST_QUERYINFO,
+                                                  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+          if (!content_loose_info)
+            goto out;
+
+          g_file_info_set_attribute_uint64 (ret_file_info,
+                                            "standard::size",
+                                            g_file_info_get_attribute_uint64 (content_loose_info, "standard::size"));
+        }
+      /* fixme - don't have file size for packed =/ */
+      
+      /* Now, look for the content */
+      if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR
+          && out_input)
+        {
+          if (content_pack_checksum != NULL)
+            {
+              if (!ostree_repo_map_pack_file (self, content_pack_checksum,
+                                              &content_pack_data, &content_pack_len,
+                                              cancellable, error))
+                goto out;
+              if (!read_pack_entry (TRUE, content_pack_data, content_pack_len, content_pack_offset,
+                                    &packed_object, cancellable, error))
+                goto out;
+              ret_input = get_pack_entry_stream (packed_object);
+            }
+          else if (content_loose_path != NULL)
+            {
+              ret_input = (GInputStream*)g_file_read (content_loose_path, cancellable, error);
+              if (!ret_input)
+                goto out;
+            }
+          else
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                           "Couldn't find object '%s'", checksum);
+              goto out;
+            }
+        }
+    }
+  else
+    {
+      content_loose_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE);
+      ret_file_info = g_file_query_info (content_loose_path, OSTREE_GIO_FAST_QUERYINFO,
+                                         G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+      if (!ret_file_info)
+        goto out;
+      if (out_xattrs)
+        {
+          ret_xattrs = ostree_get_xattrs_for_file (content_loose_path, error);
+          if (!ret_xattrs)
+            goto out;
+        }
+    }
 
   ret = TRUE;
   ot_transfer_out_value (out_input, &ret_input);
+  ot_transfer_out_value (out_file_info, &ret_file_info);
+  ot_transfer_out_value (out_xattrs, &ret_xattrs);
  out:
-  ot_clear_gvariant (&index);
-  ot_clear_gvariant (&index_contents);
+  g_free (content_pack_checksum);
   g_clear_object (&ret_input);
+  g_clear_object (&content_loose_info);
+  g_clear_object (&ret_file_info);
+  ot_clear_gvariant (&ret_xattrs);
   return ret;
 }
 
@@ -2827,7 +2880,8 @@ list_objects_in_index (OstreeRepo                     *self,
   GVariant *index_variant = NULL;
   GVariant *contents;
   GVariantIter content_iter;
-  const char *checksum;
+  GVariant *csum_bytes;
+  char *checksum = NULL;
   guint32 objtype_u32;
   guint64 offset;
 
@@ -2839,7 +2893,7 @@ list_objects_in_index (OstreeRepo                     *self,
   contents = g_variant_get_child_value (index_variant, 3);
   g_variant_iter_init (&content_iter, contents);
 
-  while (g_variant_iter_loop (&content_iter, "(&sut)", &checksum, &objtype_u32, &offset))
+  while (g_variant_iter_loop (&content_iter, "(&u ayt)", &objtype_u32, &csum_bytes, &offset))
     {
       GVariant *obj_key;
       GVariant *objdata;
@@ -2849,7 +2903,9 @@ list_objects_in_index (OstreeRepo                     *self,
 
       g_variant_builder_init (&pack_contents_builder,
                               G_VARIANT_TYPE_STRING_ARRAY);
-
+      
+      g_free (checksum);
+      checksum = ostree_checksum_from_bytes (csum_bytes);
       obj_key = ostree_object_name_serialize (checksum, objtype);
 
       objdata = g_hash_table_lookup (inout_objects, obj_key);
@@ -2880,6 +2936,7 @@ list_objects_in_index (OstreeRepo                     *self,
 
   ret = TRUE;
  out:
+  g_free (checksum);
   g_clear_object (&index_path);
   ot_clear_gvariant (&index_variant);
   ot_clear_gvariant (&contents);
@@ -2953,17 +3010,22 @@ find_object_in_packs (OstreeRepo        *self,
                       const char        *checksum,
                       OstreeObjectType   objtype,
                       char             **out_pack_checksum,
+                      guint64           *out_pack_offset,
                       GCancellable      *cancellable,
                       GError           **error)
 {
   gboolean ret = FALSE;
   GPtrArray *index_checksums = NULL;
   char *ret_pack_checksum = NULL;
+  guint64 ret_pack_offset;
   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);
+
   if (!ostree_repo_list_pack_indexes (self, &index_checksums, cancellable, error))
     goto out;
 
@@ -2982,21 +3044,25 @@ find_object_in_packs (OstreeRepo        *self,
       ot_clear_gvariant (&index_contents);
       index_contents = g_variant_get_child_value (index_variant, 3);
       
-      if (!bsearch_in_pack_index (index_contents, checksum, objtype, &offset))
+      if (!bsearch_in_pack_index (index_contents, csum_bytes, objtype, &offset))
         continue;
 
       ret_pack_checksum = g_strdup (checksum);
+      ret_pack_offset = offset;
       break;
     }
 
   ret = TRUE;
   ot_transfer_out_value (out_pack_checksum, &ret_pack_checksum);
+  if (out_pack_offset)
+    *out_pack_offset = ret_pack_offset;
  out:
   g_free (ret_pack_checksum);
   if (index_checksums)
     g_ptr_array_unref (index_checksums);
   g_clear_object (&index_path);
   ot_clear_gvariant (&index_variant);
+  ot_clear_gvariant (&csum_bytes);
   ot_clear_gvariant (&index_contents);
   return ret;
 }
@@ -3008,48 +3074,129 @@ ostree_repo_find_object (OstreeRepo           *self,
                          GFile               **out_stored_path,
                          GFile               **out_pending_path,
                          char                **out_pack_checksum,
+                         guint64              *out_pack_offset,
                          GCancellable         *cancellable,
                          GError             **error)
 {
   gboolean ret = FALSE;
   GFile *object_path = NULL;
+  GFile *ret_stored_path = NULL;
+  GFile *ret_pending_path = NULL;
   char *ret_pack_checksum = NULL;
+  guint64 ret_pack_offset = 0;
   struct stat stbuf;
 
-  g_return_val_if_fail (out_stored_path, FALSE);
-  g_return_val_if_fail (out_pending_path, FALSE);
-
   object_path = ostree_repo_get_object_path (self, checksum, objtype);
   
-  *out_stored_path = NULL;
-  *out_pending_path = NULL;
   if (lstat (ot_gfile_get_path_cached (object_path), &stbuf) == 0)
     {
-      *out_stored_path = object_path;
+      ret_stored_path = object_path;
       object_path = NULL;
     }
   else
     {
       g_clear_object (&object_path);
-      object_path = get_pending_object_path (self, checksum, objtype);
-      if (lstat (ot_gfile_get_path_cached (object_path), &stbuf) == 0)
+      if (out_pending_path)
         {
-          *out_pending_path = object_path;
-          object_path = NULL;
+          object_path = get_pending_object_path (self, checksum, objtype);
+          if (lstat (ot_gfile_get_path_cached (object_path), &stbuf) == 0)
+            {
+              ret_pending_path = object_path;
+              object_path = NULL;
+            }
         }
-      if (!find_object_in_packs (self, checksum, objtype, &ret_pack_checksum,
+    }
+
+  if (out_pack_checksum)
+    {
+      if (!find_object_in_packs (self, checksum, objtype,
+                                 &ret_pack_checksum, &ret_pack_offset,
                                  cancellable, error))
         goto out;
     }
   
   ret = TRUE;
+  ot_transfer_out_value (out_stored_path, &ret_stored_path);
+  ot_transfer_out_value (out_pending_path, &ret_pending_path);
   ot_transfer_out_value (out_pack_checksum, &ret_pack_checksum);
+  if (out_pack_offset)
+    *out_pack_offset = ret_pack_offset;
 out:
   g_clear_object (&object_path);
+  g_clear_object (&ret_stored_path);
+  g_clear_object (&ret_pending_path);
   g_free (ret_pack_checksum);
   return ret;
 }
 
+gboolean
+ostree_repo_load_variant (OstreeRepo  *self,
+                          OstreeObjectType  expected_type,
+                          const char    *sha256, 
+                          GVariant     **out_variant,
+                          GError       **error)
+{
+  gboolean ret = FALSE;
+  GFile *object_path = NULL;
+  GVariant *packed_object = NULL;
+  GVariant *container_variant = NULL;
+  GVariant *ret_variant = NULL;
+  char *pack_checksum = NULL;
+  guchar *pack_data;
+  guint64 pack_len;
+  guint64 object_offset;
+  GCancellable *cancellable = NULL;
+
+  g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (expected_type), FALSE);
+
+  if (!ostree_repo_find_object (self, expected_type, sha256, &object_path, NULL,
+                                &pack_checksum, &object_offset,
+                                cancellable, error))
+    goto out;
+
+  /* Prefer loose metadata for now */
+  if (object_path != NULL)
+    {
+      if (!ostree_map_metadata_file (object_path, expected_type, &ret_variant, error))
+        goto out;
+    }
+  else
+    {
+      guint32 actual_type;
+
+      if (!ostree_repo_map_pack_file (self, pack_checksum, &pack_data, &pack_len,
+                                      cancellable, error))
+        goto out;
+      
+      if (!read_pack_entry (TRUE, pack_data, pack_len, object_offset, &packed_object,
+                            cancellable, error))
+        goto out;
+
+      container_variant = get_pack_entry_as_variant (packed_object, OSTREE_SERIALIZED_VARIANT_FORMAT, TRUE);
+      
+      g_variant_get (container_variant, "(uv)",
+                     &actual_type, &ret_variant);
+      ot_util_variant_take_ref (ret_variant);
+
+      if (actual_type != expected_type)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted metadata object '%s'; found type %u, expected %u",
+                       sha256, actual_type, (guint32)expected_type);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_variant, &ret_variant);
+ out:
+  g_clear_object (&object_path);
+  g_free (pack_checksum);
+  ot_clear_gvariant (&ret_variant);
+  ot_clear_gvariant (&packed_object);
+  ot_clear_gvariant (&container_variant);
+  return ret;
+}
 /**
  * ostree_repo_list_objects:
  * @self:
@@ -3256,58 +3403,46 @@ checkout_one_file (OstreeRepo                  *self,
 {
   gboolean ret = FALSE;
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GVariant *archive_metadata = NULL;
+  GFile *possible_loose_path = NULL;
+  GInputStream *input = NULL;
   GVariant *xattrs = NULL;
   const char *checksum;
-  GFile *content_object_path = NULL;
-  GInputStream *content_input = NULL;
-  GFile *object_path = NULL;
+  struct stat stbuf;
 
   checksum = ostree_repo_file_get_checksum ((OstreeRepoFile*)src);
 
+  /* First check for a loose object */
   if (priv->mode == OSTREE_REPO_MODE_ARCHIVE && mode == OSTREE_REPO_CHECKOUT_MODE_USER)
     {
-      object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
-
-      if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, destination, cancellable, error) < 0)
-        goto out;
+      possible_loose_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
     }
-  else if (priv->mode == OSTREE_REPO_MODE_ARCHIVE)
+  else if (priv->mode == OSTREE_REPO_MODE_BARE && mode == OSTREE_REPO_CHECKOUT_MODE_NONE)
     {
-      if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, checksum, &archive_metadata, error))
-        goto out;
-              
-      if (!ostree_parse_archived_file_meta (archive_metadata, NULL, &xattrs, error))
-        goto out;
-              
-      content_object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
-
-      if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
-        {
-          content_input = (GInputStream*)g_file_read (content_object_path, cancellable, error);
-          if (!content_input)
-            goto out;
-        }
+      possible_loose_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE);
+    }
 
-      if (!checkout_file_from_input (destination, mode, overwrite_mode, file_info, xattrs, 
-                                     content_input, cancellable, error))
+  if (possible_loose_path && lstat (ot_gfile_get_path_cached (possible_loose_path), &stbuf) >= 0)
+    {
+      /* If we found one, we can just hardlink */
+      if (!checkout_file_hardlink (self, mode, overwrite_mode, possible_loose_path, destination,
+                                   cancellable, error) < 0)
         goto out;
     }
   else
     {
-      object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE);
+      if (!ostree_repo_load_file (self, checksum, &input, NULL, &xattrs, cancellable, error))
+        goto out;
 
-      if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, destination, cancellable, error) < 0)
+      if (!checkout_file_from_input (destination, mode, overwrite_mode, file_info, xattrs, 
+                                     input, cancellable, error))
         goto out;
     }
 
   ret = TRUE;
  out:
-  g_clear_object (&object_path);
-  ot_clear_gvariant (&archive_metadata);
+  g_clear_object (&possible_loose_path);
+  g_clear_object (&input);
   ot_clear_gvariant (&xattrs);
-  g_clear_object (&content_object_path);
-  g_clear_object (&content_input);
   return ret;
 }
 
@@ -3393,6 +3528,7 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
   g_free (dest_path);
   return ret;
 }
+
 gboolean
 ostree_repo_read_commit (OstreeRepo *self,
                          const char *rev, 
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 2fc4508..a825d48 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -98,6 +98,7 @@ gboolean      ostree_repo_find_object (OstreeRepo           *self,
                                        GFile               **out_stored_path,
                                        GFile               **out_pending_path,
                                        char                **out_pack_checksum,
+                                       guint64              *out_pack_offset,
                                        GCancellable         *cancellable,
                                        GError              **error);
 
@@ -161,13 +162,14 @@ gboolean ostree_repo_map_pack_file (OstreeRepo    *self,
                                     GCancellable  *cancellable,
                                     GError       **error);
 
-gboolean ostree_repo_load_pack_entry (OstreeRepo         *self,
-                                      const char         *pack_sha256,
-                                      const char         *entry_sha256,
-                                      OstreeObjectType    objtype,
-                                      GInputStream      **object_input,
-                                      GCancellable       *cancellable,
-                                      GError            **error);
+gboolean ostree_repo_load_file (OstreeRepo         *self,
+                                const char         *entry_sha256,
+                                GInputStream      **out_input,
+                                GFileInfo         **out_file_info,
+                                GVariant          **out_xattrs,
+                                GCancellable       *cancellable,
+                                GError            **error);
+
 typedef enum {
   OSTREE_REPO_COMMIT_FILTER_ALLOW,
   OSTREE_REPO_COMMIT_FILTER_SKIP
diff --git a/src/ostree/ostree-pull.c b/src/ostree/ostree-pull.c
index 619042e..b9a76a5 100644
--- a/src/ostree/ostree-pull.c
+++ b/src/ostree/ostree-pull.c
@@ -234,7 +234,8 @@ fetch_and_store_object (OstreeRepo  *repo,
   g_assert (objtype != OSTREE_OBJECT_TYPE_RAW_FILE);
 
   if (!ostree_repo_find_object (repo, objtype, checksum,
-                                &stored_path, &pending_path, &pack_checksum,
+                                &stored_path, &pending_path,
+                                &pack_checksum, NULL,
                                 cancellable, error))
     goto out;
       
@@ -351,14 +352,14 @@ fetch_and_store_tree_recurse (OstreeRepo   *repo,
           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,
+                                            &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,
+                                            &stored_path, &pending_path, &pack_checksum, NULL,
                                             cancellable, error))
                 goto out;
             }
diff --git a/src/ostree/ot-builtin-repack.c b/src/ostree/ot-builtin-repack.c
index 40c92f9..ceefd6d 100644
--- a/src/ostree/ot-builtin-repack.c
+++ b/src/ostree/ot-builtin-repack.c
@@ -188,17 +188,17 @@ compare_index_content (gconstpointer         ap,
   gpointer b = *((gpointer*)bp);
   GVariant *a_v = a;
   GVariant *b_v = b;
-  const char *a_checksum;
-  const char *b_checksum;
+  GVariant *a_csum_bytes;
+  GVariant *b_csum_bytes;
   guint32 a_objtype;
   guint32 b_objtype;
   guint64 a_offset;
   guint64 b_offset;
   int c;
 
-  g_variant_get (a_v, "(&sut)", &a_checksum, &a_objtype, &a_offset);      
-  g_variant_get (b_v, "(&sut)", &b_checksum, &b_objtype, &b_offset);      
-  c = strcmp (a_checksum, b_checksum);
+  g_variant_get (a_v, "(u ayt)", &a_objtype, &a_csum_bytes, &a_offset);      
+  g_variant_get (b_v, "(u ayt)", &b_objtype, &b_csum_bytes, &b_offset);      
+  c = ostree_cmp_checksum_bytes (a_csum_bytes, b_csum_bytes);
   if (c == 0)
     {
       if (a_objtype < b_objtype)
@@ -232,13 +232,14 @@ create_pack_file (OtRepackData        *data,
   gsize bytes_written;
   GPtrArray *index_content_list = NULL;
   GVariant *pack_header = NULL;
-  GVariant *object_header = NULL;
+  GVariant *packed_object = NULL;
   GVariant *index_content = NULL;
   GVariantBuilder index_content_builder;
   GChecksum *pack_checksum = NULL;
   char *pack_name = NULL;
   GFile *pack_file_path = NULL;
   GFile *pack_index_path = NULL;
+  GMemoryOutputStream *object_data_stream = NULL;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
@@ -262,10 +263,10 @@ create_pack_file (OtRepackData        *data,
   offset = 0;
   pack_checksum = g_checksum_new (G_CHECKSUM_SHA256);
 
-  pack_header = g_variant_new ("(su a{sv}u)",
-                               "OSTPACK", GUINT32_TO_BE (0),
+  pack_header = g_variant_new ("(s a{sv}t)",
+                               "OSTv0PACKFILE",
                                g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0),
-                               objects->len);
+                               (guint64)objects->len);
 
   if (!write_variant_with_size (pack_out, pack_header, pack_checksum, &offset,
                                 cancellable, error))
@@ -281,8 +282,9 @@ create_pack_file (OtRepackData        *data,
       guint64 obj_bytes_written;
       guint64 expected_objsize;
       guint64 objsize;
-      GOutputStream *write_pack_out;
-      guchar entry_type;
+      GOutputStream *write_object_out;
+      guchar entry_flags = 0;
+      GVariant *index_entry;
 
       g_variant_get (object_data, "(&sut)", &checksum, &objtype_u32, &expected_objsize);
                      
@@ -292,21 +294,30 @@ create_pack_file (OtRepackData        *data,
         goto out;
 
       /* offset points to aligned header size */
-      g_ptr_array_add (index_content_list,
-                       g_variant_ref_sink (g_variant_new ("(sut)", checksum, (guint32)objtype, offset)));
+      index_entry = g_variant_new ("(uayt)",
+                                   (guint32)objtype, ostree_checksum_to_bytes (checksum), offset);
+      g_ptr_array_add (index_content_list, g_variant_ref_sink (index_entry));
 
-      ot_clear_gvariant (&object_header);
-      switch (data->int_compression)
+      if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE
+          || objtype == OSTREE_OBJECT_TYPE_DIR_META
+          || objtype == OSTREE_OBJECT_TYPE_COMMIT)
         {
-        case OT_COMPRESSION_GZIP:
-          {
-            entry_type = OSTREE_PACK_FILE_ENTRY_TYPE_GZIP_RAW;
-            break;
-          }
-        default:
-          {
-            g_assert_not_reached ();
-          }
+          ;
+        }
+      else
+        {
+          switch (data->int_compression)
+            {
+            case OT_COMPRESSION_GZIP:
+              {
+                entry_flags |= OSTREE_PACK_FILE_ENTRY_FLAG_GZIP;
+                break;
+              }
+            default:
+              {
+                g_assert_not_reached ();
+              }
+            }
         }
 
       g_clear_object (&object_path);
@@ -326,38 +337,26 @@ create_pack_file (OtRepackData        *data,
 
       g_assert_cmpint (objsize, ==, expected_objsize);
 
-      ot_clear_gvariant (&object_header);
-      object_header = g_variant_new ("(tuys)", GUINT64_TO_BE (objsize),
-                                     GUINT32_TO_BE ((guint32)objtype),
-                                     entry_type,
-                                     checksum);
-
-      if (!write_variant_with_size (pack_out, object_header, pack_checksum,
-                                    &offset, cancellable, error))
-        goto out;
+      g_clear_object (&object_data_stream);
+      object_data_stream = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, NULL, NULL);
       
-      if (data->int_compression != OT_COMPRESSION_NONE)
+      if (entry_flags & OSTREE_PACK_FILE_ENTRY_FLAG_GZIP)
         {
           g_clear_object (&compressor);
-          switch (data->int_compression)
-            {
-            case OT_COMPRESSION_GZIP:
-              compressor = (GConverter*)g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, OT_GZIP_COMPRESSION_LEVEL);
-              break;
-            default:
-              g_assert_not_reached ();
-            }
+          compressor = (GConverter*)g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, OT_GZIP_COMPRESSION_LEVEL);
           
           g_clear_object (&compressed_object_output);
           compressed_object_output = (GConverterOutputStream*)g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
                                                                             "converter", compressor,
-                                                                            "base-stream", pack_out,
-                                                                            "close-base-stream", FALSE,
+                                                                            "base-stream", object_data_stream,
+                                                                            "close-base-stream", TRUE,
                                                                             NULL);
-          write_pack_out = (GOutputStream*)compressed_object_output;
+          write_object_out = (GOutputStream*)compressed_object_output;
         }
       else
-         write_pack_out = (GOutputStream*)pack_out;
+        {
+          write_object_out = (GOutputStream*)object_data_stream;
+        }
 
       obj_bytes_written = 0;
       do
@@ -367,7 +366,7 @@ create_pack_file (OtRepackData        *data,
           g_checksum_update (pack_checksum, (guint8*)buf, bytes_read);
           if (bytes_read > 0)
             {
-              if (!g_output_stream_write_all (write_pack_out, buf, bytes_read, &bytes_written, cancellable, error))
+              if (!g_output_stream_write_all (write_object_out, buf, bytes_read, &bytes_written, cancellable, error))
                 goto out;
               offset += bytes_written;
               obj_bytes_written += bytes_written;
@@ -375,14 +374,24 @@ create_pack_file (OtRepackData        *data,
         }
       while (bytes_read > 0);
 
-      if (compressed_object_output)
-        {
-          if (!g_output_stream_flush ((GOutputStream*)compressed_object_output, cancellable, error))
-            goto out;
-        }
+      if (!g_output_stream_close (write_object_out, cancellable, error))
+        goto out;
 
       g_assert_cmpint (obj_bytes_written, ==, objsize);
 
+      ot_clear_gvariant (&packed_object);
+      packed_object = g_variant_new ("(uy ay@ay)", GUINT32_TO_BE ((guint32)objtype),
+                                     entry_flags,
+                                     g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
+                                                                g_memory_output_stream_get_data (object_data_stream),
+                                                                g_memory_output_stream_get_data_size (object_data_stream),
+                                                                1),
+                                     ostree_checksum_to_bytes (checksum));
+      g_clear_object (&object_data_stream);
+
+      if (!write_variant_with_size (pack_out, packed_object, pack_checksum,
+                                    &offset, cancellable, error))
+        goto out;
     }
   
   if (!g_output_stream_close (pack_out, cancellable, error))
@@ -414,8 +423,8 @@ create_pack_file (OtRepackData        *data,
       GVariant *index_item = index_content_list->pdata[i];
       g_variant_builder_add_value (&index_content_builder, index_item);
     }
-  index_content = g_variant_new ("(su a{sv}@a(sut))",
-                                 "OSTPACKINDEX", GUINT32_TO_BE(0),
+  index_content = g_variant_new ("(s a{sv}@a(uayt))",
+                                 "OSTv0PACKINDEX",
                                  g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0),
                                  g_variant_builder_end (&index_content_builder));
 
@@ -460,7 +469,6 @@ create_pack_file (OtRepackData        *data,
   g_clear_object (&compressor);
   g_clear_object (&compressed_object_output);
   g_clear_object (&object_file_info);
-  ot_clear_gvariant (&object_header);
   if (pack_checksum)
     g_checksum_free (pack_checksum);
   g_clear_object (&pack_dir);
@@ -544,7 +552,7 @@ cluster_objects_stupidly (OtRepackData      *data,
 
       g_variant_get_child (objdata, 2, "t", &objsize);
 
-      if (current_size + objsize > data->pack_size)
+      if (current_size + objsize > data->pack_size || i == (object_list->len - 1))
         {
           guint j;
           GPtrArray *current = g_ptr_array_new ();
@@ -708,7 +716,6 @@ do_stats_gather_loose (OtRepackData  *data,
         {
           GVariant *copy = g_variant_ref (serialized_key);
           g_hash_table_replace (ret_loose_and_packed, copy, copy);
-          n_loose++;
           n_loose_and_packed++;
         }
       else if (is_loose)
@@ -793,6 +800,13 @@ ostree_builtin_repack (int argc, char **argv, GFile *repo_path, GError **error)
   if (!ostree_repo_check (repo, error))
     goto out;
 
+  if (ostree_repo_get_mode (repo) != OSTREE_REPO_MODE_ARCHIVE)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Can't repack bare repositories yet");
+      goto out;
+    }
+
   data.repo = repo;
   data.error = error;
 
diff --git a/tests/t0000-basic.sh b/tests/t0000-basic.sh
index df27bfc..2d55e20 100755
--- a/tests/t0000-basic.sh
+++ b/tests/t0000-basic.sh
@@ -19,7 +19,7 @@
 
 set -e
 
-echo "1..31"
+echo "1..28"
 
 . libtest.sh
 
@@ -206,14 +206,3 @@ cmp union-files-count{,.new}
 cd checkout-test2-union
 assert_file_has_content ./yet/another/tree/green "leaf"
 echo "ok checkout union 1"
-
-cd ${test_tmpdir}
-$OSTREE repack
-echo "ok repack"
-
-cd ${test_tmpdir}
-$OSTREE repack
-echo "ok repack again"
-
-$OSTREE checkout test2 checkout-test2-from-packed
-echo "ok checkout union 1"
diff --git a/tests/t0001-archive.sh b/tests/t0001-archive.sh
index 6bdc9aa..b07b988 100755
--- a/tests/t0001-archive.sh
+++ b/tests/t0001-archive.sh
@@ -21,7 +21,7 @@ set -e
 
 . libtest.sh
 
-echo '1..10'
+echo '1..13'
 
 setup_test_repository "archive"
 echo "ok setup"
@@ -67,3 +67,14 @@ cd ${test_tmpdir}
 $OSTREE cat test2 /baz/cow > cow-contents
 assert_file_has_content cow-contents "moo"
 echo "ok cat-file"
+
+cd ${test_tmpdir}
+$OSTREE repack
+echo "ok repack"
+
+cd ${test_tmpdir}
+$OSTREE repack
+echo "ok repack again"
+
+$OSTREE checkout test2 checkout-test2-from-packed
+echo "ok checkout union 1"



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