[ostree/wip/pull: 3/5] work on pull



commit fa2415f94c8821c70ec6fcf51312e0596b84dc0a
Author: Colin Walters <walters verbum org>
Date:   Sat Oct 29 12:01:31 2011 -0400

    work on pull

 Makefile-src.am             |    9 +-
 configure.ac                |    1 +
 src/libostree/ostree-core.c |   93 ++++++++++++
 src/libostree/ostree-core.h |   22 ++-
 src/libostree/ostree-repo.c |  246 +++++++++++++++++++------------
 src/libostree/ostree-repo.h |   25 +++
 src/main.c                  |    1 +
 src/ot-builtin-pull.c       |  348 +++++++++++++++++++++++++++++++++++++++++++
 src/ot-builtin-remote.c     |   18 +--
 src/ot-builtins.h           |    1 +
 tests/Makefile              |    5 +-
 tests/libtest.sh            |   27 ++++
 tests/ostree-http-server.c  |    5 +-
 tests/t0012-pull.sh         |   33 ++++
 14 files changed, 714 insertions(+), 120 deletions(-)
---
diff --git a/Makefile-src.am b/Makefile-src.am
index 9990ecd..30b5af8 100644
--- a/Makefile-src.am
+++ b/Makefile-src.am
@@ -40,8 +40,8 @@ libostree_la_SOURCES = src/libostree/ostree.h \
 	src/libostree/ostree-checkout.c \
 	src/libostree/ostree-checkout.h \
 	$(NULL)
-libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
-libostree_la_LIBADD = libotutil.la $(GIO_UNIX_LIBS)
+libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
 
 bin_PROGRAMS += ostree
 
@@ -53,10 +53,11 @@ ostree_SOURCES = src/main.c \
 	src/ot-builtin-init.c \
 	src/ot-builtin-link-file.c \
 	src/ot-builtin-log.c \
+	src/ot-builtin-pull.c \
 	src/ot-builtin-run-triggers.c \
 	src/ot-builtin-remote.c \
 	src/ot-builtin-rev-parse.c \
 	src/ot-builtin-show.c \
 	$(NULL)
-ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
-ostree_LDADD = libotutil.la libostree.la $(GIO_UNIX_LIBS)
+ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
diff --git a/configure.ac b/configure.ac
index be78b1f..92c06d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,7 @@ LT_INIT
 PKG_PROG_PKG_CONFIG
 
 PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0 >= 2.28])
+PKG_CHECK_MODULES(OT_COREBIN_DEP, [libsoup-gnome-2.4 >= 2.34.0 gio-unix-2.0 >= 2.28])
 
 AM_PATH_PYTHON
 
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index d92681b..c0b8e74 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -27,6 +27,20 @@
 #include <sys/types.h>
 #include <attr/xattr.h>
 
+gboolean
+ostree_validate_checksum_string (const char *sha256,
+                                 GError    **error)
+{
+  if (strlen (sha256) != 64)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid rev '%s'", sha256);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+
 static char *
 stat_to_string (struct stat *stbuf)
 {
@@ -270,3 +284,82 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
 
   return ret;
 }
+
+gboolean
+ostree_parse_metadata_file (const char                  *path,
+                            OstreeSerializedVariantType *out_type,
+                            GVariant                   **out_variant,
+                            GError                     **error)
+{
+  GMappedFile *mfile = NULL;
+  gboolean ret = FALSE;
+  GVariant *ret_variant = NULL;
+  GVariant *container = NULL;
+  guint32 ret_type;
+
+  mfile = g_mapped_file_new (path, FALSE, error);
+  if (mfile == NULL)
+    {
+      goto out;
+    }
+  else
+    {
+      container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+                                           g_mapped_file_get_contents (mfile),
+                                           g_mapped_file_get_length (mfile),
+                                           FALSE,
+                                           (GDestroyNotify) g_mapped_file_unref,
+                                           mfile);
+      g_variant_get (container, "(uv)",
+                     &ret_type, &ret_variant);
+      if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
+          goto out;
+        }
+      mfile = NULL;
+    }
+
+  ret = TRUE;
+  *out_type = ret_type;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  if (container != NULL)
+    g_variant_unref (container);
+  if (mfile != NULL)
+    g_mapped_file_unref (mfile);
+  return ret;
+}
+
+char *
+ostree_get_relative_object_path (const char *checksum,
+                                 OstreeObjectType type)
+{
+  GString *path;
+  const char *type_string;
+
+  g_assert (strlen (checksum) == 64);
+
+  path = g_string_new ("objects/");
+
+  g_string_append_len (path, checksum, 2);
+  g_string_append_c (path, '/');
+  g_string_append (path, checksum + 2);
+  switch (type)
+    {
+    case OSTREE_OBJECT_TYPE_FILE:
+      type_string = ".file";
+      break;
+    case OSTREE_OBJECT_TYPE_META:
+      type_string = ".meta";
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+  g_string_append (path, type_string);
+  return g_string_free (path, FALSE);
+}
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 6d80941..03d8375 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -39,6 +39,7 @@ typedef enum {
   OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
   OSTREE_SERIALIZED_XATTR_VARIANT = 4
 } OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
 
 #define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
 
@@ -83,13 +84,24 @@ typedef enum {
  */
 #define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
 
-GVariant *ostree_get_xattrs_for_path (const char *path,
-                                        GError    **error);
+gboolean ostree_validate_checksum_string (const char *sha256,
+                                          GError    **error);
+
+char *ostree_get_relative_object_path (const char *checksum,
+                                       OstreeObjectType type);
+
+GVariant *ostree_get_xattrs_for_path (const char   *path,
+                                      GError     **error);
+
+gboolean ostree_parse_metadata_file (const char                  *path,
+                                     OstreeSerializedVariantType *out_type,
+                                     GVariant                   **out_variant,
+                                     GError                     **error);
 
 gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
-                                          GChecksum **out_checksum,
-                                          struct stat *out_stbuf,
-                                          GError **error);
+                                        GChecksum **out_checksum,
+                                        struct stat *out_stbuf,
+                                        GError **error);
 
 
 #endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 964ce69..f22e60c 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -57,6 +57,7 @@ struct _OstreeRepoPrivate {
   char *path;
   GFile *repo_file;
   GFile *local_heads_dir;
+  GFile *remote_heads_dir;
   char *objects_path;
   char *config_path;
 
@@ -74,6 +75,7 @@ ostree_repo_finalize (GObject *object)
   g_free (priv->path);
   g_clear_object (&priv->repo_file);
   g_clear_object (&priv->local_heads_dir);
+  g_clear_object (&priv->remote_heads_dir);
   g_free (priv->objects_path);
   g_free (priv->config_path);
   if (priv->config)
@@ -140,6 +142,7 @@ ostree_repo_constructor (GType                  gtype,
   
   priv->repo_file = ot_util_new_file_for_path (priv->path);
   priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
+  priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
   
   priv->objects_path = g_build_filename (priv->path, "objects", NULL);
   priv->config_path = g_build_filename (priv->path, "config", NULL);
@@ -180,19 +183,6 @@ ostree_repo_new (const char *path)
 }
 
 static gboolean
-validate_checksum_string (const char *sha256,
-                          GError    **error)
-{
-  if (strlen (sha256) != 64)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid rev '%s'", sha256);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-static gboolean
 parse_rev_file (OstreeRepo     *self,
                 const char     *path,
                 char          **sha256,
@@ -252,7 +242,7 @@ parse_rev_file (OstreeRepo     *self,
     }
   else 
     {
-      if (!validate_checksum_string (rev, error))
+      if (!ostree_validate_checksum_string (rev, error))
         goto out;
     }
 
@@ -304,7 +294,7 @@ resolve_rev (OstreeRepo     *self,
        {
          g_strchomp (ret_rev);
          
-         if (!validate_checksum_string (ret_rev, error))
+         if (!ostree_validate_checksum_string (ret_rev, error))
            goto out;
        }
    }
@@ -357,6 +347,81 @@ write_checksum_file (GFile *parentdir,
   return ret;
 }
 
+/**
+ * ostree_repo_get_config:
+ * @self:
+ *
+ * Returns: (transfer none): The repository configuration; do not modify
+ */
+GKeyFile *
+ostree_repo_get_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  return priv->config;
+}
+
+/**
+ * ostree_repo_copy_config:
+ * @self:
+ *
+ * Returns: (transfer full): A newly-allocated copy of the repository config
+ */
+GKeyFile *
+ostree_repo_copy_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GKeyFile *copy;
+  char *data;
+  gsize len;
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  copy = g_key_file_new ();
+  data = g_key_file_to_data (priv->config, &len, NULL);
+  if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
+    g_assert_not_reached ();
+  g_free (data);
+  return copy;
+}
+
+/**
+ * ostree_repo_write_config:
+ * @self:
+ * @new_config: Overwrite the config file with this data.  Do not change later!
+ * @error: a #GError
+ *
+ * Save @new_config in place of this repository's config file.  Note
+ * that @new_config should not be modified after - this function
+ * simply adds a reference.
+ */
+gboolean
+ostree_repo_write_config (OstreeRepo *self,
+                          GKeyFile   *new_config,
+                          GError    **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *data = NULL;
+  gsize len;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  data = g_key_file_to_data (new_config, &len, error);
+  if (!g_file_set_contents (priv->config_path, data, len, error))
+    goto out;
+  
+  g_key_file_unref (priv->config);
+  priv->config = g_key_file_ref (new_config);
+
+  ret = TRUE;
+ out:
+  g_free (data);
+  return ret;
+}
+
 gboolean
 ostree_repo_check (OstreeRepo *self, GError **error)
 {
@@ -402,6 +467,13 @@ ostree_repo_check (OstreeRepo *self, GError **error)
   return ret;
 }
 
+const char *
+ostree_repo_get_path (OstreeRepo  *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  return priv->path;
+}
+
 static gboolean
 import_gvariant_object (OstreeRepo  *self,
                         OstreeSerializedVariantType type,
@@ -463,54 +535,13 @@ load_gvariant_object_unknown (OstreeRepo  *self,
                               GVariant     **out_variant,
                               GError       **error)
 {
-  GMappedFile *mfile = NULL;
   gboolean ret = FALSE;
-  GVariant *ret_variant = NULL;
-  GVariant *container = NULL;
   char *path = NULL;
-  guint32 ret_type;
 
   path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
-  
-  mfile = g_mapped_file_new (path, FALSE, error);
-  if (mfile == NULL)
-    goto out;
-  else
-    {
-      container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
-                                           g_mapped_file_get_contents (mfile),
-                                           g_mapped_file_get_length (mfile),
-                                           FALSE,
-                                           (GDestroyNotify) g_mapped_file_unref,
-                                           mfile);
-      if (!g_variant_is_of_type (container, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT)))
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Corrupted metadata object '%s'", sha256);
-          goto out;
-        }
-      g_variant_get (container, "(uv)",
-                     &ret_type, &ret_variant);
-      mfile = NULL;
-    }
-
-  ret = TRUE;
- out:
-  if (!ret)
-    {
-      if (ret_variant)
-        g_variant_unref (ret_variant);
-    }
-  else
-    {
-      *out_type = ret_type;
-      *out_variant = ret_variant;
-    }
-  if (container != NULL)
-    g_variant_unref (container);
+  ret = ostree_parse_metadata_file (path, out_type, out_variant, error);
   g_free (path);
-  if (mfile != NULL)
-    g_mapped_file_unref (mfile);
+
   return ret;
 }
 
@@ -534,7 +565,6 @@ load_gvariant_object (OstreeRepo  *self,
                    "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
                    type, (guint32)expected_type);
       goto out;
-      
     }
 
   ret = TRUE;
@@ -616,41 +646,25 @@ get_object_path (OstreeRepo  *self,
                  OstreeObjectType type)
 {
   OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  char *checksum_prefix;
-  char *base_path;
-  char *ret;
-  const char *type_string;
-
-  checksum_prefix = g_strndup (checksum, 2);
-  base_path = g_build_filename (priv->objects_path, checksum_prefix, checksum + 2, NULL);
-  switch (type)
-    {
-    case OSTREE_OBJECT_TYPE_FILE:
-      type_string = ".file";
-      break;
-    case OSTREE_OBJECT_TYPE_META:
-      type_string = ".meta";
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-  ret = g_strconcat (base_path, type_string, NULL);
-  g_free (base_path);
-  g_free (checksum_prefix);
+  char *relpath;
+
+  relpath = ostree_get_relative_object_path (checksum, type);
+  ret = g_build_filename (priv->path, relpath, NULL);
+  g_free (relpath);
  
   return ret;
 }
 
 static char *
 prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
-                                          GChecksum    *checksum,
+                                          const char   *checksum,
                                           OstreeObjectType type,
                                           GError      **error)
 {
   char *checksum_dir = NULL;
   char *object_path = NULL;
 
-  object_path = get_object_path (self, g_checksum_get_string (checksum), type);
+  object_path = get_object_path (self, checksum, type);
   checksum_dir = g_path_get_dirname (object_path);
 
   if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
@@ -661,18 +675,21 @@ prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
   return object_path;
 }
 
-static gboolean
-link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
-               gboolean ignore_exists, gboolean force,
-               GChecksum **out_checksum,
-               GError **error)
+gboolean      
+ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                  const char   *path,
+                                  const char   *checksum,
+                                  OstreeObjectType objtype,
+                                  gboolean      ignore_exists,
+                                  gboolean      force,
+                                  gboolean     *did_exist,
+                                  GError      **error)
 {
   char *src_basename = NULL;
   char *src_dirname = NULL;
   char *dest_basename = NULL;
   char *tmp_dest_basename = NULL;
   char *dest_dirname = NULL;
-  GChecksum *id = NULL;
   DIR *src_dir = NULL;
   DIR *dest_dir = NULL;
   gboolean ret = FALSE;
@@ -689,9 +706,7 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
       goto out;
     }
 
-  if (!ostree_stat_and_checksum_file (dirfd (src_dir), path, &id, &stbuf, error))
-    goto out;
-  dest_path = prepare_dir_for_checksum_get_object_path (self, id, type, error);
+  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, type, error);
   if (!dest_path)
     goto out;
 
@@ -719,7 +734,11 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
           ot_util_set_error_from_errno (error, errno);
           goto out;
         }
+      else
+        *did_exist = TRUE;
     }
+  else
+    *did_exist = FALSE;
 
   if (force)
     {
@@ -732,12 +751,8 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
       (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
     }
 
-  *out_checksum = id;
-  id = NULL;
   ret = TRUE;
  out:
-  if (id != NULL)
-    g_checksum_free (id);
   if (src_dir != NULL)
     closedir (src_dir);
   if (dest_dir != NULL)
@@ -750,6 +765,31 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
   return ret;
 }
 
+static gboolean
+link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
+               gboolean ignore_exists, gboolean force,
+               GChecksum **out_checksum,
+               GError **error)
+{
+  struct stat stbuf;
+  GChecksum *id = NULL;
+  gboolean did_exist;
+
+  if (!ostree_stat_and_checksum_file (dirfd (src_dir), path, &id, &stbuf, error))
+    goto out;
+
+  if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
+                                         ignore_exists, force, &did_exist, error))
+    goto out;
+
+  *out_checksum = id;
+  id = NULL;
+  ret = TRUE;
+ out:
+  if (id != NULL)
+    g_checksum_free (id);
+}
+
 gboolean
 ostree_repo_link_file (OstreeRepo *self,
                          const char   *path,
@@ -839,6 +879,7 @@ parse_tree (OstreeRepo    *self,
                              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);
 
@@ -922,6 +963,7 @@ load_commit_and_trees (OstreeRepo   *self,
                              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);
 
@@ -1366,6 +1408,17 @@ add_files_to_tree_and_import (OstreeRepo   *self,
   return ret;
 }
 
+gboolean      
+ostree_repo_write_ref (OstreeRepo  *self,
+                       gboolean     is_local,
+                       const char  *name,
+                       const char  *rev,
+                       GError     **error)
+{
+  return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir, 
+                              name, rev, error))
+}
+
 static gboolean
 commit_parsed_tree (OstreeRepo *self,
                     const char   *branch,
@@ -1403,7 +1456,7 @@ commit_parsed_tree (OstreeRepo *self,
                                commit, &ret_commit, error))
     goto out;
 
-  if (!write_checksum_file (priv->local_heads_dir, branch, g_checksum_get_string (ret_commit), error))
+  if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
     goto out;
 
   ret = TRUE;
@@ -1789,6 +1842,7 @@ checkout_one_directory (OstreeRepo  *self,
 
   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);
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index c0159ea..36149ef 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -53,17 +53,42 @@ OstreeRepo* ostree_repo_new (const char *path);
 
 gboolean      ostree_repo_check (OstreeRepo  *self, GError **error);
 
+const char *  ostree_repo_get_path (OstreeRepo  *self);
+
+GKeyFile *    ostree_repo_get_config (OstreeRepo *self);
+
+GKeyFile *    ostree_repo_copy_config (OstreeRepo *self);
+
+gboolean      ostree_repo_write_config (OstreeRepo *self,
+                                        GKeyFile   *new_config,
+                                        GError    **error);
+
 gboolean      ostree_repo_link_file (OstreeRepo *self,
                                        const char   *path,
                                        gboolean      ignore_exists,
                                        gboolean      force,
                                        GError      **error);
 
+gboolean      ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                                const char   *path,
+                                                const char   *checksum,
+                                                OstreeObjectType objtype,
+                                                gboolean      ignore_exists,
+                                                gboolean      force,
+                                                gboolean     *did_exist,
+                                                GError      **error);
+
 gboolean      ostree_repo_resolve_rev (OstreeRepo  *self,
                                        const char  *rev,
                                        char       **out_resolved,
                                        GError     **error);
 
+gboolean      ostree_repo_write_ref (OstreeRepo  *self,
+                                     gboolean     is_local,
+                                     const char  *name,
+                                     const char  *rev,
+                                     GError     **error);
+
 gboolean      ostree_repo_load_variant (OstreeRepo *self,
                                           const char   *sha256,
                                           OstreeSerializedVariantType *out_type,
diff --git a/src/main.c b/src/main.c
index f32e741..ad5dfd8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -33,6 +33,7 @@ static OstreeBuiltin builtins[] = {
   { "commit", ostree_builtin_commit, 0 },
   { "link-file", ostree_builtin_link_file, 0 },
   { "log", ostree_builtin_log, 0 },
+  { "pull", ostree_builtin_pull, 0 },
   { "fsck", ostree_builtin_fsck, 0 },
   { "remote", ostree_builtin_remote, 0 },
   { "rev-parse", ostree_builtin_rev_parse, 0 },
diff --git a/src/ot-builtin-pull.c b/src/ot-builtin-pull.c
new file mode 100644
index 0000000..db13cb9
--- /dev/null
+++ b/src/ot-builtin-pull.c
@@ -0,0 +1,348 @@
+/* -*- 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 "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+#include <libsoup/soup-gnome.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+static void
+usage_error (GOptionContext *context, const char *message, GError **error)
+{
+  gchar *help = g_option_context_get_help (context, TRUE, NULL);
+  g_printerr ("%s\n", help);
+  g_free (help);
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       message);
+}
+
+static gboolean
+fetch_uri (OstreeRepo  *repo,
+           SoupSession *soup,
+           SoupURI     *uri,
+           char       **temp_filename,
+           GError     **error)
+{
+  gboolean ret = FALSE;
+  SoupMessage *msg = NULL;
+  guint response;
+  char *template = NULL;
+  int fd;
+  SoupBuffer *buf = NULL;
+  GFile *tempf = NULL;
+  
+  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+  
+  response = soup_session_send_message (soup, msg);
+  if (response != 200)
+    {
+      char *uri_string = soup_uri_to_string (uri, FALSE);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to retrieve '%s': %d %s",
+                   uri_string, response, msg->reason_phrase);
+      g_free (uri_string);
+      goto out;
+    }
+
+  template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
+  
+  fd = g_mkstemp (template);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  close (fd);
+  tempf = g_file_new_for_path (template);
+
+  buf = soup_message_body_flatten (msg->response_body);
+
+  if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
+    goto out;
+  
+  *temp_filename = template;
+  template = NULL;
+
+  ret = TRUE;
+ out:
+  g_free (template);
+  g_clear_object (&msg);
+  g_clear_object (&tempf);
+  return ret;
+}
+
+static gboolean
+store_object (OstreeRepo  *repo,
+              SoupSession *soup,
+              SoupURI     *baseuri,
+              const char  *object,
+              OstreeObjectType objtype,
+              gboolean    *did_exist,
+              GError     **error)
+{
+  gboolean ret = FALSE;
+  char *filename = NULL;
+  char *objpath = NULL;
+  char *relpath = NULL;
+  SoupURI *obj_uri = NULL;
+  GChecksum *checksum = NULL;
+
+  objpath = ostree_get_relative_object_path (rev, objtype);
+  obj_uri = soup_uri_copy (baseuri);
+  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
+  soup_uri_set_path (obj_uri, relpath);
+
+  if (!fetch_uri (repo, soup, relpath, &filename, error))
+    goto out;
+
+  if (!ostree_stat_and_checksum_file (-1, filename, &checksum,
+                                      &stbuf, error))
+    goto out;
+
+  if (strcmp (g_checksum_get_string (checksum), object) != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted object %s (actual checksum is %s)",
+                   object, g_checksum_get_string (checksum));
+      goto out;
+    }
+
+  if (!ostree_repo_store_object_trusted (repo, filename, object, TRUE, FALSE, did_exist, error))
+    goto out;
+
+  ret = TRUE;
+  *temp_filename = ret_filename;
+  ret_filename = NULL;
+ out:
+  if (checksum)
+    g_checksum_free (checksum);
+  if (obj_uri)
+    soup_uri_free (obj_uri);
+  g_free (ret_filename);
+  g_free (objpath);
+  return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo   *repo,
+                    SoupSession  *session,
+                    SoupURI      *baseuri,
+                    const char   *rev,
+                    GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *tree = NULL;
+  OstreeSerializedVariantType metatype;
+  gboolean did_exist;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Tree metadata '%s' has wrong type %d, expected %d",
+                       rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
+          goto out;
+        }
+      
+      /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+      g_variant_get_child (commit, 2, "@a(ss)", &files_variant);
+      g_variant_get_child (commit, 3, "@a(sss)", &dirs_variant);
+
+      store files_variant
+      store dirs_variant
+    }
+
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo   *repo,
+                      SoupSession  *session,
+                      SoupURI      *baseuri,
+                      const char   *rev,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  OstreeSerializedVariantType metatype;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+  gboolean did_exist;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Commit '%s' has wrong type %d, expected %d",
+                       branch, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
+          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 (!store_object (repo, session, baseuri, tree_meta_checksum, error))
+        goto out;
+      
+      if (!store_tree_recurse (repo, session, baseuri, tree_contents_checksum, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  return ret;
+}
+                      
+gboolean
+ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *remote;
+  const char *branch;
+  char *remote_branch_ref_path = NULL;
+  char *key = NULL;
+  char *baseurl = NULL;
+  char *refpath = NULL;
+  char *temppath = NULL;
+  GKeyFile *config = NULL;
+  SoupURI *base_uri = NULL;
+  SoupURI *target_uri = NULL;
+  SoupSession *soup = NULL;
+  char *rev = NULL;
+  gsize len;
+
+  context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      usage_error (context, "REMOTE and BRANCH must be specified", error);
+      goto out;
+    }
+
+  remote = argv[1];
+  branch = argv[2];
+
+  config = ostree_repo_get_config (repo);
+
+  key = g_strdup_printf ("remote \"%s\"", remote);
+  baseurl = g_key_file_get_string (config, key, "url", error);
+  if (!baseurl)
+    goto out;
+  base_uri = soup_uri_new (baseurl);
+  if (!base_uri)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to parse url '%s'", baseurl);
+      goto out;
+    }
+  target_uri = soup_uri_copy (base_uri);
+  g_free (refpath);
+  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
+  soup_uri_set_path (target_uri, refpath);
+  
+  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+                                             NULL);
+  if (!fetch_uri (repo, soup, target_uri, &temppath, error))
+    goto out;
+
+  if (!ot_util_get_file_contents_utf8 (temppath, &rev, error))
+    goto out;
+
+  if (!ostree_validate_checksum_string (rev, error))
+    goto out;
+
+  if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+    goto out;
+
+  if (!g_file_
+ 
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (temppath)
+    (void) unlink (temppath);
+  g_free (temppath);
+  g_free (key);
+  g_free (rev);
+  g_free (baseurl);
+  g_free (refpath);
+  g_free (remote_branch_ref_path);
+  g_clear_object (&soup);
+  if (base_uri)
+    soup_uri_free (base_uri);
+  if (target_uri)
+    soup_uri_free (target_uri);
+  if (commit)
+    g_variant_unref (commit);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/src/ot-builtin-remote.c b/src/ot-builtin-remote.c
index f26d683..40f7c29 100644
--- a/src/ot-builtin-remote.c
+++ b/src/ot-builtin-remote.c
@@ -51,9 +51,6 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
   OstreeRepo *repo = NULL;
   OstreeCheckout *checkout = NULL;
   const char *op;
-  gsize len;
-  char *config_path = NULL;
-  char *data = NULL;
   GKeyFile *config = NULL;
 
   context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
@@ -77,10 +74,7 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
 
   op = argv[1];
 
-  config = g_key_file_new ();
-  config_path = g_build_filename (repo_path, "config", NULL);
-  if (!g_key_file_load_from_file (config, config_path, 0, error))
-    goto out;
+  config = ostree_repo_copy_config (repo);
 
   if (!strcmp (op, "add"))
     {
@@ -92,25 +86,23 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
         }
       key = g_strdup_printf ("remote \"%s\"", argv[2]);
       g_key_file_set_string (config, key, "url", argv[3]);
+      g_free (key);
     }
   else
     {
       usage_error (context, "Unknown operation", error);
       goto out;
     }
- 
-  data = g_key_file_to_data (config, &len, error);
-  if (!g_file_set_contents (config_path, data, len, error))
-    goto out;
 
+  if (!ostree_repo_write_config (repo, config, error))
+    goto out;
+ 
   ret = TRUE;
  out:
   if (context)
     g_option_context_free (context);
   if (config)
     g_key_file_unref (config);
-  g_free (data);
-  g_free (config_path);
   g_clear_object (&repo);
   g_clear_object (&checkout);
   return ret;
diff --git a/src/ot-builtins.h b/src/ot-builtins.h
index 1da3db9..1373f60 100644
--- a/src/ot-builtins.h
+++ b/src/ot-builtins.h
@@ -41,6 +41,7 @@ gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GErro
 gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
 gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
 gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
 gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
 gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
 gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);
diff --git a/tests/Makefile b/tests/Makefile
index c24db2c..e35744b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -20,7 +20,10 @@
 
 TESTS = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 
-all:
+all: ostree-http-server
+
+ostree-http-server: ostree-http-server.c Makefile
+	gcc $(CFLAGS) `pkg-config --cflags --libs libsoup-gnome-2.4` -o ostree-http-server $<
 
 check:
 	@for test in $(TESTS); do \
diff --git a/tests/libtest.sh b/tests/libtest.sh
index cedf537..9e50e4d 100644
--- a/tests/libtest.sh
+++ b/tests/libtest.sh
@@ -18,6 +18,9 @@
 #
 # Author: Colin Walters <walters verbum org>
 
+cd `dirname $0`
+SRCDIR=`pwd`
+cd -
 TMPDIR=${TMPDIR:-/tmp}
 export TMPDIR
 test_tmpdir=`mktemp -d "$TMPDIR/ostree-tests.XXXXXXXXXX"`
@@ -91,4 +94,28 @@ setup_test_repository2 () {
     ostree fsck -q $ot_repo
 }
 
+setup_fake_remote_repo1() {
+    oldpwd=`pwd`
+    mkdir remote
+    ostree init --repo=remote
+    mkdir remote-files
+    cd remote-files 
+    echo first > firstfile
+    mkdir baz
+    echo moo > baz/cow
+    echo alien > baz/saucer
+    find | grep -v '^\.$' | ostree commit --repo=${test_tmpdir}/remote -b main -s "A remote commit" -m "Some Commit body" --from-stdin
+    mkdir baz/deeper
+    ostree commit --repo=${test_tmpdir}/remote -b main -s "Add deeper" --add=baz/deeper
+    echo hi > baz/deeper/ohyeah
+    mkdir baz/another/
+    echo x > baz/another/y
+    find | grep -v '^\.$' | ostree commit --repo=${test_tmpdir}/remote -b main -s "The rest" --from-stdin
+    cd ..
+    rm -rf remote-files
+    
+    ${SRCDIR}/ostree-http-server > ${test_tmpdir}/remote-address &
+    cd ${oldpwd} 
+}
+
 trap 'die' EXIT
diff --git a/tests/ostree-http-server.c b/tests/ostree-http-server.c
index 63edda5..a6c0279 100644
--- a/tests/ostree-http-server.c
+++ b/tests/ostree-http-server.c
@@ -39,7 +39,10 @@ request_callback (SoupServer *server, SoupMessage *msg,
       file = g_file_new_for_path (path + 1);
       
       if (g_file_load_contents (file, NULL, &content, &len, NULL, NULL))
-	soup_message_set_response (msg, "application/octet-stream", SOUP_MEMORY_TAKE, content, len);
+	{
+	  soup_message_set_response (msg, "application/octet-stream", SOUP_MEMORY_TAKE, content, len);
+	  soup_message_set_status (msg, SOUP_STATUS_OK);
+	}
       else
 	soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
     }
diff --git a/tests/t0012-pull.sh b/tests/t0012-pull.sh
new file mode 100755
index 0000000..a98466e
--- /dev/null
+++ b/tests/t0012-pull.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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>
+
+set -e
+
+. libtest.sh
+
+echo '1..1'
+
+setup_fake_remote_repo1
+cd ${test_tmpdir}
+mkdir repo
+ostree init --repo=repo
+ostree remote --repo=repo add origin $(cat remote-address)/remote
+ostree pull --repo=repo origin main
+echo "ok pull"



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