[ostree] core: Add internal GFile implementation for reading commits



commit 8b43c539cf54a20fceeed94bad63b32d659991c4
Author: Colin Walters <walters verbum org>
Date:   Mon Nov 7 11:25:49 2011 -0500

    core: Add internal GFile implementation for reading commits
    
    The data structures we use for reading already-written commits versus
    building them don't need to be the same.  It's cleaner if we can have
    generic code which operates on a GFile implementation for reads,
    because then we can share more generic code for walking and operating
    on filesystem trees via GIO.

 Makefile-libostree.am                   |    4 +
 libostree/ostree-core.c                 |    4 +-
 libostree/ostree-core.h                 |    5 +-
 libostree/ostree-repo-file-enumerator.c |  142 +++
 libostree/ostree-repo-file-enumerator.h |   54 ++
 libostree/ostree-repo-file.c            | 1427 +++++++++++++++++++++++++++++++
 libostree/ostree-repo-file.h            |  114 +++
 libostree/ostree-repo.c                 |  280 ++-----
 libostree/ostree-repo.h                 |    8 +-
 ostree/ot-builtin-checkout.c            |    2 +-
 ostree/ot-builtin-compose.c             |    2 +-
 11 files changed, 1839 insertions(+), 203 deletions(-)
---
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index 7b8790b..f1357cc 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -25,6 +25,10 @@ libostree_la_SOURCES = libostree/ostree.h \
 	libostree/ostree-core.h \
 	libostree/ostree-repo.c \
 	libostree/ostree-repo.h \
+	libostree/ostree-repo-file.c \
+	libostree/ostree-repo-file.h \
+	libostree/ostree-repo-file-enumerator.c \
+	libostree/ostree-repo-file-enumerator.h \
 	libostree/ostree-checkout.c \
 	libostree/ostree-checkout.h \
 	$(NULL)
diff --git a/libostree/ostree-core.c b/libostree/ostree-core.c
index a3fc34c..9a05e91 100644
--- a/libostree/ostree-core.c
+++ b/libostree/ostree-core.c
@@ -296,7 +296,7 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
 }
 
 gboolean
-ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
+ostree_set_xattrs (const char *path, GVariant *xattrs, GCancellable *cancellable, GError **error)
 {
   gboolean ret = FALSE;
   int i, n;
@@ -790,7 +790,7 @@ unpack_file (const char   *path,
         }
     }
 
-  if (!ostree_set_xattrs (dest_path, xattrs, error))
+  if (!ostree_set_xattrs (dest_path, xattrs, NULL, error))
     goto out;
 
   if (ret_checksum)
diff --git a/libostree/ostree-core.h b/libostree/ostree-core.h
index 3dc70b4..2b39f3f 100644
--- a/libostree/ostree-core.h
+++ b/libostree/ostree-core.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
 
 #define OSTREE_MAX_METADATA_SIZE (1 << 26)
 
-#define OSTREE_GIO_FAST_QUERYINFO "standard::name,standard::type,standard::is-symlink,standard::symlink-target,unix::*"
+#define OSTREE_GIO_FAST_QUERYINFO "standard::name,standard::type,standard::is-symlink,standard::symlink-target,standard::is-hidden,unix::*"
 
 #define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
 
@@ -98,7 +98,8 @@ char *ostree_get_relative_object_path (const char *checksum,
 GVariant *ostree_get_xattrs_for_path (const char   *path,
                                       GError     **error);
 
-gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs,
+                            GCancellable *cancellable, GError **error);
 
 gboolean ostree_parse_metadata_file (const char                  *path,
                                      OstreeSerializedVariantType *out_type,
diff --git a/libostree/ostree-repo-file-enumerator.c b/libostree/ostree-repo-file-enumerator.c
new file mode 100644
index 0000000..3a40281
--- /dev/null
+++ b/libostree/ostree-repo-file-enumerator.c
@@ -0,0 +1,142 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+#include <string.h>
+
+struct _OstreeRepoFileEnumerator
+{
+  GFileEnumerator parent;
+
+  OstreeRepoFile *dir;
+  char *attributes;
+  GFileQueryInfoFlags flags;
+
+  int index;
+};
+
+#define ostree_repo_file_enumerator_get_type _ostree_repo_file_enumerator_get_type
+G_DEFINE_TYPE (OstreeRepoFileEnumerator, ostree_repo_file_enumerator, G_TYPE_FILE_ENUMERATOR);
+
+static GFileInfo *ostree_repo_file_enumerator_next_file (GFileEnumerator  *enumerator,
+						     GCancellable     *cancellable,
+						     GError          **error);
+static gboolean   ostree_repo_file_enumerator_close     (GFileEnumerator  *enumerator,
+						     GCancellable     *cancellable,
+						     GError          **error);
+
+
+static void
+ostree_repo_file_enumerator_dispose (GObject *object)
+{
+  OstreeRepoFileEnumerator *self;
+
+  self = OSTREE_REPO_FILE_ENUMERATOR (object);
+
+  g_clear_object (&self->dir);
+  g_free (self->attributes);
+  
+  if (G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose)
+    G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_enumerator_finalize (GObject *object)
+{
+  OstreeRepoFileEnumerator *self;
+
+  self = OSTREE_REPO_FILE_ENUMERATOR (object);
+  (void)self;
+
+  G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->finalize (object);
+}
+
+
+static void
+ostree_repo_file_enumerator_class_init (OstreeRepoFileEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
+  
+  gobject_class->finalize = ostree_repo_file_enumerator_finalize;
+  gobject_class->dispose = ostree_repo_file_enumerator_dispose;
+
+  enumerator_class->next_file = ostree_repo_file_enumerator_next_file;
+  enumerator_class->close_fn = ostree_repo_file_enumerator_close;
+}
+
+static void
+ostree_repo_file_enumerator_init (OstreeRepoFileEnumerator *self)
+{
+}
+
+GFileEnumerator *
+_ostree_repo_file_enumerator_new (OstreeRepoFile       *dir,
+				  const char           *attributes,
+				  GFileQueryInfoFlags   flags,
+				  GCancellable         *cancellable,
+				  GError              **error)
+{
+  OstreeRepoFileEnumerator *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE_ENUMERATOR,
+		       "container", dir,
+		       NULL);
+
+  self->dir = g_object_ref (dir);
+  self->attributes = g_strdup (attributes);
+  self->flags = flags;
+  
+  return G_FILE_ENUMERATOR (self);
+}
+
+static GFileInfo *
+ostree_repo_file_enumerator_next_file (GFileEnumerator  *enumerator,
+				       GCancellable     *cancellable,
+				       GError          **error)
+{
+  OstreeRepoFileEnumerator *self = OSTREE_REPO_FILE_ENUMERATOR (enumerator);
+  gboolean ret = FALSE;
+  GFileInfo *info = NULL;
+
+  if (!_ostree_repo_file_tree_query_child (self->dir, self->index,
+                                           self->attributes, self->flags,
+                                           &info, cancellable, error))
+    goto out;
+
+  self->index++;
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    g_clear_object (&info);
+  return info;
+}
+
+static gboolean
+ostree_repo_file_enumerator_close (GFileEnumerator  *enumerator,
+				   GCancellable     *cancellable,
+				   GError          **error)
+{
+  return TRUE;
+}
diff --git a/libostree/ostree-repo-file-enumerator.h b/libostree/ostree-repo-file-enumerator.h
new file mode 100644
index 0000000..04c8e21
--- /dev/null
+++ b/libostree/ostree-repo-file-enumerator.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_REPO_FILE_ENUMERATOR
+#define _OSTREE_REPO_FILE_ENUMERATOR
+
+#include "ostree-repo-file.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE_ENUMERATOR         (_ostree_repo_file_enumerator_get_type ())
+#define OSTREE_REPO_FILE_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumerator))
+#define OSTREE_REPO_FILE_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_REPO_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+
+typedef struct _OstreeRepoFileEnumerator        OstreeRepoFileEnumerator;
+typedef struct _OstreeRepoFileEnumeratorClass   OstreeRepoFileEnumeratorClass;
+
+struct _OstreeRepoFileEnumeratorClass
+{
+  GFileEnumeratorClass parent_class;
+};
+
+GType   _ostree_repo_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileEnumerator * _ostree_repo_file_enumerator_new      (OstreeRepoFile       *dir,
+							 const char           *attributes,
+							 GFileQueryInfoFlags   flags,
+							 GCancellable         *cancellable,
+							 GError              **error);
+
+G_END_DECLS
+
+#endif
diff --git a/libostree/ostree-repo-file.c b/libostree/ostree-repo-file.c
new file mode 100644
index 0000000..6626872
--- /dev/null
+++ b/libostree/ostree-repo-file.c
@@ -0,0 +1,1427 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+
+static void ostree_repo_file_file_iface_init (GFileIface *iface);
+
+static void
+tree_replace_contents (OstreeRepoFile  *self,
+                       GVariant        *new_files,
+                       GVariant        *new_dirs);
+
+struct _OstreeRepoFile
+{
+  GObject parent_instance;
+
+  OstreeRepo *repo;
+
+  char *commit;
+  GError *commit_resolve_error;
+  
+  OstreeRepoFile *parent;
+  int index;
+  char *name;
+
+  char *tree_contents_checksum;
+  GVariant *tree_contents;
+  char *tree_metadata_checksum;
+  GVariant *tree_metadata;
+};
+
+#define ostree_repo_file_get_type _ostree_repo_file_get_type
+G_DEFINE_TYPE_WITH_CODE (OstreeRepoFile, ostree_repo_file, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+						ostree_repo_file_file_iface_init))
+
+static void
+ostree_repo_file_finalize (GObject *object)
+{
+  OstreeRepoFile *self;
+
+  self = OSTREE_REPO_FILE (object);
+
+  if (self->tree_contents)
+    g_variant_unref (self->tree_contents);
+  if (self->tree_metadata)
+    g_variant_unref (self->tree_metadata);
+  g_free (self->tree_contents_checksum);
+  g_free (self->tree_metadata_checksum);
+  g_free (self->commit);
+  g_free (self->name);
+
+  G_OBJECT_CLASS (ostree_repo_file_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_file_dispose (GObject *object)
+{
+  OstreeRepoFile *self;
+
+  self = OSTREE_REPO_FILE (object);
+
+  g_clear_object (&self->repo);
+  g_clear_object (&self->parent);
+
+  if (G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose)
+    G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_class_init (OstreeRepoFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = ostree_repo_file_finalize;
+  gobject_class->dispose = ostree_repo_file_dispose;
+}
+
+static void
+ostree_repo_file_init (OstreeRepoFile *self)
+{
+  self->index = -1;
+}
+
+static gboolean
+set_error_noent (GFile *self, GError **error)
+{
+  char *path = g_file_get_path (self);
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+               "No such file or directory: %s", path);
+  g_free (path);
+  return FALSE;
+}
+
+GFile * 
+_ostree_repo_file_new_root (OstreeRepo  *repo,
+                            const char  *commit)
+{
+  OstreeRepoFile *self;
+
+  g_return_val_if_fail (repo != NULL, NULL);
+  g_return_val_if_fail (commit != NULL, NULL);
+  g_return_val_if_fail (strlen (commit) == 64, NULL);
+
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (repo);
+  self->commit = g_strdup (commit);
+
+  return G_FILE (self);
+}
+
+
+GFile *
+_ostree_repo_file_new_child (OstreeRepoFile *parent,
+                             const char  *name)
+{
+  OstreeRepoFile *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (parent->repo);
+  self->parent = g_object_ref (parent);
+  self->name = g_strdup (name);
+
+  return G_FILE (self);
+}
+
+OstreeRepoFile *
+_ostree_repo_file_new_empty_tree (OstreeRepo  *repo)
+{
+  OstreeRepoFile *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (repo);
+
+  tree_replace_contents (self, NULL, NULL);
+
+  return self;
+}
+
+static gboolean
+do_resolve_commit (OstreeRepoFile  *self,
+                   GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  GVariant *root_contents = NULL;
+  GVariant *root_metadata = NULL;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+
+  g_assert (self->parent == NULL);
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                                         self->commit, &commit, error))
+    goto out;
+
+  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+  g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+  g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+                                         tree_contents_checksum, &root_contents,
+					 error))
+    goto out;
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                         tree_meta_checksum, &root_metadata,
+					 error))
+    goto out;
+  
+  self->tree_metadata = root_metadata;
+  root_metadata = NULL;
+  self->tree_contents = root_contents;
+  root_contents = NULL;
+
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_contents)
+    g_variant_unref (root_contents);
+  return ret;
+}
+
+static gboolean
+do_resolve_nonroot (OstreeRepoFile     *self,
+                    GError            **error)
+{
+  gboolean ret = FALSE;
+  GVariant *container = NULL;
+  GVariant *tree_contents = NULL;
+  GVariant *tree_metadata = NULL;
+  gboolean is_dir;
+  int i;
+
+  i = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, &container);
+  
+  if (i < 0)
+    {
+      set_error_noent ((GFile*)self, error);
+      goto out;
+    }
+
+  if (is_dir)
+    {
+      const char *name;
+      const char *content_checksum;
+      const char *metadata_checksum;
+      GVariant *files_variant;
+
+      files_variant = g_variant_get_child_value (self->parent->tree_contents, 2);
+      self->index = g_variant_n_children (files_variant) + i;
+      g_variant_unref (files_variant);
+
+      g_variant_get_child (container, i, "(&s&s&s)",
+                           &name, &content_checksum, &metadata_checksum);
+          
+      if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+                                             content_checksum, &tree_contents,
+                                             error))
+        goto out;
+          
+      if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                             metadata_checksum, &tree_metadata,
+                                             error))
+        goto out;
+
+      self->tree_contents = tree_contents;
+      tree_contents = NULL;
+      self->tree_metadata = tree_metadata;
+      tree_metadata = NULL;
+    }
+  else
+    self->index = i;
+
+  ret = TRUE;
+ out:
+  if (container)
+    g_variant_unref (container);
+  if (tree_metadata)
+    g_variant_unref (tree_metadata);
+  if (tree_contents)
+    g_variant_unref (tree_contents);
+  return ret;
+}
+
+gboolean
+_ostree_repo_file_ensure_resolved (OstreeRepoFile  *self,
+                                   GError         **error)
+{
+  if (self->commit_resolve_error != NULL)
+    goto out;
+
+  if (self->parent == NULL)
+    {
+      if (self->tree_contents == NULL)
+        (void)do_resolve_commit (self, &(self->commit_resolve_error));
+    }
+  else if (self->index == -1)
+    {
+      (void)do_resolve_nonroot (self, &(self->commit_resolve_error));
+    }
+  
+ out:
+  if (self->commit_resolve_error)
+    {
+      if (error)
+	*error = g_error_copy (self->commit_resolve_error);
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+gboolean
+_ostree_repo_file_get_xattrs (OstreeRepoFile  *self,
+                              GVariant       **out_xattrs,
+                              GCancellable    *cancellable,
+                              GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *ret_xattrs = NULL;
+  GVariant *metadata = NULL;
+  GInputStream *input = NULL;
+  GFile *local_file = NULL;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  if (self->tree_metadata)
+    ret_xattrs = g_variant_get_child_value (self->tree_metadata, 4);
+  else if (ostree_repo_is_archive (self->repo))
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+        goto out;
+      ret_xattrs = g_variant_get_child_value (metadata, 4);
+    }
+  else
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      ret_xattrs = ostree_get_xattrs_for_path (ot_gfile_get_path_cached (local_file), error);
+    }
+
+  ret = TRUE;
+  *out_xattrs = ret_xattrs;
+  ret_xattrs = NULL;
+ out:
+  if (ret_xattrs)
+    g_variant_unref (ret_xattrs);
+  if (metadata)
+    g_variant_unref (metadata);
+  g_clear_object (&input);
+  g_clear_object (&local_file);
+  return ret;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_contents (OstreeRepoFile  *self)
+{
+  return self->tree_contents;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_metadata (OstreeRepoFile  *self)
+{
+  return self->tree_metadata;
+}
+
+void
+_ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+                                     const char     *checksum,
+                                     GVariant       *metadata)
+{
+  if (self->tree_metadata)
+    g_variant_unref (self->tree_metadata);
+  self->tree_metadata = g_variant_ref (metadata);
+  g_free (self->tree_metadata_checksum);
+  self->tree_metadata_checksum = g_strdup (checksum);
+}
+
+void
+_ostree_repo_file_make_empty_tree (OstreeRepoFile  *self)
+{
+  tree_replace_contents (self, NULL, NULL);
+}
+
+void
+_ostree_repo_file_tree_set_content_checksum (OstreeRepoFile  *self,
+                                             const char      *checksum)
+{
+  g_assert (self->parent == NULL);
+  g_free (self->tree_contents_checksum);
+  self->tree_contents_checksum = g_strdup (checksum);
+}
+
+const char *
+_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile  *self)
+{
+  g_assert (self->parent == NULL);
+  return self->tree_contents_checksum;
+}
+
+GFile *
+_ostree_repo_file_nontree_get_local (OstreeRepoFile  *self)
+{
+  const char *checksum;
+  char *path;
+  GFile *ret;
+
+  g_assert (!ostree_repo_is_archive (self->repo));
+
+  checksum = _ostree_repo_file_nontree_get_checksum (self);
+  path = ostree_repo_get_object_path (self->repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+  ret = ot_util_new_file_for_path (path);
+  g_free (path);
+  
+  return ret;
+}
+
+OstreeRepo *
+_ostree_repo_file_get_repo (OstreeRepoFile  *self)
+{
+  return self->repo;
+}
+
+OstreeRepoFile *
+_ostree_repo_file_get_root (OstreeRepoFile  *self)
+{
+  OstreeRepoFile *parent = self;
+
+  while (parent->parent)
+    parent = parent->parent;
+  return parent;
+}
+
+const char *
+_ostree_repo_file_nontree_get_checksum (OstreeRepoFile  *self)
+{
+  int n;
+  gboolean is_dir;
+
+  g_assert (self->parent);
+
+  n = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, NULL);
+  g_assert (n >= 0 && !is_dir);
+  
+  return _ostree_repo_file_tree_get_child_checksum (self->parent, n);
+}
+
+const char *
+_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile  *self,
+                                           int n)
+{
+  GVariant *files_variant;
+  const char *checksum;
+
+  g_assert (self->tree_contents);
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+  g_variant_get_child (files_variant, n, "(@s&s)", NULL, &checksum);
+
+  g_variant_unref (files_variant);
+
+  return checksum;
+}
+
+static gboolean
+ostree_repo_file_is_native (GFile *file)
+{
+  return FALSE;
+}
+
+static gboolean
+ostree_repo_file_has_uri_scheme (GFile      *file,
+				 const char *uri_scheme)
+{
+  return g_ascii_strcasecmp (uri_scheme, "ostree") == 0;
+}
+
+static char *
+ostree_repo_file_get_uri_scheme (GFile *file)
+{
+  return g_strdup ("ostree");
+}
+
+static char *
+ostree_repo_file_get_basename (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  return g_strdup (self->name);
+}
+
+static char *
+ostree_repo_file_get_path (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  OstreeRepoFile *parent;
+  GString *buf;
+  GSList *parents;
+  GSList *iter;
+
+  buf = g_string_new ("");
+  parents = NULL;
+
+  for (parent = self->parent; parent; parent = parent->parent)
+    parents = g_slist_prepend (parents, parent);
+
+  if (parents->next)
+    {
+      for (iter = parents->next; iter; iter = iter->next)
+        {
+          parent = iter->data;
+          g_string_append_c (buf, '/');
+          g_string_append (buf, parent->name);
+        }
+    }
+  g_string_append_c (buf, '/');
+  g_string_append (buf, self->name);
+
+  g_slist_free (parents);
+
+  return g_string_free (buf, FALSE);
+}
+
+static char *
+ostree_repo_file_get_uri (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  char *path;
+  char *uri_path;
+  char *ret;
+
+  path = g_file_get_path (file);
+  uri_path = g_filename_to_uri (path, NULL, NULL);
+  g_free (path);
+  g_assert (g_str_has_prefix (uri_path, "file://"));
+  ret = g_strconcat ("ostree://", self->commit, uri_path+strlen("file://"), NULL);
+  g_free (uri_path);
+
+  return ret;
+}
+
+static char *
+ostree_repo_file_get_parse_name (GFile *file)
+{
+  return ostree_repo_file_get_uri (file);
+}
+
+static GFile *
+ostree_repo_file_get_parent (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  return g_object_ref (self->parent);
+}
+
+static GFile *
+ostree_repo_file_dup (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  if (self->parent)
+    return _ostree_repo_file_new_child (self->parent, self->name);
+  else
+    return _ostree_repo_file_new_root (self->repo, self->commit);
+}
+
+static guint
+ostree_repo_file_hash (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  
+  if (self->parent)
+    return g_file_hash (self->parent) + g_str_hash (self->name);
+  else
+    return g_str_hash (self->commit);
+}
+
+static gboolean
+ostree_repo_file_equal (GFile *file1,
+                        GFile *file2)
+{
+  OstreeRepoFile *self1 = OSTREE_REPO_FILE (file1);
+  OstreeRepoFile *self2 = OSTREE_REPO_FILE (file2);
+
+  if (self1->parent && self2->parent)
+    {
+      return g_str_equal (self1->name, self2->name)
+        && g_file_equal ((GFile*)self1->parent, (GFile*)self2->parent);
+    }
+  else if (!self1->parent && !self2->parent)
+    {
+      return g_str_equal (self1->commit, self2->commit);
+    }
+  else
+    return FALSE;
+}
+
+static const char *
+match_prefix (const char *path, 
+              const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+  
+  /* Handle the case where prefix is the root, so that
+   * the IS_DIR_SEPRARATOR check below works */
+  if (prefix_len > 0 &&
+      G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
+    prefix_len--;
+  
+  return path + prefix_len;
+}
+
+static gboolean
+ostree_repo_file_prefix_matches (GFile *parent,
+				 GFile *descendant)
+{
+  const char *remainder;
+  char *parent_path;
+  char *descendant_path;
+
+  parent_path = g_file_get_path (parent);
+  descendant_path = g_file_get_path (descendant);
+  remainder = match_prefix (descendant_path, parent_path);
+  g_free (parent_path);
+  g_free (descendant_path);
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return TRUE;
+  return FALSE;
+}
+
+static char *
+ostree_repo_file_get_relative_path (GFile *parent,
+				    GFile *descendant)
+{
+  const char *remainder;
+  char *parent_path;
+  char *descendant_path;
+
+  parent_path = g_file_get_path (parent);
+  descendant_path = g_file_get_path (descendant);
+  remainder = match_prefix (descendant_path, parent_path);
+  g_free (parent_path);
+  g_free (descendant_path);
+  
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return g_strdup (remainder + 1);
+  return NULL;
+}
+
+static GFile *
+ostree_repo_file_resolve_relative_path (GFile      *file,
+					const char *relative_path)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  OstreeRepoFile *parent;
+  char *filename;
+  const char *rest;
+  GFile *ret;
+
+  if (g_path_is_absolute (relative_path) && self->parent)
+    {
+      g_assert (*relative_path == '/');
+      return ostree_repo_file_resolve_relative_path ((GFile*)_ostree_repo_file_get_root (self),
+                                                     relative_path+1);
+    }
+
+  rest = strchr (relative_path, '/');
+  if (rest)
+    {
+      rest += 1;
+      filename = g_strndup (relative_path, rest - relative_path);
+    }
+  else
+    filename = g_strdup (relative_path);
+
+  parent = (OstreeRepoFile*)_ostree_repo_file_new_child (self, filename);
+  g_free (filename);
+    
+  if (!rest)
+    ret = (GFile*)parent;
+  else
+    {
+      ret = ostree_repo_file_resolve_relative_path ((GFile*)parent, rest);
+      g_clear_object (&parent);
+    }
+  return ret;
+}
+
+static GFileEnumerator *
+ostree_repo_file_enumerate_children (GFile                *file,
+				     const char           *attributes,
+				     GFileQueryInfoFlags   flags,
+				     GCancellable         *cancellable,
+				     GError              **error)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  return _ostree_repo_file_enumerator_new (self,
+					   attributes, flags,
+					   cancellable, error);
+}
+
+static GFile *
+ostree_repo_file_get_child_for_display_name (GFile        *file,
+					 const char   *display_name,
+					 GError      **error)
+{
+  return g_file_get_child (file, display_name);
+}
+
+static GFile *
+get_child_local_file (OstreeRepo   *repo,
+                      const char   *checksum)
+{
+  char *path;
+  GFile *ret;
+
+  path = ostree_repo_get_object_path (repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+  ret = ot_util_new_file_for_path (path);
+  g_free (path);
+  
+  return ret;
+}
+
+static gboolean
+query_child_info_file_nonarchive (OstreeRepo       *repo,
+                                  const char       *checksum,
+                                  GFileAttributeMatcher *matcher,
+                                  GFileInfo        *info,
+                                  GCancellable     *cancellable,
+                                  GError          **error)
+{
+  gboolean ret = FALSE;
+  GFileInfo *local_info = NULL;
+  GFile *local_file = NULL;
+  int i ;
+  const char *mapped_boolean[] = {
+    "standard::is-symlink"
+  };
+  const char *mapped_string[] = {
+  };
+  const char *mapped_byte_string[] = {
+    "standard::symlink-target"
+  };
+  const char *mapped_uint32[] = {
+    "standard::type",
+    "unix::device",
+    "unix::mode",
+    "unix::nlink",
+    "unix::uid",
+    "unix::gid",
+    "unix::rdev"
+  };
+  const char *mapped_uint64[] = {
+    "standard::size",
+    "standard::allocated-size",
+    "unix::inode"
+  };
+
+  if (!(g_file_attribute_matcher_matches (matcher, "unix::mode")
+        || g_file_attribute_matcher_matches (matcher, "standard::type")))
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  local_file = get_child_local_file (repo, checksum);
+  local_info = g_file_query_info (local_file,
+				  OSTREE_GIO_FAST_QUERYINFO,
+				  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+				  cancellable,
+				  error);
+  if (!local_info)
+    goto out;
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_boolean); i++)
+    g_file_info_set_attribute_boolean (info, mapped_boolean[i], g_file_info_get_attribute_boolean (local_info, mapped_boolean[i]));
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_string); i++)
+    {
+      const char *string = g_file_info_get_attribute_string (local_info, mapped_string[i]);
+      if (string)
+        g_file_info_set_attribute_string (info, mapped_string[i], string);
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_byte_string); i++)
+    {
+      const char *byte_string = g_file_info_get_attribute_byte_string (local_info, mapped_byte_string[i]);
+      if (byte_string)
+        g_file_info_set_attribute_byte_string (info, mapped_byte_string[i], byte_string);
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_uint32); i++)
+    g_file_info_set_attribute_uint32 (info, mapped_uint32[i], g_file_info_get_attribute_uint32 (local_info, mapped_uint32[i]));
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_uint64); i++)
+    g_file_info_set_attribute_uint64 (info, mapped_uint64[i], g_file_info_get_attribute_uint64 (local_info, mapped_uint64[i]));
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&local_info);
+  g_clear_object (&local_file);
+  return ret;
+}
+
+static gboolean
+query_child_info_file_archive (OstreeRepo       *repo,
+                               const char       *checksum,
+                               GFileAttributeMatcher *matcher,
+                               GFileInfo        *info,
+                               GCancellable     *cancellable,
+                               GError          **error)
+{
+  gboolean ret = FALSE;
+  GFile *local_file = NULL;
+  GVariant *metadata = NULL;
+  GInputStream *input = NULL;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  guint32 file_type;
+  gsize bytes_read;
+  char *buf = NULL;
+
+  local_file = get_child_local_file (repo, checksum);
+
+  if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+    goto out;
+
+  g_variant_get (metadata, "(uuuu a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 NULL, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  g_file_info_set_attribute_boolean (info, "standard::is-symlink",
+                                     S_ISLNK (mode));
+  if (S_ISLNK (mode))
+    file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+  else if (S_ISREG (mode))
+    file_type = G_FILE_TYPE_REGULAR;
+  else if (S_ISBLK (mode) || S_ISCHR(mode))
+    file_type = G_FILE_TYPE_SPECIAL;
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile %s: Invalid mode", checksum);
+      goto out;
+    }
+  g_file_info_set_attribute_uint32 (info, "standard::type", file_type);
+
+  g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+  g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+  g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+
+  if (file_type == G_FILE_TYPE_REGULAR)
+    {
+      g_file_info_set_attribute_uint64 (info, "standard::size", content_len);
+    }
+  else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
+    {
+      gsize len = MIN (PATH_MAX, content_len) + 1;
+      buf = g_malloc (len);
+
+      if (!g_input_stream_read_all (input, buf, len, &bytes_read, cancellable, error))
+        goto out;
+      buf[bytes_read] = '\0';
+
+      g_file_info_set_attribute_byte_string (info, "standard::symlink-target", buf);
+    }
+  else if (file_type == G_FILE_TYPE_SPECIAL)
+    {
+      guint32 device;
+
+      if (!g_input_stream_read_all (input, &device, 4, &bytes_read, cancellable, error))
+        goto out;
+
+      device = GUINT32_FROM_BE (device);
+      g_file_info_set_attribute_uint32 (info, "unix::device", device);
+    }
+
+  ret = TRUE;
+ out:
+  g_free (buf);
+  if (metadata)
+    g_variant_unref (metadata);
+  g_clear_object (&local_file);
+  g_clear_object (&input);
+  return ret;
+}
+
+static void
+set_info_from_dirmeta (GFileInfo  *info,
+                       GVariant   *metadata)
+{
+  guint32 version, uid, gid, mode;
+
+  g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_DIRECTORY);
+
+  /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+  g_variant_get (metadata, "(uuuu a(ayay))",
+                 &version, &uid, &gid, &mode,
+                 NULL);
+  version = GUINT32_FROM_BE (version);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+
+  g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+  g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+  g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+}
+
+static gboolean
+query_child_info_dir (OstreeRepo         *repo,
+                      const char         *metadata_checksum,
+                      GFileAttributeMatcher *matcher,
+                      GFileQueryInfoFlags flags,
+                      GFileInfo        *info,
+                      GCancellable    *cancellable,
+                      GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *metadata = NULL;
+
+  if (!g_file_attribute_matcher_matches (matcher, "unix::mode"))
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                         metadata_checksum, &metadata, error))
+    goto out;
+
+  set_info_from_dirmeta (info, metadata);
+  
+  ret = TRUE;
+ out:
+  if (metadata)
+    g_variant_unref (metadata);
+  return ret;
+}
+
+static gboolean
+bsearch_in_file_variant (GVariant  *variant,
+                         const char *name,
+                         int        *out_pos)
+{
+  int i, n;
+  int m;
+
+  i = 0;
+  n = g_variant_n_children (variant) - 1;
+  m = 0;
+
+  while (i <= n)
+    {
+      GVariant *child;
+      const char *cur;
+      int cmp;
+
+      m = i + ((n - i) / 2);
+
+      child = g_variant_get_child_value (variant, m);
+      g_variant_get_child (child, 0, "&s", &cur, NULL);      
+
+      cmp = strcmp (cur, name);
+      if (cmp < 0)
+        i = m + 1;
+      else if (cmp > 0)
+        n = m - 1;
+      else
+        {
+          g_variant_unref (child);
+          *out_pos = m;
+          return TRUE;
+        }
+      g_variant_unref (child);
+    }
+
+  *out_pos = m;
+  return FALSE;
+}
+
+static GVariant *
+remove_variant_child (GVariant *variant,
+                      int       n)
+{
+  GVariantBuilder builder;
+  GVariantIter *iter;
+  int i;
+  GVariant *child;
+
+  g_variant_builder_init (&builder, g_variant_get_type (variant));
+  iter = g_variant_iter_new (variant);
+
+  i = 0;
+  while ((child = g_variant_iter_next_value (iter)) != NULL)
+    {
+      if (i != n)
+        g_variant_builder_add_value (&builder, child);
+      g_variant_unref (child);
+    }
+  g_variant_iter_free (iter);
+  
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+insert_variant_child (GVariant  *variant,
+                      int        n,
+                      GVariant  *item)
+{
+  GVariantBuilder builder;
+  GVariantIter *iter;
+  int i;
+  GVariant *child;
+
+  g_variant_builder_init (&builder, g_variant_get_type (variant));
+  iter = g_variant_iter_new (variant);
+
+  i = 0;
+  while ((child = g_variant_iter_next_value (iter)) != NULL)
+    {
+      if (i == n)
+        g_variant_builder_add_value (&builder, item);
+      g_variant_builder_add_value (&builder, child);
+      g_variant_unref (child);
+    }
+  g_variant_iter_free (iter);
+  
+  return g_variant_builder_end (&builder);
+}
+
+static void
+tree_replace_contents (OstreeRepoFile  *self,
+                       GVariant        *new_files,
+                       GVariant        *new_dirs)
+{
+  guint version;
+  GVariant *metadata;
+  GVariant *tmp_files = NULL;
+  GVariant *tmp_dirs = NULL;
+  
+  if (!(new_files || new_dirs) && self->tree_contents)
+    return;
+  else if (!self->tree_contents)
+    {
+      version = GUINT32_TO_BE (0);
+      metadata = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+      tmp_dirs = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+      tmp_files = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+    }
+  else
+    {
+      g_variant_get_child (self->tree_contents, 0, "u", &version);
+      metadata = g_variant_get_child_value (self->tree_contents, 1);
+      if (!new_files)
+        tmp_files = g_variant_get_child_value (self->tree_contents, 2);
+      if (!new_dirs)
+        tmp_dirs = g_variant_get_child_value (self->tree_contents, 3);
+    }
+
+  if (self->tree_contents)
+    g_variant_unref (self->tree_contents);
+  self->tree_contents = g_variant_new ("(u a{sv}@a(ss)@a(sss))", version, metadata,
+                                       new_files ? new_files : tmp_files,
+                                       new_dirs ? new_dirs : tmp_dirs);
+
+  g_variant_unref (metadata);
+  if (tmp_files)
+    g_variant_unref (tmp_files);
+  if (tmp_dirs)
+    g_variant_unref (tmp_dirs);
+}
+
+void
+_ostree_repo_file_tree_remove_child (OstreeRepoFile  *self,
+                                     const char      *name)
+{
+  int i;
+  GVariant *files_variant;
+  GVariant *new_files_variant = NULL;
+  GVariant *dirs_variant;
+  GVariant *new_dirs_variant = NULL;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  if (bsearch_in_file_variant (files_variant, name, &i))
+    {
+      new_files_variant = remove_variant_child (files_variant, i);
+    }
+  else
+    {
+      if (bsearch_in_file_variant (dirs_variant, name, &i))
+        {
+          new_dirs_variant = remove_variant_child (dirs_variant, i);
+        }
+    }
+
+  tree_replace_contents (self, new_files_variant, new_dirs_variant);
+
+  g_variant_unref (files_variant);
+  g_variant_unref (dirs_variant);
+}
+
+void
+_ostree_repo_file_tree_add_file (OstreeRepoFile  *self,
+                                 const char      *name,
+                                 const char      *checksum)
+{
+  int n;
+  GVariant *files_variant;
+  GVariant *new_files_variant;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+  if (!bsearch_in_file_variant (files_variant, name, &n))
+    {
+      new_files_variant = insert_variant_child (files_variant, n,
+                                                g_variant_new ("(ss)", name, checksum));
+      g_variant_ref_sink (new_files_variant);
+      tree_replace_contents (self, new_files_variant, NULL);
+      g_variant_unref (new_files_variant);
+    }
+  g_variant_unref (files_variant);
+}
+
+void
+_ostree_repo_file_tree_add_dir (OstreeRepoFile  *self,
+                                const char      *name,
+                                const char      *content_checksum,
+                                const char      *metadata_checksum)
+{
+  int n;
+  GVariant *dirs_variant;
+  GVariant *new_dirs_variant;
+
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  if (!bsearch_in_file_variant (dirs_variant, name, &n))
+    {
+      new_dirs_variant = insert_variant_child (dirs_variant, n,
+                                               g_variant_new ("(sss)", name, content_checksum,
+                                                              metadata_checksum));
+      g_variant_ref_sink (new_dirs_variant);
+      tree_replace_contents (self, NULL, new_dirs_variant);
+      g_variant_unref (new_dirs_variant);
+    }
+  g_variant_unref (dirs_variant);
+}
+
+int
+_ostree_repo_file_tree_find_child  (OstreeRepoFile  *self,
+                                    const char      *name,
+                                    gboolean        *is_dir,
+                                    GVariant       **out_container)
+{
+  int i;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  GVariant *ret_container = NULL;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  i = -1;
+  if (bsearch_in_file_variant (files_variant, name, &i))
+    {
+      *is_dir = FALSE;
+      ret_container = files_variant;
+      files_variant = NULL;
+    }
+  else
+    {
+      if (bsearch_in_file_variant (dirs_variant, name, &i))
+        {
+          *is_dir = TRUE;
+          ret_container = dirs_variant;
+          dirs_variant = NULL;
+        }
+      else
+        {
+          i = -1;
+        }
+    }
+  if (ret_container && out_container)
+    {
+      *out_container = ret_container;
+      ret_container = NULL;
+    }
+  if (ret_container)
+    g_variant_unref (ret_container);
+  if (files_variant)
+    g_variant_unref (files_variant);
+  if (dirs_variant)
+    g_variant_unref (dirs_variant);
+  return i;
+}
+
+gboolean
+_ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
+                                    int              n,
+                                    const char      *attributes,
+                                    GFileQueryInfoFlags flags,
+                                    GFileInfo      **out_info,
+                                    GCancellable    *cancellable,
+                                    GError         **error)
+{
+  const char *name = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *ret_info = NULL;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  GVariant *tree_child_metadata = NULL;
+  GFileAttributeMatcher *matcher = NULL;
+  int c;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  matcher = g_file_attribute_matcher_new (attributes);
+
+  ret_info = g_file_info_new ();
+
+  g_assert (self->tree_contents);
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  c = g_variant_n_children (files_variant);
+  if (n < c)
+    {
+      const char *checksum;
+
+      g_variant_get_child (files_variant, n, "(&s&s)", &name, &checksum);
+
+      if (ostree_repo_is_archive (self->repo))
+	{
+	  if (!query_child_info_file_archive (self->repo, checksum, matcher, ret_info,
+                                              cancellable, error))
+	    goto out;
+	}
+      else
+	{
+	  if (!query_child_info_file_nonarchive (self->repo, checksum, matcher, ret_info,
+                                                 cancellable, error))
+	    goto out;
+	}
+    }
+  else
+    {
+      const char *tree_checksum;
+      const char *meta_checksum;
+
+      n -= c;
+
+      c = g_variant_n_children (dirs_variant);
+
+      if (n < c)
+        {
+          g_variant_get_child (dirs_variant, n, "(&s&s&s)",
+                               &name, &tree_checksum, &meta_checksum);
+
+          if (!query_child_info_dir (self->repo, meta_checksum,
+                                     matcher, flags, ret_info,
+                                     cancellable, error))
+            goto out;
+        }
+      else
+        n -= c;
+    }
+
+  if (name)
+    {
+      g_file_info_set_attribute_byte_string (ret_info, "standard::name",
+                                             name);
+      g_file_info_set_attribute_string (ret_info, "standard::display-name",
+                                        name);
+      if (*name == '.')
+        g_file_info_set_is_hidden (ret_info, TRUE);
+    }
+  else
+    {
+      g_clear_object (&ret_info);
+    }
+
+  ret = TRUE;
+  *out_info = ret_info;
+  ret_info = NULL;
+ out:
+  g_clear_object (&ret_info);
+  if (matcher)
+    g_file_attribute_matcher_unref (matcher);
+  if (tree_child_metadata)
+    g_variant_unref (tree_child_metadata);
+  g_variant_unref (files_variant);
+  g_variant_unref (dirs_variant);
+  return ret;
+}
+
+static GFileInfo *
+ostree_repo_file_query_info (GFile                *file,
+			     const char           *attributes,
+			     GFileQueryInfoFlags   flags,
+			     GCancellable         *cancellable,
+			     GError              **error)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  gboolean ret = FALSE;
+  GFileInfo *info = NULL;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  if (!self->parent)
+    {
+      info = g_file_info_new ();
+      set_info_from_dirmeta (info, self->tree_metadata);
+    }
+  else
+    {
+      if (!_ostree_repo_file_tree_query_child (self->parent, self->index, 
+                                               attributes, flags, 
+                                               &info, cancellable, error))
+        goto out;
+    }
+      
+  ret = TRUE;
+ out:
+  if (!ret)
+    g_clear_object (&info);
+  return info;
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_settable_attributes (GFile         *file,
+					GCancellable  *cancellable,
+					GError       **error)
+{
+  return g_file_attribute_info_list_new ();
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_writable_namespaces (GFile         *file,
+					GCancellable  *cancellable,
+					GError       **error)
+{
+  return g_file_attribute_info_list_new ();
+}
+
+static GFileInputStream *
+ostree_repo_file_read (GFile         *file,
+		       GCancellable  *cancellable,
+		       GError       **error)
+{
+  gboolean ret = FALSE;
+  GFile *local_file = NULL;
+  GFileInputStream *ret_stream = NULL;
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  if (self->tree_contents)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+			   G_IO_ERROR_IS_DIRECTORY,
+			   "Can't open directory");
+      goto out;
+    }
+
+  if (ostree_repo_is_archive (self->repo))
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+			   G_IO_ERROR_NOT_SUPPORTED,
+			   "Can't open archived file (yet)");
+      goto out;
+    }
+  else
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      ret_stream = g_file_read (local_file, cancellable, error);
+      if (!ret_stream)
+	goto out;
+    }
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&local_file);
+  if (!ret)
+    g_clear_object (&ret_stream);
+  return ret_stream;
+}
+
+static void
+ostree_repo_file_file_iface_init (GFileIface *iface)
+{
+  iface->dup = ostree_repo_file_dup;
+  iface->hash = ostree_repo_file_hash;
+  iface->equal = ostree_repo_file_equal;
+  iface->is_native = ostree_repo_file_is_native;
+  iface->has_uri_scheme = ostree_repo_file_has_uri_scheme;
+  iface->get_uri_scheme = ostree_repo_file_get_uri_scheme;
+  iface->get_basename = ostree_repo_file_get_basename;
+  iface->get_path = ostree_repo_file_get_path;
+  iface->get_uri = ostree_repo_file_get_uri;
+  iface->get_parse_name = ostree_repo_file_get_parse_name;
+  iface->get_parent = ostree_repo_file_get_parent;
+  iface->prefix_matches = ostree_repo_file_prefix_matches;
+  iface->get_relative_path = ostree_repo_file_get_relative_path;
+  iface->resolve_relative_path = ostree_repo_file_resolve_relative_path;
+  iface->get_child_for_display_name = ostree_repo_file_get_child_for_display_name;
+  iface->set_display_name = NULL;
+  iface->enumerate_children = ostree_repo_file_enumerate_children;
+  iface->query_info = ostree_repo_file_query_info;
+  iface->query_filesystem_info = NULL;
+  iface->find_enclosing_mount = NULL;
+  iface->query_settable_attributes = ostree_repo_file_query_settable_attributes;
+  iface->query_writable_namespaces = ostree_repo_file_query_writable_namespaces;
+  iface->set_attribute = NULL;
+  iface->set_attributes_from_info = NULL;
+  iface->read_fn = ostree_repo_file_read;
+  iface->append_to = NULL;
+  iface->create = NULL;
+  iface->replace = NULL;
+  iface->open_readwrite = NULL;
+  iface->create_readwrite = NULL;
+  iface->replace_readwrite = NULL;
+  iface->delete_file = NULL;
+  iface->trash = NULL;
+  iface->make_directory = NULL;
+  iface->make_symbolic_link = NULL;
+  iface->copy = NULL;
+  iface->move = NULL;
+  iface->monitor_dir = NULL;
+  iface->monitor_file = NULL;
+
+  iface->supports_thread_contexts = TRUE;
+}
diff --git a/libostree/ostree-repo-file.h b/libostree/ostree-repo-file.h
new file mode 100644
index 0000000..4e16c71
--- /dev/null
+++ b/libostree/ostree-repo-file.h
@@ -0,0 +1,114 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_REPO_FILE
+#define _OSTREE_REPO_FILE
+
+#include "ostree-repo.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE         (_ostree_repo_file_get_type ())
+#define OSTREE_REPO_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFile))
+#define OSTREE_REPO_FILE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+#define OSTREE_IS_LOCAL_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_IS_LOCAL_FILE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_REPO_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+
+typedef struct _OstreeRepoFile        OstreeRepoFile;
+typedef struct _OstreeRepoFileClass   OstreeRepoFileClass;
+
+struct _OstreeRepoFileClass
+{
+  GObjectClass parent_class;
+};
+
+GType   _ostree_repo_file_get_type (void) G_GNUC_CONST;
+
+GFile * _ostree_repo_file_new_root (OstreeRepo  *repo,
+                                    const char  *commit);
+
+OstreeRepoFile * _ostree_repo_file_new_empty_tree (OstreeRepo  *repo);
+
+GFile * _ostree_repo_file_new_child (OstreeRepoFile *parent,
+                                     const char  *name);
+
+gboolean _ostree_repo_file_ensure_resolved (OstreeRepoFile  *self,
+                                            GError         **error);
+
+gboolean _ostree_repo_file_get_xattrs (OstreeRepoFile  *self,
+                                       GVariant       **out_xattrs,
+                                       GCancellable    *cancellable,
+                                       GError         **error);
+
+OstreeRepo * _ostree_repo_file_get_repo (OstreeRepoFile  *self);
+OstreeRepoFile * _ostree_repo_file_get_root (OstreeRepoFile  *self);
+
+void _ostree_repo_file_make_empty_tree (OstreeRepoFile  *self);
+
+void _ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+                                          const char     *checksum,
+                                          GVariant       *metadata);
+
+void _ostree_repo_file_tree_set_content_checksum (OstreeRepoFile  *self,
+                                                  const char      *checksum);
+const char *_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile  *self);
+
+gboolean _ostree_repo_file_is_tree (OstreeRepoFile  *self);
+
+const char * _ostree_repo_file_nontree_get_checksum (OstreeRepoFile  *self);
+
+GFile *_ostree_repo_file_nontree_get_local (OstreeRepoFile  *self);
+
+void    _ostree_repo_file_tree_remove_child (OstreeRepoFile  *self,
+                                             const char      *name);
+
+void    _ostree_repo_file_tree_add_file (OstreeRepoFile  *self,
+                                         const char      *name,
+                                         const char      *checksum);
+
+void    _ostree_repo_file_tree_add_dir (OstreeRepoFile  *self,
+                                        const char      *name,
+                                        const char      *content_checksum,
+                                        const char      *metadata_checksum);
+
+int     _ostree_repo_file_tree_find_child  (OstreeRepoFile  *self,
+                                            const char      *name,
+                                            gboolean        *is_dir,
+                                            GVariant       **out_container);
+
+const char *_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile  *self,
+                                                       int n);
+
+gboolean _ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
+                                             int              n,
+                                             const char      *attributes,
+                                             GFileQueryInfoFlags flags,
+                                             GFileInfo      **out_info,
+                                             GCancellable    *cancellable,
+                                             GError         **error);
+
+GVariant *_ostree_repo_file_tree_get_contents (OstreeRepoFile  *self);
+GVariant *_ostree_repo_file_tree_get_metadata (OstreeRepoFile  *self);
+
+G_END_DECLS
+
+#endif
diff --git a/libostree/ostree-repo.c b/libostree/ostree-repo.c
index 0f5fb88..1d9e853 100644
--- a/libostree/ostree-repo.c
+++ b/libostree/ostree-repo.c
@@ -25,6 +25,7 @@
 
 #include "ostree.h"
 #include "otutil.h"
+#include "ostree-repo-file-enumerator.h"
 
 #include <gio/gunixoutputstream.h>
 #include <gio/gunixinputstream.h>
@@ -1016,142 +1017,6 @@ parsed_tree_data_free (ParsedTreeData *pdata)
   g_free (pdata);
 }
 
-static gboolean
-parse_tree (OstreeRepo    *self,
-            const char      *sha256,
-            ParsedTreeData **out_pdata,
-            GError         **error)
-{
-  gboolean ret = FALSE;
-  ParsedTreeData *ret_pdata = NULL;
-  int i, n;
-  guint32 version;
-  GVariant *tree_variant = NULL;
-  GVariant *meta_variant = NULL;
-  GVariant *files_variant = NULL;
-  GVariant *dirs_variant = NULL;
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_TREE_VARIANT,
-                                         sha256, &tree_variant, error))
-    goto out;
-
-  /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
-  g_variant_get (tree_variant, "(u a{sv}@a(ss)@a(sss))",
-                 &version, &meta_variant, &files_variant, &dirs_variant);
-  version = GUINT32_FROM_BE (version);
-
-  ret_pdata = parsed_tree_data_new ();
-  n = g_variant_n_children (files_variant);
-  for (i = 0; i < n; i++)
-    {
-      const char *filename;
-      const char *checksum;
-
-      g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum);
-
-      g_hash_table_insert (ret_pdata->files, g_strdup (filename), g_strdup (checksum));
-    }
-
-  n = g_variant_n_children (dirs_variant);
-  for (i = 0; i < n; i++)
-    {
-      const char *dirname;
-      const char *tree_checksum;
-      const char *meta_checksum;
-      ParsedTreeData *child_tree = NULL;
-      GVariant *metadata = NULL;
-      ParsedDirectoryData *child_dir = NULL;
-
-      g_variant_get_child (dirs_variant, i, "(&s&s&s)",
-                           &dirname, &tree_checksum, &meta_checksum);
-      
-      if (!parse_tree (self, tree_checksum, &child_tree, error))
-        goto out;
-
-      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
-                                             meta_checksum, &metadata, error))
-        {
-          parsed_tree_data_free (child_tree);
-          goto out;
-        }
-
-      child_dir = g_new0 (ParsedDirectoryData, 1);
-      child_dir->tree_data = child_tree;
-      child_dir->metadata_sha256 = g_strdup (meta_checksum);
-      child_dir->meta_data = metadata;
-
-      g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
-    }
-
-  ret = TRUE;
- out:
-  if (!ret)
-    parsed_tree_data_free (ret_pdata);
-  else
-    *out_pdata = ret_pdata;
-  if (tree_variant)
-    g_variant_unref (tree_variant);
-  if (meta_variant)
-    g_variant_unref (meta_variant);
-  if (files_variant)
-    g_variant_unref (files_variant);
-  if (dirs_variant)
-    g_variant_unref (dirs_variant);
-  return ret;
-}
-
-static gboolean
-load_commit_and_trees (OstreeRepo   *self,
-                       const char     *commit_sha256,
-                       GVariant      **out_commit,
-                       ParsedDirectoryData **out_root_data,
-                       GError        **error)
-{
-  GVariant *ret_commit = NULL;
-  ParsedDirectoryData *ret_root_data = NULL;
-  ParsedTreeData *tree_data = NULL;
-  char *ret_metadata_checksum = NULL;
-  GVariant *root_metadata = NULL;
-  gboolean ret = FALSE;
-  const char *tree_contents_checksum;
-  const char *tree_meta_checksum;
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
-                                         commit_sha256, &ret_commit, error))
-    goto out;
-
-  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-  g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
-  g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
-                                         tree_meta_checksum, &root_metadata, error))
-    goto out;
-
-  if (!parse_tree (self, tree_contents_checksum, &tree_data, error))
-    goto out;
-
-  ret_root_data = g_new0 (ParsedDirectoryData, 1);
-  ret_root_data->tree_data = tree_data;
-  ret_root_data->metadata_sha256 = g_strdup (tree_meta_checksum);
-  ret_root_data->meta_data = root_metadata;
-  root_metadata = NULL;
-
-  ret = TRUE;
-  *out_commit = ret_commit;
-  ret_commit = NULL;
-  *out_root_data = ret_root_data;
-  ret_root_data = NULL;
- out:
-  if (ret_commit)
-    g_variant_unref (ret_commit);
-  parsed_directory_data_free (ret_root_data);
-  g_free (ret_metadata_checksum);
-  if (root_metadata)
-    g_variant_unref (root_metadata);
-  return ret;
-}
-
 static GVariant *
 create_empty_gvariant_dict (void)
 {
@@ -1807,106 +1672,130 @@ ostree_repo_load_variant (OstreeRepo *self,
 
 static gboolean
 checkout_tree (OstreeRepo    *self,
-               ParsedTreeData  *tree,
+               OstreeRepoFile *dir,
                const char      *destination,
+               GCancellable    *cancellable,
                GError         **error);
 
 static gboolean
 checkout_one_directory (OstreeRepo  *self,
                         const char *destination,
                         const char *dirname,
-                        ParsedDirectoryData *dir,
+                        OstreeRepoFile *dir,
+                        GFileInfo      *dir_info,
+                        GCancellable    *cancellable,
                         GError         **error)
 {
   gboolean ret = FALSE;
   char *dest_path = NULL;
-  guint32 version, uid, gid, mode;
   GVariant *xattr_variant = NULL;
 
   dest_path = g_build_filename (destination, dirname, NULL);
-      
-  /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
-  g_variant_get (dir->meta_data, "(uuuu a(ayay))",
-                 &version, &uid, &gid, &mode,
-                 &xattr_variant);
-  version = GUINT32_FROM_BE (version);
-  uid = GUINT32_FROM_BE (uid);
-  gid = GUINT32_FROM_BE (gid);
-  mode = GUINT32_FROM_BE (mode);
-
-  if (mkdir (dest_path, (mode_t)mode) < 0)
+
+  if (!_ostree_repo_file_get_xattrs (dir, &xattr_variant, NULL, error))
+    goto out;
+
+  if (mkdir (dest_path, (mode_t)g_file_info_get_attribute_uint32 (dir_info, "unix::mode")) < 0)
     {
       ot_util_set_error_from_errno (error, errno);
       g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
       goto out;
     }
-      
-  if (!checkout_tree (self, dir->tree_data, dest_path, error))
-    goto out;
 
-  if (!ostree_set_xattrs (dest_path, xattr_variant, error))
+  if (!ostree_set_xattrs (dest_path, xattr_variant, cancellable, error))
+    goto out;
+      
+  if (!checkout_tree (self, dir, dest_path, cancellable, error))
     goto out;
 
   ret = TRUE;
  out:
   g_free (dest_path);
-  g_variant_unref (xattr_variant);
+  if (xattr_variant)
+    g_variant_unref (xattr_variant);
   return ret;
 }
 
 static gboolean
 checkout_tree (OstreeRepo    *self,
-               ParsedTreeData  *tree,
+               OstreeRepoFile *dir,
                const char      *destination,
+               GCancellable    *cancellable,
                GError         **error)
 {
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
   gboolean ret = FALSE;
-  GHashTableIter hash_iter;
-  gpointer key, value;
+  GError *temp_error = NULL;
+  GFileInfo *file_info = NULL;
+  GFileEnumerator *dir_enum = NULL;
+  GFile *child = NULL;
+  char *object_path = NULL;
+  char *dest_path = NULL;
 
-  g_hash_table_iter_init (&hash_iter, tree->files);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+  dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO, 
+                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                        cancellable, 
+                                        error);
+  if (!dir_enum)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
     {
-      const char *filename = key;
-      const char *checksum = value;
-      char *object_path;
-      char *dest_path;
+      const char *name;
+      guint32 type;
 
-      object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
-      dest_path = g_build_filename (destination, filename, NULL);
-      
-      if (priv->archive)
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+      child = g_file_get_child ((GFile*)dir, name);
+
+      if (type == G_FILE_TYPE_DIRECTORY)
         {
-          if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+          if (!checkout_one_directory (self, destination, name, (OstreeRepoFile*)child, file_info, cancellable, error))
             goto out;
         }
       else
         {
-          if (link (object_path, dest_path) < 0)
+          const char *checksum = _ostree_repo_file_nontree_get_checksum ((OstreeRepoFile*)child);
+
+          dest_path = g_build_filename (destination, name, NULL);
+          object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+
+          if (priv->archive)
             {
-              ot_util_set_error_from_errno (error, errno);
-              g_free (object_path);
-              g_free (dest_path);
-              goto out;
+              if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+                goto out;
+            }
+          else
+            {
+              if (link (object_path, dest_path) < 0)
+                {
+                  ot_util_set_error_from_errno (error, errno);
+                  goto out;
+                }
             }
-          g_free (object_path);
-          g_free (dest_path);
         }
-    }
 
-  g_hash_table_iter_init (&hash_iter, tree->directories);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+      g_free (object_path);
+      object_path = NULL;
+      g_free (dest_path);
+      dest_path = NULL;
+      g_clear_object (&file_info);
+      g_clear_object (&child);
+    }
+  if (file_info == NULL && temp_error != NULL)
     {
-      const char *dirname = key;
-      ParsedDirectoryData *dir = value;
-      
-      if (!checkout_one_directory (self, destination, dirname, dir, error))
-        goto out;
+      g_propagate_error (error, temp_error);
+      goto out;
     }
 
   ret = TRUE;
  out:
+  g_clear_object (&dir_enum);
+  g_clear_object (&file_info);
+  g_clear_object (&child);
+  g_free (object_path);
+  g_free (dest_path);
   return ret;
 }
 
@@ -1914,13 +1803,13 @@ gboolean
 ostree_repo_checkout (OstreeRepo *self,
                       const char   *rev,
                       const char   *destination,
+                      GCancellable *cancellable,
                       GError      **error)
 {
   gboolean ret = FALSE;
-  GVariant *commit = NULL;
   char *resolved = NULL;
-  char *root_meta_sha = NULL;
-  ParsedDirectoryData *root = NULL;
+  OstreeRepoFile *root = NULL;
+  GFileInfo *root_info = NULL;
 
   if (g_file_test (destination, G_FILE_TEST_EXISTS))
     {
@@ -1933,18 +1822,23 @@ ostree_repo_checkout (OstreeRepo *self,
   if (!resolve_rev (self, rev, FALSE, &resolved, error))
     goto out;
 
-  if (!load_commit_and_trees (self, resolved, &commit, &root, error))
+  root = (OstreeRepoFile*)_ostree_repo_file_new_root (self, resolved);
+  if (!_ostree_repo_file_ensure_resolved (root, error))
+    goto out;
+
+  root_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 NULL, error);
+  if (!root_info)
     goto out;
 
-  if (!checkout_one_directory (self, destination, NULL, root, error))
+  if (!checkout_one_directory (self, destination, NULL, root, root_info, cancellable, error))
     goto out;
 
   ret = TRUE;
  out:
   g_free (resolved);
-  if (commit)
-    g_variant_unref (commit);
-  parsed_directory_data_free (root);
-  g_free (root_meta_sha);
+  g_clear_object (&root);
+  g_clear_object (&root_info);
   return ret;
 }
diff --git a/libostree/ostree-repo.h b/libostree/ostree-repo.h
index 17ec48f..5f46e46 100644
--- a/libostree/ostree-repo.h
+++ b/libostree/ostree-repo.h
@@ -18,7 +18,6 @@
  *
  * Author: Colin Walters <walters verbum org>
  */
-/* ostree-repo.h */
 
 #ifndef _OSTREE_REPO
 #define _OSTREE_REPO
@@ -120,9 +119,10 @@ gboolean      ostree_repo_commit_from_filelist_fd (OstreeRepo   *self,
                                                    GError      **error);
 
 gboolean      ostree_repo_checkout (OstreeRepo *self,
-                                      const char   *ref,
-                                      const char   *destination,
-                                      GError      **error);
+                                    const char   *ref,
+                                    const char   *destination,
+                                    GCancellable   *cancellable,
+                                    GError      **error);
 
 typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
                                         GFileInfo *fileinfo, gpointer user_data);
diff --git a/ostree/ot-builtin-checkout.c b/ostree/ot-builtin-checkout.c
index 8a80270..3662033 100644
--- a/ostree/ot-builtin-checkout.c
+++ b/ostree/ot-builtin-checkout.c
@@ -64,7 +64,7 @@ ostree_builtin_checkout (int argc, char **argv, const char *repo_path, GError **
   commit = argv[1];
   destination = argv[2];
   
-  if (!ostree_repo_checkout (repo, commit, destination, error))
+  if (!ostree_repo_checkout (repo, commit, destination, NULL, error))
     goto out;
 
   ret = TRUE;
diff --git a/ostree/ot-builtin-compose.c b/ostree/ot-builtin-compose.c
index 0f39af0..d43a6e5 100644
--- a/ostree/ot-builtin-compose.c
+++ b/ostree/ot-builtin-compose.c
@@ -196,7 +196,7 @@ compose_branch_on_dir (OstreeRepo *repo,
   branchf = ot_util_new_file_for_path (branchpath);
 
   g_print ("Checking out %s (commit %s)...\n", branch, branchrev);
-  if (!ostree_repo_checkout (repo, branchrev, branchpath, error))
+  if (!ostree_repo_checkout (repo, branchrev, branchpath, NULL, error))
     goto out;
   g_print ("...done\n");
   g_print ("Merging over destination...\n");



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