[ostree/wip/pull-rebase2] Add pull
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree/wip/pull-rebase2] Add pull
- Date: Tue, 1 Nov 2011 00:19:47 +0000 (UTC)
commit 8be40f3af387bfe07e958ae80985aaffc705432b
Author: Colin Walters <walters verbum org>
Date: Sat Oct 29 12:01:31 2011 -0400
Add pull
Makefile-src.am | 16 +-
configure.ac | 1 +
src/libostree/ostree-checkout.c | 2 +-
src/libostree/ostree-core.c | 313 ++++++++++-
src/libostree/ostree-core.h | 52 ++-
src/libostree/ostree-repo.c | 582 ++++++++++++++++----
src/libostree/ostree-repo.h | 41 ++-
src/main.c | 1 +
src/ostree-http-backend.c | 45 ++
src/ot-builtin-fsck.c | 111 ++++-
src/ot-builtin-init.c | 25 +-
src/ot-builtin-pull.c | 365 ++++++++++++
src/ot-builtin-remote.c | 18 +-
src/ot-builtins.h | 1 +
tests/Makefile | 8 +-
tests/libtest.sh | 52 ++
tests/run-apache.c | 167 ++++++
tests/t0012-pull.sh | 34 ++
tests/t0013-commit-archive.sh | 42 ++
tests/{ostree-http-server.c => tmpdir-lifecycle.c} | 89 ++--
20 files changed, 1760 insertions(+), 205 deletions(-)
---
diff --git a/Makefile-src.am b/Makefile-src.am
index 9990ecd..ae28761 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,18 @@ 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)
+
+bin_PROGRAMS += ostree-http-backend
+
+ostree_http_backend_SOURCES = src/ostree-http-backend.c
+ostree_http_backend_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_http_backend_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-checkout.c b/src/libostree/ostree-checkout.c
index e4f09fb..0569f28 100644
--- a/src/libostree/ostree-checkout.c
+++ b/src/libostree/ostree-checkout.c
@@ -204,7 +204,7 @@ run_trigger (OstreeCheckout *self,
{
temp_path = g_build_filename (priv->path, basename, NULL);
rel_temp_path = g_strconcat ("./", basename, NULL);
- temp_copy = g_file_new_for_path (temp_path);
+ temp_copy = ot_util_new_file_for_path (temp_path);
if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
goto out;
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index d92681b..ce54fbc 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -27,13 +27,27 @@
#include <sys/types.h>
#include <attr/xattr.h>
-static char *
-stat_to_string (struct stat *stbuf)
+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;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
{
- return g_strdup_printf ("%u:%u:%u",
- (guint32)(stbuf->st_mode & ~S_IFMT),
- (guint32)stbuf->st_uid,
- (guint32)stbuf->st_gid);
+ guint32 perms = (mode & ~S_IFMT);
+ g_checksum_update (checksum, (guint8*) &uid, 4);
+ g_checksum_update (checksum, (guint8*) &gid, 4);
+ g_checksum_update (checksum, (guint8*) &perms, 4);
}
static char *
@@ -144,6 +158,7 @@ ostree_get_xattrs_for_path (const char *path,
}
ret = g_variant_builder_end (&builder);
+ g_variant_ref_sink (ret);
out:
if (!ret)
g_variant_builder_clear (&builder);
@@ -154,9 +169,10 @@ ostree_get_xattrs_for_path (const char *path,
gboolean
ostree_stat_and_checksum_file (int dir_fd, const char *path,
- GChecksum **out_checksum,
- struct stat *out_stbuf,
- GError **error)
+ OstreeObjectType objtype,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error)
{
GChecksum *content_sha256 = NULL;
GChecksum *content_and_meta_sha256 = NULL;
@@ -202,10 +218,12 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
}
}
- stat_string = stat_to_string (&stbuf);
- xattrs = ostree_get_xattrs_for_path (path, error);
- if (!xattrs)
- goto out;
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ }
content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
@@ -224,6 +242,8 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
else if (S_ISLNK(stbuf.st_mode))
{
symlink_target = g_malloc (PATH_MAX);
+
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
if (bytes_read < 0)
@@ -235,6 +255,7 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
}
else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
{
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
}
@@ -249,8 +270,12 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
content_and_meta_sha256 = g_checksum_copy (content_sha256);
- g_checksum_update (content_and_meta_sha256, (guint8*)stat_string, strlen (stat_string));
- g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
+ stbuf.st_gid, stbuf.st_mode);
+ g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ }
*out_stbuf = stbuf;
*out_checksum = content_and_meta_sha256;
@@ -267,6 +292,264 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
g_variant_unref (xattrs);
if (content_sha256)
g_checksum_free (content_sha256);
+ return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
+{
+ gboolean ret = FALSE;
+ int i, n;
+
+ n = g_variant_n_children (xattrs);
+ for (i = 0; i < n; i++)
+ {
+ const guint8* name;
+ GVariant *value;
+ const guint8* value_data;
+ gsize value_len;
+ gboolean loop_err;
+
+ g_variant_get_child (xattrs, i, "(^&ay ay)",
+ &name, &value);
+ value_data = g_variant_get_fixed_array (value, &value_len, 1);
+
+ loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
+
+ g_variant_unref (value);
+ if (loop_err)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ 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,
+ gboolean archive)
+{
+ 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:
+ if (archive)
+ type_string = ".packfile";
+ else
+ 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);
+}
+
+gboolean
+ostree_pack_object (GOutputStream *output,
+ GFile *file,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *path = NULL;
+ GFileInfo *finfo = NULL;
+ GFileInputStream *instream = NULL;
+ gboolean pack_builder_initialized = FALSE;
+ GVariantBuilder pack_builder;
+ GVariant *pack_variant = NULL;
+ GVariant *xattrs = NULL;
+ gsize bytes_written;
+
+ path = g_file_get_path (file);
+
+ finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+ if (!finfo)
+ goto out;
+
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ {
+ guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
+ if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
+ goto out;
+
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+
+ if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
+ goto out;
+ }
+ else
+ {
+ guint32 uid, gid, mode;
+ guint32 device = 0;
+ guint32 metadata_size_be;
+ const char *target = NULL;
+ guint64 object_size;
+
+ uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
+ gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
+ mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+ g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
+ pack_builder_initialized = TRUE;
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
+
+ if (S_ISREG (mode))
+ {
+ object_size = (guint64)g_file_info_get_size (finfo);
+ }
+ else if (S_ISLNK (mode))
+ {
+ target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+ object_size = strlen (target);
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
+ object_size = 4;
+ }
+ else
+ g_assert_not_reached ();
+
+ g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
+ pack_variant = g_variant_builder_end (&pack_builder);
+ pack_builder_initialized = FALSE;
+
+ metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
+
+ if (!g_output_stream_write_all (output, &metadata_size_be, 4,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+
+ if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
+ &bytes_written, cancellable, error))
+ goto out;
+
+ if (S_ISREG (mode))
+ {
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+ bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
+ if (bytes_written < 0)
+ goto out;
+ if (bytes_written != object_size)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File size changed unexpectedly");
+ goto out;
+ }
+ }
+ else if (S_ISLNK (mode))
+ {
+ if (!g_output_stream_write_all (output, target, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ guint32 device_be = GUINT32_TO_BE (device);
+ g_assert (object_size == 4);
+ if (!g_output_stream_write_all (output, &device_be, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ ret = TRUE;
+ out:
+ g_free (path);
+ g_clear_object (&finfo);
+ g_clear_object (&instream);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (pack_builder_initialized)
+ g_variant_builder_clear (&pack_builder);
+ if (pack_variant)
+ g_variant_unref (pack_variant);
+ return ret;
+}
+
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 6d80941..3c16918 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,54 @@ 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,
+ gboolean archive);
+
+GVariant *ostree_get_xattrs_for_path (const char *path,
+ GError **error);
+
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, 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);
+ OstreeObjectType type,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error);
+
+/* Packed files:
+ *
+ * guint32 metadata_length [metadata gvariant] [content]
+ *
+ * metadata variant:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ * t - content length
+ *
+ * And then following the end of the variant is the content. If
+ * symlink, then this is the target; if device, then device ID as
+ * network byte order uint32.
+ */
+#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
+
+gboolean ostree_pack_object (GOutputStream *output,
+ GFile *path,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error);
+
+void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
#endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 964ce69..a1323c6 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -57,12 +57,14 @@ struct _OstreeRepoPrivate {
char *path;
GFile *repo_file;
GFile *local_heads_dir;
+ GFile *remote_heads_dir;
char *objects_path;
char *config_path;
gboolean inited;
GKeyFile *config;
+ gboolean archive;
};
static void
@@ -74,6 +76,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 +143,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 +184,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 +243,7 @@ parse_rev_file (OstreeRepo *self,
}
else
{
- if (!validate_checksum_string (rev, error))
+ if (!ostree_validate_checksum_string (rev, error))
goto out;
}
@@ -304,7 +295,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,12 +348,88 @@ 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)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
gboolean ret = FALSE;
char *version = NULL;;
+ GError *temp_error = NULL;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -383,9 +450,12 @@ ostree_repo_check (OstreeRepo *self, GError **error)
goto out;
}
- version = g_key_file_get_value (priv->config, "core", "repo_version", error);
- if (!version)
- goto out;
+ version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
+ if (temp_error)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
if (strcmp (version, "0") != 0)
{
@@ -394,6 +464,20 @@ ostree_repo_check (OstreeRepo *self, GError **error)
goto out;
}
+ priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
+ if (temp_error)
+ {
+ if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+
priv->inited = TRUE;
ret = TRUE;
@@ -402,6 +486,23 @@ 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;
+}
+
+gboolean
+ostree_repo_is_archive (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ return priv->archive;
+}
+
static gboolean
import_gvariant_object (OstreeRepo *self,
OstreeSerializedVariantType type,
@@ -463,54 +564,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 +594,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 +675,26 @@ get_object_path (OstreeRepo *self,
OstreeObjectType type)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
- char *checksum_prefix;
- char *base_path;
char *ret;
- const char *type_string;
+ char *relpath;
- 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);
+ relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
+ 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))
@@ -662,21 +706,23 @@ prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
}
static gboolean
-link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
- gboolean ignore_exists, gboolean force,
- GChecksum **out_checksum,
- GError **error)
+link_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;
- struct stat stbuf;
char *dest_path = NULL;
src_basename = g_path_get_basename (path);
@@ -689,9 +735,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, objtype, error);
if (!dest_path)
goto out;
@@ -719,7 +763,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 +780,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,12 +794,108 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
return ret;
}
+static gboolean
+archive_file_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ GFile *infile = NULL;
+ GFile *outfile = NULL;
+ GFileOutputStream *out = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ char *dest_tmp_path = NULL;
+
+ infile = ot_util_new_file_for_path (path);
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
+
+ outfile = ot_util_new_file_for_path (dest_tmp_path);
+ out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ if (rename (dest_tmp_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ g_free (dest_tmp_path);
+ g_clear_object (&infile);
+ g_clear_object (&outfile);
+ g_clear_object (&out);
+ return ret;
+}
+
+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)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
+ return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+ else
+ return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+}
+
+static gboolean
+link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
+ gboolean ignore_exists, gboolean force,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ GChecksum *id = NULL;
+ gboolean did_exist;
+
+ if (!ostree_stat_and_checksum_file (-1, path, type, &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);
+ return ret;
+}
+
gboolean
ostree_repo_link_file (OstreeRepo *self,
- const char *path,
- gboolean ignore_exists,
- gboolean force,
- GError **error)
+ const char *path,
+ gboolean ignore_exists,
+ gboolean force,
+ GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
GChecksum *checksum = NULL;
@@ -770,6 +910,227 @@ ostree_repo_link_file (OstreeRepo *self,
return TRUE;
}
+static gboolean
+unpack_and_checksum_packfile (OstreeRepo *self,
+ const char *path,
+ gchar **out_filename,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ char *temp_path = NULL;
+ GFile *temp_file = NULL;
+ GFileOutputStream *temp_out = NULL;
+ char *metadata_buf = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GFileInputStream *in = NULL;
+ GChecksum *ret_checksum = NULL;
+ guint32 metadata_len;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read, bytes_written;
+ char buf[8192];
+ int temp_fd = -1;
+
+ file = ot_util_new_file_for_path (path);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata length");
+ goto out;
+ }
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != metadata_len)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata");
+ goto out;
+ }
+
+ metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE, NULL, NULL);
+
+ g_variant_get (metadata, "(uuuu a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ temp_path = g_build_filename (priv->path, "tmp-packfile-XXXXXX");
+ temp_file = ot_util_new_file_for_path (temp_path);
+
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG (mode))
+ {
+ temp_fd = g_mkstemp (temp_path);
+ if (temp_fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ close (temp_fd);
+ temp_fd = -1;
+ temp_out = g_file_replace (temp_file, NULL, FALSE, 0, NULL, error);
+ if (!temp_out)
+ goto out;
+
+ do
+ {
+ if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+ goto out;
+ g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+ if (!g_output_stream_write_all ((GOutputStream*)temp_out, buf, bytes_read, &bytes_written, NULL, error))
+ goto out;
+ }
+ while (bytes_read > 0);
+
+ if (!g_output_stream_close ((GOutputStream*)temp_out, NULL, error))
+ goto out;
+ }
+ else if (S_ISLNK (mode))
+ {
+ g_assert (sizeof (buf) > PATH_MAX);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+ goto out;
+ buf[bytes_read] = '\0';
+ if (symlink (buf, temp_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISCHR (mode) || S_ISBLK (mode))
+ {
+ guint32 dev;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading device id");
+ goto out;
+ }
+ dev = GUINT32_FROM_BE (dev);
+ if (mknod (temp_path, mode, dev) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; invalid mode %u", mode);
+ goto out;
+ }
+
+ if (!S_ISLNK (mode))
+ {
+ if (chmod (temp_path, mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (!ostree_set_xattrs (temp_path, xattrs, error))
+ goto out;
+
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ *out_filename = temp_path;
+ temp_path = NULL;
+ out:
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_free (metadata_buf);
+ if (temp_path)
+ (void) unlink (temp_path);
+ g_free (temp_path);
+ g_clear_object (&file);
+ g_clear_object (&in);
+ g_clear_object (&temp_file);
+ g_clear_object (&temp_out);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *tempfile = NULL;
+ GChecksum *checksum = NULL;
+ struct stat stbuf;
+ gboolean did_exist;
+
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ {
+ if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, error))
+ goto out;
+ }
+ else
+ {
+ if (!unpack_and_checksum_packfile (self, path, &tempfile, &checksum, error))
+ goto out;
+
+ }
+
+ if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted object %s (actual checksum is %s)",
+ expected_checksum, g_checksum_get_string (checksum));
+ goto out;
+ }
+
+ if (!ostree_repo_store_object_trusted (self, tempfile ? tempfile : path,
+ expected_checksum,
+ objtype,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (tempfile)
+ (void) unlink (tempfile);
+ g_free (tempfile);
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
typedef struct _ParsedTreeData ParsedTreeData;
typedef struct _ParsedDirectoryData ParsedDirectoryData;
@@ -839,6 +1200,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 +1284,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 +1729,18 @@ 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)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ 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,
@@ -1377,7 +1752,6 @@ commit_parsed_tree (OstreeRepo *self,
GChecksum **out_commit,
GError **error)
{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
gboolean ret = FALSE;
GChecksum *root_checksum = NULL;
GChecksum *ret_commit = NULL;
@@ -1403,7 +1777,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;
@@ -1648,7 +2022,8 @@ iter_object_dir (OstreeRepo *self,
if (type != G_FILE_TYPE_DIRECTORY
&& (g_str_has_suffix (name, ".meta")
- || g_str_has_suffix (name, ".file")))
+ || g_str_has_suffix (name, ".file")
+ || g_str_has_suffix (name, ".packfile")))
{
char *dot;
char *path;
@@ -1789,6 +2164,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..15d5036 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -53,17 +53,50 @@ OstreeRepo* ostree_repo_new (const char *path);
gboolean ostree_repo_check (OstreeRepo *self, GError **error);
+const char * ostree_repo_get_path (OstreeRepo *self);
+
+gboolean ostree_repo_is_archive (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);
+ const char *path,
+ gboolean ignore_exists,
+ gboolean force,
+ GError **error);
+
+gboolean ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ 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/ostree-http-backend.c b/src/ostree-http-backend.c
new file mode 100644
index 0000000..ebde526
--- /dev/null
+++ b/src/ostree-http-backend.c
@@ -0,0 +1,45 @@
+/* -*- 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 <libsoup/soup-gnome.h>
+#include <ostree.h>
+
+#include <string.h>
+
+int
+main (int argc,
+ char **argv)
+{
+ const char *repo_root;
+
+ g_type_init ();
+
+ repo_root = g_getenv ("OSTREE_REPO_PREFIX");
+ if (!repo_root)
+ {
+ g_printerr ("OSTREE_REPO_PREFIX not set\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/ot-builtin-fsck.c b/src/ot-builtin-fsck.c
index 766d17f..3a21752 100644
--- a/src/ot-builtin-fsck.c
+++ b/src/ot-builtin-fsck.c
@@ -39,6 +39,82 @@ typedef struct {
guint n_objects;
} HtFsckData;
+static gboolean
+checksum_packed_file (HtFsckData *data,
+ const char *path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *ret_checksum = NULL;
+ GFile *file = NULL;
+ char *metadata_buf = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GFileInputStream *in = NULL;
+ guint32 metadata_len;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read;
+ char buf[8192];
+
+ file = ot_util_new_file_for_path (path);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+
+ metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE, NULL, NULL);
+
+ g_variant_get (metadata, "(uuuu a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ do
+ {
+ if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+ goto out;
+ g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+ }
+ while (bytes_read > 0);
+
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_free (metadata_buf);
+ g_clear_object (&file);
+ g_clear_object (&in);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+
static void
object_iter_callback (OstreeRepo *repo,
const char *path,
@@ -53,25 +129,46 @@ object_iter_callback (OstreeRepo *repo,
char *checksum_prefix = NULL;
char *checksum_string = NULL;
char *filename_checksum = NULL;
+ gboolean packed = FALSE;
+ OstreeObjectType objtype;
char *dot;
- dirname = g_path_get_dirname (path);
- checksum_prefix = g_path_get_basename (dirname);
-
/* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
if (nlinks < 2 && !quiet)
g_printerr ("note: floating object: %s\n", path); */
- if (!ostree_stat_and_checksum_file (-1, path, &checksum, &stbuf, &error))
- goto out;
+ if (g_str_has_suffix (path, ".meta"))
+ objtype = OSTREE_OBJECT_TYPE_META;
+ else if (g_str_has_suffix (path, ".file"))
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ else if (g_str_has_suffix (path, ".packfile"))
+ {
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ packed = TRUE;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ if (!checksum_packed_file (data, path, &checksum, &error))
+ goto out;
+ }
+ else
+ {
+ if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
+ goto out;
+ }
filename_checksum = g_strdup (g_file_info_get_name (file_info));
dot = strrchr (filename_checksum, '.');
g_assert (dot != NULL);
*dot = '\0';
-
+
+ dirname = g_path_get_dirname (path);
+ checksum_prefix = g_path_get_basename (dirname);
checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
-
+
if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
{
g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
diff --git a/src/ot-builtin-init.c b/src/ot-builtin-init.c
index ef4530b..d16b57c 100644
--- a/src/ot-builtin-init.c
+++ b/src/ot-builtin-init.c
@@ -27,8 +27,11 @@
#include <glib/gi18n.h>
static char *repo_path;
+static gboolean archive;
+
static GOptionEntry options[] = {
{ "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+ { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
{ NULL }
};
@@ -44,6 +47,7 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
GFile *repodir = NULL;
GFile *child = NULL;
GFile *grandchild = NULL;
+ GString *config_data = NULL;
context = g_option_context_new ("- Initialize a new empty repository");
g_option_context_add_main_entries (context, options, NULL);
@@ -54,12 +58,15 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
if (repo_path == NULL)
repo_path = ".";
- repodir = g_file_new_for_path (repo_path);
+ repodir = ot_util_new_file_for_path (repo_path);
child = g_file_get_child (repodir, "config");
+
+ config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
+ g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
if (!g_file_replace_contents (child,
- DEFAULT_CONFIG_CONTENTS,
- strlen (DEFAULT_CONFIG_CONTENTS),
+ config_data->str,
+ config_data->len,
NULL, FALSE, 0, NULL,
NULL, error))
goto out;
@@ -73,18 +80,20 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
child = g_file_get_child (repodir, "refs");
if (!g_file_make_directory (child, NULL, error))
goto out;
+
grandchild = g_file_get_child (child, "heads");
if (!g_file_make_directory (grandchild, NULL, error))
goto out;
- g_clear_object (&child);
g_clear_object (&grandchild);
- child = g_file_get_child (repodir, "tags");
- if (!g_file_make_directory (child, NULL, error))
+ grandchild = g_file_get_child (child, "remotes");
+ if (!g_file_make_directory (grandchild, NULL, error))
goto out;
+ g_clear_object (&grandchild);
+
g_clear_object (&child);
- child = g_file_get_child (repodir, "remotes");
+ child = g_file_get_child (repodir, "tags");
if (!g_file_make_directory (child, NULL, error))
goto out;
g_clear_object (&child);
@@ -93,6 +102,8 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
out:
if (context)
g_option_context_free (context);
+ if (config_data)
+ g_string_free (config_data, TRUE);
g_clear_object (&repodir);
g_clear_object (&child);
g_clear_object (&grandchild);
diff --git a/src/ot-builtin-pull.c b/src/ot-builtin-pull.c
new file mode 100644
index 0000000..a9ba6e0
--- /dev/null
+++ b/src/ot-builtin-pull.c
@@ -0,0 +1,365 @@
+/* -*- 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 = ot_util_new_file_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;
+
+ objpath = ostree_get_relative_object_path (object, objtype, TRUE);
+ 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, obj_uri, &filename, error))
+ goto out;
+
+ if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (obj_uri)
+ soup_uri_free (obj_uri);
+ if (filename)
+ (void) unlink (filename);
+ g_free (filename);
+ g_free (objpath);
+ g_free (relpath);
+ return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ const char *rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *tree = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ OstreeSerializedVariantType metatype;
+ gboolean did_exist;
+ int i, n;
+
+ 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 (tree, 2, "@a(ss)", &files_variant);
+ g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
+
+ 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, "(ss)", &filename, &checksum);
+
+ if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
+ goto out;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ const char *dirname;
+ const char *tree_checksum;
+ const char *meta_checksum;
+
+ g_variant_get_child (dirs_variant, i, "(sss)",
+ &dirname, &tree_checksum, &meta_checksum);
+
+ if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
+ goto out;
+
+ if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (tree)
+ g_variant_unref (tree);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ 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",
+ rev, 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, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!store_tree_recurse (repo, soup, base_uri, 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;
+ 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;
+
+ 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;
+
+ rev = ot_util_get_file_contents_utf8 (temppath, error);
+ if (!rev)
+ goto out;
+ g_strchomp (rev);
+
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+
+ if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
+ goto out;
+
+ 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);
+ g_clear_object (&repo);
+ g_clear_object (&soup);
+ 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..6beba04 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -20,7 +20,13 @@
TESTS = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-all:
+all: tmpdir-lifecycle run-apache
+
+tmpdir-lifecycle: tmpdir-lifecycle.c Makefile
+ gcc $(CFLAGS) `pkg-config --cflags --libs gio-unix-2.0` -o $@ $<
+
+run-apache: run-apache.c Makefile
+ gcc $(CFLAGS) `pkg-config --cflags --libs gio-unix-2.0` -o $@ $<
check:
@for test in $(TESTS); do \
diff --git a/tests/libtest.sh b/tests/libtest.sh
index cedf537..9601ec2 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,53 @@ setup_test_repository2 () {
ostree fsck -q $ot_repo
}
+setup_fake_remote_repo1() {
+ oldpwd=`pwd`
+ mkdir ostree-srv
+ cd ostree-srv
+ mkdir gnomerepo
+ ostree init --archive --repo=gnomerepo
+ mkdir gnomerepo-files
+ cd gnomerepo-files
+ echo first > firstfile
+ mkdir baz
+ echo moo > baz/cow
+ echo alien > baz/saucer
+ find | grep -v '^\.$' | ostree commit --repo=${test_tmpdir}/ostree-srv/gnomerepo -b main -s "A remote commit" -m "Some Commit body" --from-stdin
+ mkdir baz/deeper
+ ostree commit --repo=${test_tmpdir}/ostree-srv/gnomerepo -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}/ostree-srv/gnomerepo -b main -s "The rest" --from-stdin
+ cd ..
+ rm -rf gnomerepo-files
+
+ cd ${test_tmpdir}
+ mkdir ${test_tmpdir}/httpd
+ cd httpd
+ cp $(command -v ostree-http-backend) .
+ chmod a+x ostree-http-backend
+ cat >httpd.conf <<EOF
+ServerRoot ${test_tmpdir}/httpd
+PidFile pid
+LogLevel crit
+ErrorLog log
+LockFile lock
+ServerName localhost
+
+LoadModule alias_module modules/mod_alias.so
+LoadModule cgi_module modules/mod_cgi.so
+LoadModule env_module modules/mod_env.so
+
+StartServers 1
+
+# SetEnv OSTREE_REPO_PREFIX ${test_tmpdir}/ostree-srv
+Alias /ostree/ ${test_tmpdir}/ostree-srv/
+# ScriptAlias /ostree/ ${test_tmpdir}/httpd/ostree-http-backend/
+EOF
+ ${SRCDIR}/tmpdir-lifecycle ${SRCDIR}/run-apache `pwd`/httpd.conf ${test_tmpdir}/httpd-address
+ cd ${oldpwd}
+}
+
trap 'die' EXIT
diff --git a/tests/run-apache.c b/tests/run-apache.c
new file mode 100644
index 0000000..b5f7e5c
--- /dev/null
+++ b/tests/run-apache.c
@@ -0,0 +1,167 @@
+/* -*- 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 <gio/gio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+
+/* Taken from gnome-user-share src/httpd.c under the GPLv2 */
+static int
+get_port (void)
+{
+ int sock;
+ int saved_errno;
+ struct sockaddr_in addr;
+ int reuse;
+ socklen_t len;
+
+ sock = socket (PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ return -1;
+ }
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ reuse = 1;
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
+ if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
+ {
+ saved_errno = errno;
+ close (sock);
+ errno = saved_errno;
+ return -1;
+ }
+
+ len = sizeof (addr);
+ if (getsockname (sock, (struct sockaddr *)&addr, &len) == -1)
+ {
+ saved_errno = errno;
+ close (sock);
+ errno = saved_errno;
+ return -1;
+ }
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+ /* XXX This exposes a potential race condition, but without this,
+ * httpd will not start on the above listed platforms due to the fact
+ * that SO_REUSEADDR is also needed when Apache binds to the listening
+ * socket. At this time, Apache does not support that socket option.
+ */
+ close (sock);
+#endif
+ return ntohs (addr.sin_port);
+}
+
+static const char *known_httpd_modules_locations [] = {
+ "/usr/libexec/apache2",
+ "/usr/lib/apache2/modules",
+ "/usr/lib64/httpd/modules",
+ "/usr/lib/httpd/modules",
+ NULL
+};
+
+static gchar*
+get_httpd_modules_path ()
+{
+ int i;
+
+ for (i = 0; known_httpd_modules_locations[i]; i++)
+ {
+ if (g_file_test (known_httpd_modules_locations[i], G_FILE_TEST_IS_EXECUTABLE)
+ && g_file_test (known_httpd_modules_locations[i], G_FILE_TEST_IS_DIR))
+ {
+ return g_strdup (known_httpd_modules_locations[i]);
+ }
+ }
+ return NULL;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ int port;
+ char *listen;
+ char *address_string;
+ GError *error = NULL;
+ GPtrArray *httpd_argv;
+ char *modules;
+
+ if (argc != 3)
+ {
+ fprintf (stderr, "usage: run-apache CONF PORTFILE");
+ return 1;
+ }
+
+ g_type_init ();
+
+ port = get_port ();
+ if (port == -1)
+ {
+ perror ("Failed to bind port");
+ return 1;
+ }
+
+ httpd_argv = g_ptr_array_new ();
+ g_ptr_array_add (httpd_argv, "httpd");
+ g_ptr_array_add (httpd_argv, "-f");
+ g_ptr_array_add (httpd_argv, argv[1]);
+ g_ptr_array_add (httpd_argv, "-C");
+ listen = g_strdup_printf ("Listen 127.0.0.1:%d", port);
+ g_ptr_array_add (httpd_argv, listen);
+ g_ptr_array_add (httpd_argv, NULL);
+
+ address_string = g_strdup_printf ("http://127.0.0.1:%d\n", port);
+
+ if (!g_file_set_contents (argv[2], address_string, -1, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return 1;
+ }
+
+ setenv ("LANG", "C", 1);
+ modules = get_httpd_modules_path ();
+ if (modules == NULL)
+ {
+ g_printerr ("Failed to find httpd modules\n");
+ return 1;
+ }
+ if (symlink (modules, "modules") < 0)
+ {
+ perror ("failed to make modules symlink");
+ return 1;
+ }
+ execvp ("httpd", (char**)httpd_argv->pdata);
+ perror ("Failed to run httpd");
+ return 1;
+}
diff --git a/tests/t0012-pull.sh b/tests/t0012-pull.sh
new file mode 100755
index 0000000..fe70782
--- /dev/null
+++ b/tests/t0012-pull.sh
@@ -0,0 +1,34 @@
+#!/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
+set -x
+
+. 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 httpd-address)/ostree/gnomerepo
+ostree pull --repo=repo origin main
+echo "ok pull"
diff --git a/tests/t0013-commit-archive.sh b/tests/t0013-commit-archive.sh
new file mode 100755
index 0000000..7f6ffc9
--- /dev/null
+++ b/tests/t0013-commit-archive.sh
@@ -0,0 +1,42 @@
+#!/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..4'
+
+mkdir files
+cd files
+echo first > firstfile
+echo second > secondfile
+ln -s foo bar
+
+mkdir ../repo
+repo="--repo=../repo"
+ostree init --archive $repo
+echo 'ok init'
+ostree commit $repo -b test -s "Test Commit 1" -m "Commit body first" --add=firstfile
+echo 'ok commit 1'
+ostree commit $repo -b test -s "Test Commit 2" -m "Commit body first" --add=secondfile --add=bar
+echo 'ok commit 2'
+ostree fsck -q $repo
+echo 'ok fsck'
diff --git a/tests/ostree-http-server.c b/tests/tmpdir-lifecycle.c
similarity index 52%
rename from tests/ostree-http-server.c
rename to tests/tmpdir-lifecycle.c
index 63edda5..95aae63 100644
--- a/tests/ostree-http-server.c
+++ b/tests/tmpdir-lifecycle.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
+ * Kill a child process when the current directory is deleted
+ *
* Copyright (C) 2011 Colin Walters <walters verbum org>
*
* This program is free software; you can redistribute it and/or modify
@@ -19,35 +21,16 @@
* Author: Colin Walters <walters verbum org>
*/
-#include <libsoup/soup-gnome.h>
-#include <sys/types.h>
+#include <gio/gio.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
-static void
-request_callback (SoupServer *server, SoupMessage *msg,
- const char *path, GHashTable *query,
- SoupClientContext *context, gpointer data)
-{
- if (msg->method == SOUP_METHOD_GET)
- {
- GFile *file;
- char *content;
- gsize len;
-
- /* Strip leading / */
- 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);
- else
- soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
- }
- else
- {
- soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
- }
-}
+struct TmpdirLifecyleData {
+ GMainLoop *loop;
+ GPid pid;
+ gboolean exited;
+};
static void
on_dir_changed (GFileMonitor *mon,
@@ -56,49 +39,65 @@ on_dir_changed (GFileMonitor *mon,
GFileMonitorEvent event,
gpointer user_data)
{
- GMainLoop *loop = user_data;
+ struct TmpdirLifecyleData *data = user_data;
if (event == G_FILE_MONITOR_EVENT_DELETED)
- g_main_loop_quit (loop);
+ g_main_loop_quit (data->loop);
+}
+
+static void
+on_child_exited (GPid pid,
+ int status,
+ gpointer user_data)
+{
+ struct TmpdirLifecyleData *data = user_data;
+
+ data->exited = TRUE;
+ g_main_loop_quit (data->loop);
}
int
main (int argc,
char **argv)
{
- SoupAddress *addr;
- SoupServer *server;
- GMainLoop *loop;
GFileMonitor *monitor;
GFile *curdir;
GError *error = NULL;
+ GPtrArray *new_argv;
+ int i;
+ GPid pid;
+ struct TmpdirLifecyleData data;
g_type_init ();
- loop = g_main_loop_new (NULL, TRUE);
+ memset (&data, 0, sizeof (data));
+
+ data.loop = g_main_loop_new (NULL, TRUE);
curdir = g_file_new_for_path (".");
monitor = g_file_monitor_directory (curdir, 0, NULL, &error);
if (!monitor)
exit (1);
g_signal_connect (monitor, "changed",
- G_CALLBACK (on_dir_changed), loop);
-
- addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
- soup_address_resolve_sync (addr, NULL);
+ G_CALLBACK (on_dir_changed), &data);
- server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
- SOUP_SERVER_ASYNC_CONTEXT, g_main_loop_get_context (loop),
- NULL);
- soup_server_add_handler (server, NULL,
- request_callback,
- NULL, NULL);
+ new_argv = g_ptr_array_new ();
+ for (i = 1; i < argc; i++)
+ g_ptr_array_add (new_argv, argv[i]);
+ g_ptr_array_add (new_argv, NULL);
- soup_server_run_async (server);
+ if (!g_spawn_async (NULL, (char**)new_argv->pdata, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return 1;
+ }
+ g_child_watch_add (pid, on_child_exited, &data);
- g_print ("http://127.0.0.1:%ld\n", (long)soup_server_get_port (server));
+ g_main_loop_run (data.loop);
- g_main_loop_run (loop);
+ if (!data.exited)
+ kill (data.pid, SIGTERM);
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]