[ostree] build: Move sources into src/ again
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] build: Move sources into src/ again
- Date: Mon, 14 Nov 2011 20:41:05 +0000 (UTC)
commit 18f0b537a45f12852e4ec6b174440cbfe7702e4d
Author: Colin Walters <walters verbum org>
Date: Mon Nov 14 15:39:38 2011 -0500
build: Move sources into src/ again
This is necessary if we want to build when srcdir == builddir,
otherwise we blow up because "ostree" is a source directory and a
binary.
Makefile-libostree.am | 24 +-
Makefile-osbuild.am | 6 +-
Makefile-ostree.am | 30 +-
Makefile-otutil.am | 24 +-
src/libostree/ostree-checkout.c | 362 ++++
src/libostree/ostree-checkout.h | 60 +
src/libostree/ostree-core.c | 856 +++++++++
src/libostree/ostree-core.h | 155 ++
src/libostree/ostree-repo-file-enumerator.c | 143 ++
src/libostree/ostree-repo-file-enumerator.h | 55 +
src/libostree/ostree-repo-file.c | 1428 +++++++++++++++
src/libostree/ostree-repo-file.h | 115 ++
src/libostree/ostree-repo.c | 1877 ++++++++++++++++++++
src/libostree/ostree-repo.h | 162 ++
libotutil/otutil.h => src/libostree/ostree.h | 10 +-
{libotutil => src/libotutil}/ot-gio-utils.c | 0
{libotutil => src/libotutil}/ot-gio-utils.h | 0
{libotutil => src/libotutil}/ot-glib-compat.c | 0
{libotutil => src/libotutil}/ot-glib-compat.h | 0
{libotutil => src/libotutil}/ot-opt-utils.c | 0
{libotutil => src/libotutil}/ot-opt-utils.h | 0
{libotutil => src/libotutil}/ot-unix-utils.c | 0
{libotutil => src/libotutil}/ot-unix-utils.h | 0
{libotutil => src/libotutil}/ot-variant-utils.c | 0
{libotutil => src/libotutil}/ot-variant-utils.h | 0
{libotutil => src/libotutil}/otutil.h | 0
{osbuild => src/osbuild}/main.c | 0
{osbuild => src/osbuild}/ob-builtin-buildone.c | 0
{osbuild => src/osbuild}/ob-builtins.h | 0
{osbuild => src/osbuild}/osbuild-raw-makeinstall.c | 0
{osbuild => src/osbuild}/ostree-buildone | 0
{osbuild => src/osbuild}/ostree-buildone-make | 0
.../ostree-buildone-makeinstall-split-artifacts | 0
{ostree => src/ostree}/main.c | 0
{ostree => src/ostree}/ot-builtin-checkout.c | 0
{ostree => src/ostree}/ot-builtin-commit.c | 0
{ostree => src/ostree}/ot-builtin-compose.c | 0
{ostree => src/ostree}/ot-builtin-diff.c | 0
{ostree => src/ostree}/ot-builtin-fsck.c | 0
{ostree => src/ostree}/ot-builtin-init.c | 0
{ostree => src/ostree}/ot-builtin-log.c | 0
{ostree => src/ostree}/ot-builtin-pull.c | 0
{ostree => src/ostree}/ot-builtin-remote.c | 0
{ostree => src/ostree}/ot-builtin-rev-parse.c | 0
{ostree => src/ostree}/ot-builtin-run-triggers.c | 0
{ostree => src/ostree}/ot-builtin-show.c | 0
{ostree => src/ostree}/ot-builtins.h | 0
47 files changed, 5259 insertions(+), 48 deletions(-)
---
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index 84737a7..091d152 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -19,17 +19,17 @@
noinst_LTLIBRARIES += libostree.la
-libostree_la_SOURCES = libostree/ostree.h \
- libostree/ostree-core.c \
- libostree/ostree-core.h \
- libostree/ostree-repo.c \
- libostree/ostree-repo.h \
- libostree/ostree-repo-file.c \
- libostree/ostree-repo-file.h \
- libostree/ostree-repo-file-enumerator.c \
- libostree/ostree-repo-file-enumerator.h \
- libostree/ostree-checkout.c \
- libostree/ostree-checkout.h \
+libostree_la_SOURCES = src/libostree/ostree.h \
+ src/libostree/ostree-core.c \
+ src/libostree/ostree-core.h \
+ src/libostree/ostree-repo.c \
+ src/libostree/ostree-repo.h \
+ src/libostree/ostree-repo-file.c \
+ src/libostree/ostree-repo-file.h \
+ src/libostree/ostree-repo-file-enumerator.c \
+ src/libostree/ostree-repo-file-enumerator.h \
+ src/libostree/ostree-checkout.c \
+ src/libostree/ostree-checkout.h \
$(NULL)
-libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
diff --git a/Makefile-osbuild.am b/Makefile-osbuild.am
index 3f2e56b..8c2b1bd 100644
--- a/Makefile-osbuild.am
+++ b/Makefile-osbuild.am
@@ -15,7 +15,7 @@
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
-bin_SCRIPTS += osbuild/ostree-buildone \
- osbuild/ostree-buildone-make \
- osbuild/ostree-buildone-makeinstall-split-artifacts \
+bin_SCRIPTS += src/osbuild/ostree-buildone \
+ src/osbuild/ostree-buildone-make \
+ src/osbuild/ostree-buildone-makeinstall-split-artifacts \
$(NULL)
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
index b39f9bd..0bb7cde 100644
--- a/Makefile-ostree.am
+++ b/Makefile-ostree.am
@@ -19,23 +19,23 @@
bin_PROGRAMS += ostree
-ostree_SOURCES = ostree/main.c \
- ostree/ot-builtins.h \
- ostree/ot-builtin-checkout.c \
- ostree/ot-builtin-commit.c \
- ostree/ot-builtin-compose.c \
- ostree/ot-builtin-diff.c \
- ostree/ot-builtin-fsck.c \
- ostree/ot-builtin-init.c \
- ostree/ot-builtin-log.c \
- ostree/ot-builtin-run-triggers.c \
- ostree/ot-builtin-remote.c \
- ostree/ot-builtin-rev-parse.c \
- ostree/ot-builtin-show.c \
+ostree_SOURCES = src/ostree/main.c \
+ src/ostree/ot-builtins.h \
+ src/ostree/ot-builtin-checkout.c \
+ src/ostree/ot-builtin-commit.c \
+ src/ostree/ot-builtin-compose.c \
+ src/ostree/ot-builtin-diff.c \
+ src/ostree/ot-builtin-fsck.c \
+ src/ostree/ot-builtin-init.c \
+ src/ostree/ot-builtin-log.c \
+ src/ostree/ot-builtin-run-triggers.c \
+ src/ostree/ot-builtin-remote.c \
+ src/ostree/ot-builtin-rev-parse.c \
+ src/ostree/ot-builtin-show.c \
$(NULL)
if USE_LIBSOUP_GNOME
-ostree_SOURCES += ostree/ot-builtin-pull.c
+ostree_SOURCES += src/ostree/ot-builtin-pull.c
endif
-ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -I$(srcdir)/ostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
diff --git a/Makefile-otutil.am b/Makefile-otutil.am
index ff729bd..6d4e492 100644
--- a/Makefile-otutil.am
+++ b/Makefile-otutil.am
@@ -20,17 +20,17 @@
noinst_LTLIBRARIES += libotutil.la
libotutil_la_SOURCES = \
- libotutil/ot-opt-utils.c \
- libotutil/ot-opt-utils.h \
- libotutil/ot-unix-utils.c \
- libotutil/ot-unix-utils.h \
- libotutil/ot-variant-utils.c \
- libotutil/ot-variant-utils.h \
- libotutil/ot-gio-utils.c \
- libotutil/ot-gio-utils.h \
- libotutil/ot-glib-compat.c \
- libotutil/ot-glib-compat.h \
- libotutil/otutil.h \
+ src/libotutil/ot-opt-utils.c \
+ src/libotutil/ot-opt-utils.h \
+ src/libotutil/ot-unix-utils.c \
+ src/libotutil/ot-unix-utils.h \
+ src/libotutil/ot-variant-utils.c \
+ src/libotutil/ot-variant-utils.h \
+ src/libotutil/ot-gio-utils.c \
+ src/libotutil/ot-gio-utils.h \
+ src/libotutil/ot-glib-compat.c \
+ src/libotutil/ot-glib-compat.h \
+ src/libotutil/otutil.h \
$(NULL)
-libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
+libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
diff --git a/src/libostree/ostree-checkout.c b/src/libostree/ostree-checkout.c
new file mode 100644
index 0000000..0d58498
--- /dev/null
+++ b/src/libostree/ostree-checkout.c
@@ -0,0 +1,362 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+enum {
+ PROP_0,
+
+ PROP_REPO,
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
+
+typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
+
+struct _OstreeCheckoutPrivate {
+ OstreeRepo *repo;
+ char *path;
+};
+
+static void
+ostree_checkout_finalize (GObject *object)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo);
+
+ G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
+}
+
+static void
+ostree_checkout_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ case PROP_REPO:
+ priv->repo = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_checkout_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ case PROP_REPO:
+ g_value_set_object (value, priv->repo);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_checkout_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeCheckoutPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ return object;
+}
+
+static void
+ostree_checkout_class_init (OstreeCheckoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
+
+ object_class->constructor = ostree_checkout_constructor;
+ object_class->get_property = ostree_checkout_get_property;
+ object_class->set_property = ostree_checkout_set_property;
+ object_class->finalize = ostree_checkout_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path", "", "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_REPO,
+ g_param_spec_object ("repo", "", "",
+ OSTREE_TYPE_REPO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_checkout_init (OstreeCheckout *self)
+{
+}
+
+OstreeCheckout*
+ostree_checkout_new (OstreeRepo *repo,
+ const char *path)
+{
+ return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
+}
+
+static gboolean
+executable_exists_in_checkout (const char *path,
+ const char *executable)
+{
+ int i;
+ const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
+
+ for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
+ {
+ char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
+ gboolean exists;
+
+ exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
+ g_free (possible_path);
+
+ if (exists)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+run_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ gboolean requires_chroot,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *path = NULL;
+ char *temp_path = NULL;
+ char *rel_temp_path = NULL;
+ GFile *temp_copy = NULL;
+ char *basename = NULL;
+ GPtrArray *args = NULL;
+ int estatus;
+
+ path = g_file_get_path (trigger);
+ basename = g_path_get_basename (path);
+
+ args = g_ptr_array_new ();
+
+ if (requires_chroot)
+ {
+ temp_path = g_build_filename (priv->path, basename, NULL);
+ rel_temp_path = g_strconcat ("./", basename, NULL);
+ temp_copy = ot_util_new_file_for_path (temp_path);
+
+ if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
+ goto out;
+
+ g_ptr_array_add (args, "chroot");
+ g_ptr_array_add (args, ".");
+ g_ptr_array_add (args, rel_temp_path);
+ g_ptr_array_add (args, NULL);
+ }
+ else
+ {
+ g_ptr_array_add (args, path);
+ g_ptr_array_add (args, NULL);
+ }
+
+ g_print ("Running trigger: %s\n", path);
+ if (!g_spawn_sync (priv->path,
+ (char**)args->pdata,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, NULL,
+ &estatus,
+ error))
+ {
+ g_prefix_error (error, "Failed to run trigger %s: ", basename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (requires_chroot && temp_path)
+ (void)unlink (temp_path);
+
+ g_free (path);
+ g_free (basename);
+ g_free (temp_path);
+ g_free (rel_temp_path);
+ g_clear_object (&temp_copy);
+ if (args)
+ g_ptr_array_free (args, TRUE);
+ return ret;
+}
+
+static gboolean
+check_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GInputStream *instream = NULL;
+ GDataInputStream *datain = NULL;
+ GError *temp_error = NULL;
+ char *line;
+ gsize len;
+ gboolean requires_chroot = TRUE;
+ gboolean matches = FALSE;
+
+ instream = (GInputStream*)g_file_read (trigger, NULL, error);
+ if (!instream)
+ goto out;
+ datain = g_data_input_stream_new (instream);
+
+ while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
+ {
+ if (g_str_has_prefix (line, "# IfExecutable: "))
+ {
+ char *executable = g_strdup (line + strlen ("# IfExecutable: "));
+ g_strchomp (executable);
+ matches = executable_exists_in_checkout (priv->path, executable);
+ g_free (executable);
+ }
+
+ g_free (line);
+ }
+ if (line == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (matches)
+ {
+ if (!run_trigger (self, trigger, requires_chroot, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&instream);
+ g_clear_object (&datain);
+ return ret;
+}
+
+gboolean
+ostree_checkout_run_triggers (OstreeCheckout *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ char *triggerdir_path = NULL;
+ GFile *triggerdir = NULL;
+ GFileInfo *file_info = NULL;
+ GFileEnumerator *enumerator = NULL;
+
+ triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
+ triggerdir = ot_util_new_file_for_path (triggerdir_path);
+
+ enumerator = g_file_enumerate_children (triggerdir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ char *child_path = NULL;
+ GFile *child = NULL;
+ gboolean success;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
+ {
+ child_path = g_build_filename (triggerdir_path, name, NULL);
+ child = ot_util_new_file_for_path (child_path);
+
+ success = check_trigger (self, child, error);
+ }
+ else
+ success = TRUE;
+
+ g_object_unref (file_info);
+ g_free (child_path);
+ g_clear_object (&child);
+ if (!success)
+ goto out;
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (triggerdir_path);
+ g_clear_object (&triggerdir);
+ g_clear_object (&enumerator);
+ return ret;
+}
diff --git a/src/libostree/ostree-checkout.h b/src/libostree/ostree-checkout.h
new file mode 100644
index 0000000..042cdc8
--- /dev/null
+++ b/src/libostree/ostree-checkout.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_CHECKOUT
+#define _OSTREE_CHECKOUT
+
+#include <ostree-repo.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
+#define OSTREE_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
+#define OSTREE_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+#define OSTREE_IS_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_IS_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_CHECKOUT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+
+typedef struct {
+ GObject parent;
+} OstreeCheckout;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeCheckoutClass;
+
+GType ostree_checkout_get_type (void);
+
+OstreeCheckout* ostree_checkout_new (OstreeRepo *repo,
+ const char *path);
+
+gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_CHECKOUT */
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
new file mode 100644
index 0000000..ca0fb1f
--- /dev/null
+++ b/src/libostree/ostree-core.c
@@ -0,0 +1,856 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#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;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
+{
+ 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 *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+ char *p;
+ GSList *xattrs = NULL;
+ GSList *iter;
+ GString *result;
+
+ result = g_string_new (0);
+
+ p = xattr_string;
+ while (p < xattr_string+len)
+ {
+ xattrs = g_slist_prepend (xattrs, p);
+ p += strlen (p) + 1;
+ }
+
+ xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+ for (iter = xattrs; iter; iter = iter->next)
+ g_string_append (result, iter->data);
+
+ g_slist_free (xattrs);
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+ const char *xattrs,
+ size_t len,
+ GVariantBuilder *builder,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ const char *p;
+
+ p = xattrs;
+ while (p < xattrs+len)
+ {
+ ssize_t bytes_read;
+ char *buf;
+
+ bytes_read = lgetxattr (path, p, NULL, 0);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ if (bytes_read == 0)
+ continue;
+
+ buf = g_malloc (bytes_read);
+ if (lgetxattr (path, p, buf, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (buf);
+ goto out;
+ }
+
+ g_variant_builder_add (builder, "(@ay ay)",
+ g_variant_new_bytestring (p),
+ g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+ buf, bytes_read, FALSE, g_free, buf));
+
+ p = p + strlen (p) + 1;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+GVariant *
+ostree_get_xattrs_for_path (const char *path,
+ GError **error)
+{
+ GVariant *ret = NULL;
+ GVariantBuilder builder;
+ char *xattr_names = NULL;
+ char *xattr_names_canonical = NULL;
+ ssize_t bytes_read;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+
+ bytes_read = llistxattr (path, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattr_names = g_malloc (bytes_read);
+ if (llistxattr (path, xattr_names, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+
+ if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
+ goto out;
+ }
+
+ ret = g_variant_builder_end (&builder);
+ g_variant_ref_sink (ret);
+ out:
+ if (!ret)
+ g_variant_builder_clear (&builder);
+ g_free (xattr_names);
+ g_free (xattr_names_canonical);
+ return ret;
+}
+
+gboolean
+ostree_stat_and_checksum_file (int dir_fd, const char *path,
+ OstreeObjectType objtype,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error)
+{
+ GChecksum *content_sha256 = NULL;
+ GChecksum *content_and_meta_sha256 = NULL;
+ char *stat_string = NULL;
+ ssize_t bytes_read;
+ GVariant *xattrs = NULL;
+ int fd = -1;
+ DIR *temp_dir = NULL;
+ char *basename = NULL;
+ gboolean ret = FALSE;
+ char *symlink_target = NULL;
+ char *device_id = NULL;
+ struct stat stbuf;
+
+ basename = g_path_get_basename (path);
+
+ if (dir_fd == -1)
+ {
+ char *dirname = g_path_get_dirname (path);
+ temp_dir = opendir (dirname);
+ if (temp_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (dirname);
+ }
+ g_free (dirname);
+ dir_fd = dirfd (temp_dir);
+ }
+
+ if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ fd = ot_util_open_file_read_at (dir_fd, basename, error);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ 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);
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ guint8 buf[8192];
+
+ while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+ g_checksum_update (content_sha256, buf, bytes_read);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ 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)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
+ }
+ 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));
+ }
+ else if (S_ISFIFO(stbuf.st_mode))
+ {
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unsupported file '%s' (must be regular, symbolic link, fifo, or character/block device)",
+ path);
+ goto out;
+ }
+
+ content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+ 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;
+ ret = TRUE;
+ out:
+ if (fd >= 0)
+ close (fd);
+ if (temp_dir != NULL)
+ closedir (temp_dir);
+ g_free (symlink_target);
+ g_free (basename);
+ g_free (stat_string);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (content_sha256)
+ g_checksum_free (content_sha256);
+ return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GCancellable *cancellable, 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)
+{
+ GFile *pathf = NULL;
+ gboolean ret = FALSE;
+ GVariant *ret_variant = NULL;
+ GVariant *container = NULL;
+ guint32 ret_type;
+
+ pathf = ot_util_new_file_for_path (path);
+ if (!ot_util_variant_map (pathf, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+ &container, error))
+ goto out;
+
+ g_variant_get (container, "(uv)",
+ &ret_type, &ret_variant);
+ ret_type = GUINT32_FROM_BE (ret_type);
+ 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;
+ }
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = ot_util_variant_take_ref (ret_variant);
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ if (container != NULL)
+ g_variant_unref (container);
+ g_clear_object (&pathf);
+ 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 if (S_ISFIFO (mode))
+ {
+ object_size = 0;
+ }
+ 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 if (S_ISFIFO (mode))
+ {
+ }
+ 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;
+}
+
+static gboolean
+splice_and_checksum (GOutputStream *out,
+ GInputStream *in,
+ GChecksum *checksum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (checksum != NULL)
+ {
+ gsize bytes_read, bytes_written;
+ char buf[4096];
+ do
+ {
+ if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
+ goto out;
+ if (checksum)
+ g_checksum_update (checksum, (guint8*)buf, bytes_read);
+ if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
+ goto out;
+ }
+ while (bytes_read > 0);
+ }
+ else
+ {
+ if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+unpack_meta (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ GFileInputStream *in = NULL;
+ GChecksum *ret_checksum = NULL;
+ GFileOutputStream *out = NULL;
+
+ file = ot_util_new_file_for_path (path);
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ return ret;
+}
+
+gboolean
+ostree_parse_packed_file (GFile *file,
+ GVariant **out_metadata,
+ GInputStream **out_content,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *metadata_buf = NULL;
+ GVariant *ret_metadata = NULL;
+ GFileInputStream *in = NULL;
+ guint32 metadata_len;
+ gsize bytes_read;
+
+ 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);
+ if (metadata_len > OSTREE_MAX_METADATA_SIZE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; metadata length %u is larger than maximum %u",
+ metadata_len, OSTREE_MAX_METADATA_SIZE);
+ goto out;
+ }
+ 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;
+ }
+
+ ret_metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE,
+ (GDestroyNotify)g_free,
+ metadata_buf);
+ metadata_buf = NULL;
+
+ ret = TRUE;
+ *out_metadata = ret_metadata;
+ ret_metadata = NULL;
+ *out_content = (GInputStream*)in;
+ in = NULL;
+ out:
+ g_clear_object (&in);
+ if (ret_metadata)
+ g_variant_unref (ret_metadata);
+ return ret;
+}
+
+static gboolean
+unpack_file (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GInputStream *in = NULL;
+ GFileOutputStream *out = NULL;
+ GChecksum *ret_checksum = NULL;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read;
+
+ file = ot_util_new_file_for_path (path);
+
+ if (!ostree_parse_packed_file (file, &metadata, &in, NULL, error))
+ goto out;
+
+ 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);
+
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG (mode))
+ {
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+ }
+ else if (S_ISLNK (mode))
+ {
+ char target[PATH_MAX+1];
+
+ if (!g_input_stream_read_all (in, target, sizeof(target)-1, &bytes_read, NULL, error))
+ goto out;
+ target[bytes_read] = '\0';
+ if (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
+ if (symlink (target, dest_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 (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 (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)&dev, 4);
+ if (mknod (dest_path, mode, dev) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISFIFO (mode))
+ {
+ if (mkfifo (dest_path, mode) < 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 (dest_path, mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (!ostree_set_xattrs (dest_path, xattrs, NULL, error))
+ goto out;
+
+ if (ret_checksum)
+ {
+ 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;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ g_clear_object (&out);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+gboolean
+ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ return unpack_meta (path, dest_path, out_checksum, error);
+ else
+ return unpack_file (path, dest_path, out_checksum, error);
+}
+
+
+
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
new file mode 100644
index 0000000..d67f42f
--- /dev/null
+++ b/src/libostree/ostree-core.h
@@ -0,0 +1,155 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_CORE
+#define _OSTREE_CORE
+
+#include <otutil.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_MAX_METADATA_SIZE (1 << 26)
+
+#define OSTREE_GIO_FAST_QUERYINFO "standard::name,standard::type,standard::is-symlink,standard::symlink-target,standard::is-hidden,unix::*"
+
+#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+ OSTREE_OBJECT_TYPE_FILE = 1,
+ OSTREE_OBJECT_TYPE_META = 2,
+} OstreeObjectType;
+
+typedef enum {
+ OSTREE_SERIALIZED_TREE_VARIANT = 1,
+ OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
+ OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
+ OSTREE_SERIALIZED_XATTR_VARIANT = 4
+} OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
+
+#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
+
+/*
+ * xattr objects:
+ * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
+ */
+#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
+
+#define OSTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ */
+#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
+
+#define OSTREE_TREE_VERSION 0
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
+ */
+#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
+
+#define OSTREE_COMMIT_VERSION 0
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - parent checksum (empty string for initial)
+ * s - subject
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Root tree contents
+ * s - Root tree metadata
+ */
+#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
+
+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,
+ GCancellable *cancellable, 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,
+ 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 *file,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_parse_packed_file (GFile *file,
+ GVariant **out_metadata,
+ GInputStream **out_content,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ 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-file-enumerator.c b/src/libostree/ostree-repo-file-enumerator.c
new file mode 100644
index 0000000..7858bde
--- /dev/null
+++ b/src/libostree/ostree-repo-file-enumerator.c
@@ -0,0 +1,143 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+#include <string.h>
+
+struct _OstreeRepoFileEnumerator
+{
+ GFileEnumerator parent;
+
+ OstreeRepoFile *dir;
+ char *attributes;
+ GFileQueryInfoFlags flags;
+
+ int index;
+};
+
+#define ostree_repo_file_enumerator_get_type _ostree_repo_file_enumerator_get_type
+G_DEFINE_TYPE (OstreeRepoFileEnumerator, ostree_repo_file_enumerator, G_TYPE_FILE_ENUMERATOR);
+
+static GFileInfo *ostree_repo_file_enumerator_next_file (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean ostree_repo_file_enumerator_close (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error);
+
+
+static void
+ostree_repo_file_enumerator_dispose (GObject *object)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = OSTREE_REPO_FILE_ENUMERATOR (object);
+
+ g_clear_object (&self->dir);
+ g_free (self->attributes);
+
+ if (G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose)
+ G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_enumerator_finalize (GObject *object)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = OSTREE_REPO_FILE_ENUMERATOR (object);
+ (void)self;
+
+ G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->finalize (object);
+}
+
+
+static void
+ostree_repo_file_enumerator_class_init (OstreeRepoFileEnumeratorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
+
+ gobject_class->finalize = ostree_repo_file_enumerator_finalize;
+ gobject_class->dispose = ostree_repo_file_enumerator_dispose;
+
+ enumerator_class->next_file = ostree_repo_file_enumerator_next_file;
+ enumerator_class->close_fn = ostree_repo_file_enumerator_close;
+}
+
+static void
+ostree_repo_file_enumerator_init (OstreeRepoFileEnumerator *self)
+{
+}
+
+GFileEnumerator *
+_ostree_repo_file_enumerator_new (OstreeRepoFile *dir,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE_ENUMERATOR,
+ "container", dir,
+ NULL);
+
+ self->dir = g_object_ref (dir);
+ self->attributes = g_strdup (attributes);
+ self->flags = flags;
+
+ return G_FILE_ENUMERATOR (self);
+}
+
+static GFileInfo *
+ostree_repo_file_enumerator_next_file (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFileEnumerator *self = OSTREE_REPO_FILE_ENUMERATOR (enumerator);
+ gboolean ret = FALSE;
+ GFileInfo *info = NULL;
+
+ if (!_ostree_repo_file_tree_query_child (self->dir, self->index,
+ self->attributes, self->flags,
+ &info, cancellable, error))
+ goto out;
+
+ self->index++;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ g_clear_object (&info);
+ return info;
+}
+
+static gboolean
+ostree_repo_file_enumerator_close (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return TRUE;
+}
diff --git a/src/libostree/ostree-repo-file-enumerator.h b/src/libostree/ostree-repo-file-enumerator.h
new file mode 100644
index 0000000..499a01b
--- /dev/null
+++ b/src/libostree/ostree-repo-file-enumerator.h
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_REPO_FILE_ENUMERATOR
+#define _OSTREE_REPO_FILE_ENUMERATOR
+
+#include "ostree-repo-file.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE_ENUMERATOR (_ostree_repo_file_enumerator_get_type ())
+#define OSTREE_REPO_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumerator))
+#define OSTREE_REPO_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_REPO_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+
+typedef struct _OstreeRepoFileEnumerator OstreeRepoFileEnumerator;
+typedef struct _OstreeRepoFileEnumeratorClass OstreeRepoFileEnumeratorClass;
+
+struct _OstreeRepoFileEnumeratorClass
+{
+ GFileEnumeratorClass parent_class;
+};
+
+GType _ostree_repo_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileEnumerator * _ostree_repo_file_enumerator_new (OstreeRepoFile *dir,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libostree/ostree-repo-file.c b/src/libostree/ostree-repo-file.c
new file mode 100644
index 0000000..a3d5e79
--- /dev/null
+++ b/src/libostree/ostree-repo-file.c
@@ -0,0 +1,1428 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+
+static void ostree_repo_file_file_iface_init (GFileIface *iface);
+
+static void
+tree_replace_contents (OstreeRepoFile *self,
+ GVariant *new_files,
+ GVariant *new_dirs);
+
+struct _OstreeRepoFile
+{
+ GObject parent_instance;
+
+ OstreeRepo *repo;
+
+ char *commit;
+ GError *commit_resolve_error;
+
+ OstreeRepoFile *parent;
+ int index;
+ char *name;
+
+ char *tree_contents_checksum;
+ GVariant *tree_contents;
+ char *tree_metadata_checksum;
+ GVariant *tree_metadata;
+};
+
+#define ostree_repo_file_get_type _ostree_repo_file_get_type
+G_DEFINE_TYPE_WITH_CODE (OstreeRepoFile, ostree_repo_file, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+ ostree_repo_file_file_iface_init))
+
+static void
+ostree_repo_file_finalize (GObject *object)
+{
+ OstreeRepoFile *self;
+
+ self = OSTREE_REPO_FILE (object);
+
+ if (self->tree_contents)
+ g_variant_unref (self->tree_contents);
+ if (self->tree_metadata)
+ g_variant_unref (self->tree_metadata);
+ g_free (self->tree_contents_checksum);
+ g_free (self->tree_metadata_checksum);
+ g_free (self->commit);
+ g_free (self->name);
+
+ G_OBJECT_CLASS (ostree_repo_file_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_file_dispose (GObject *object)
+{
+ OstreeRepoFile *self;
+
+ self = OSTREE_REPO_FILE (object);
+
+ g_clear_object (&self->repo);
+ g_clear_object (&self->parent);
+
+ if (G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose)
+ G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_class_init (OstreeRepoFileClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = ostree_repo_file_finalize;
+ gobject_class->dispose = ostree_repo_file_dispose;
+}
+
+static void
+ostree_repo_file_init (OstreeRepoFile *self)
+{
+ self->index = -1;
+}
+
+static gboolean
+set_error_noent (GFile *self, GError **error)
+{
+ char *path = g_file_get_path (self);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "No such file or directory: %s", path);
+ g_free (path);
+ return FALSE;
+}
+
+GFile *
+_ostree_repo_file_new_root (OstreeRepo *repo,
+ const char *commit)
+{
+ OstreeRepoFile *self;
+
+ g_return_val_if_fail (repo != NULL, NULL);
+ g_return_val_if_fail (commit != NULL, NULL);
+ g_return_val_if_fail (strlen (commit) == 64, NULL);
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (repo);
+ self->commit = g_strdup (commit);
+
+ return G_FILE (self);
+}
+
+
+GFile *
+_ostree_repo_file_new_child (OstreeRepoFile *parent,
+ const char *name)
+{
+ OstreeRepoFile *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (parent->repo);
+ self->parent = g_object_ref (parent);
+ self->name = g_strdup (name);
+
+ return G_FILE (self);
+}
+
+OstreeRepoFile *
+_ostree_repo_file_new_empty_tree (OstreeRepo *repo)
+{
+ OstreeRepoFile *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (repo);
+
+ tree_replace_contents (self, NULL, NULL);
+
+ return self;
+}
+
+static gboolean
+do_resolve_commit (OstreeRepoFile *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *commit = NULL;
+ GVariant *root_contents = NULL;
+ GVariant *root_metadata = NULL;
+ const char *tree_contents_checksum;
+ const char *tree_meta_checksum;
+
+ g_assert (self->parent == NULL);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ self->commit, &commit, error))
+ goto out;
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+ g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+ tree_contents_checksum, &root_contents,
+ error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ tree_meta_checksum, &root_metadata,
+ error))
+ goto out;
+
+ self->tree_metadata = root_metadata;
+ root_metadata = NULL;
+ self->tree_contents = root_contents;
+ root_contents = NULL;
+
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_contents)
+ g_variant_unref (root_contents);
+ return ret;
+}
+
+static gboolean
+do_resolve_nonroot (OstreeRepoFile *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *container = NULL;
+ GVariant *tree_contents = NULL;
+ GVariant *tree_metadata = NULL;
+ gboolean is_dir;
+ int i;
+
+ i = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, &container);
+
+ if (i < 0)
+ {
+ set_error_noent ((GFile*)self, error);
+ goto out;
+ }
+
+ if (is_dir)
+ {
+ const char *name;
+ const char *content_checksum;
+ const char *metadata_checksum;
+ GVariant *files_variant;
+
+ files_variant = g_variant_get_child_value (self->parent->tree_contents, 2);
+ self->index = g_variant_n_children (files_variant) + i;
+ g_variant_unref (files_variant);
+
+ g_variant_get_child (container, i, "(&s&s&s)",
+ &name, &content_checksum, &metadata_checksum);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+ content_checksum, &tree_contents,
+ error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ metadata_checksum, &tree_metadata,
+ error))
+ goto out;
+
+ self->tree_contents = tree_contents;
+ tree_contents = NULL;
+ self->tree_metadata = tree_metadata;
+ tree_metadata = NULL;
+ }
+ else
+ self->index = i;
+
+ ret = TRUE;
+ out:
+ if (container)
+ g_variant_unref (container);
+ if (tree_metadata)
+ g_variant_unref (tree_metadata);
+ if (tree_contents)
+ g_variant_unref (tree_contents);
+ return ret;
+}
+
+gboolean
+_ostree_repo_file_ensure_resolved (OstreeRepoFile *self,
+ GError **error)
+{
+ if (self->commit_resolve_error != NULL)
+ goto out;
+
+ if (self->parent == NULL)
+ {
+ if (self->tree_contents == NULL)
+ (void)do_resolve_commit (self, &(self->commit_resolve_error));
+ }
+ else if (self->index == -1)
+ {
+ (void)do_resolve_nonroot (self, &(self->commit_resolve_error));
+ }
+
+ out:
+ if (self->commit_resolve_error)
+ {
+ if (error)
+ *error = g_error_copy (self->commit_resolve_error);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+gboolean
+_ostree_repo_file_get_xattrs (OstreeRepoFile *self,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *ret_xattrs = NULL;
+ GVariant *metadata = NULL;
+ GInputStream *input = NULL;
+ GFile *local_file = NULL;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ if (self->tree_metadata)
+ ret_xattrs = g_variant_get_child_value (self->tree_metadata, 4);
+ else if (ostree_repo_is_archive (self->repo))
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+ goto out;
+ ret_xattrs = g_variant_get_child_value (metadata, 4);
+ }
+ else
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ ret_xattrs = ostree_get_xattrs_for_path (ot_gfile_get_path_cached (local_file), error);
+ }
+
+ ret = TRUE;
+ *out_xattrs = ret_xattrs;
+ ret_xattrs = NULL;
+ out:
+ if (ret_xattrs)
+ g_variant_unref (ret_xattrs);
+ if (metadata)
+ g_variant_unref (metadata);
+ g_clear_object (&input);
+ g_clear_object (&local_file);
+ return ret;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_contents (OstreeRepoFile *self)
+{
+ return self->tree_contents;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_metadata (OstreeRepoFile *self)
+{
+ return self->tree_metadata;
+}
+
+void
+_ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+ const char *checksum,
+ GVariant *metadata)
+{
+ if (self->tree_metadata)
+ g_variant_unref (self->tree_metadata);
+ self->tree_metadata = g_variant_ref (metadata);
+ g_free (self->tree_metadata_checksum);
+ self->tree_metadata_checksum = g_strdup (checksum);
+}
+
+void
+_ostree_repo_file_make_empty_tree (OstreeRepoFile *self)
+{
+ tree_replace_contents (self, NULL, NULL);
+}
+
+void
+_ostree_repo_file_tree_set_content_checksum (OstreeRepoFile *self,
+ const char *checksum)
+{
+ g_assert (self->parent == NULL);
+ g_free (self->tree_contents_checksum);
+ self->tree_contents_checksum = g_strdup (checksum);
+}
+
+const char *
+_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile *self)
+{
+ g_assert (self->parent == NULL);
+ return self->tree_contents_checksum;
+}
+
+GFile *
+_ostree_repo_file_nontree_get_local (OstreeRepoFile *self)
+{
+ const char *checksum;
+ char *path;
+ GFile *ret;
+
+ g_assert (!ostree_repo_is_archive (self->repo));
+
+ checksum = _ostree_repo_file_nontree_get_checksum (self);
+ path = ostree_repo_get_object_path (self->repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+ ret = ot_util_new_file_for_path (path);
+ g_free (path);
+
+ return ret;
+}
+
+OstreeRepo *
+_ostree_repo_file_get_repo (OstreeRepoFile *self)
+{
+ return self->repo;
+}
+
+OstreeRepoFile *
+_ostree_repo_file_get_root (OstreeRepoFile *self)
+{
+ OstreeRepoFile *parent = self;
+
+ while (parent->parent)
+ parent = parent->parent;
+ return parent;
+}
+
+const char *
+_ostree_repo_file_nontree_get_checksum (OstreeRepoFile *self)
+{
+ int n;
+ gboolean is_dir;
+
+ g_assert (self->parent);
+
+ n = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, NULL);
+ g_assert (n >= 0 && !is_dir);
+
+ return _ostree_repo_file_tree_get_child_checksum (self->parent, n);
+}
+
+const char *
+_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile *self,
+ int n)
+{
+ GVariant *files_variant;
+ const char *checksum;
+
+ g_assert (self->tree_contents);
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+ g_variant_get_child (files_variant, n, "(@s&s)", NULL, &checksum);
+
+ g_variant_unref (files_variant);
+
+ return checksum;
+}
+
+static gboolean
+ostree_repo_file_is_native (GFile *file)
+{
+ return FALSE;
+}
+
+static gboolean
+ostree_repo_file_has_uri_scheme (GFile *file,
+ const char *uri_scheme)
+{
+ return g_ascii_strcasecmp (uri_scheme, "ostree") == 0;
+}
+
+static char *
+ostree_repo_file_get_uri_scheme (GFile *file)
+{
+ return g_strdup ("ostree");
+}
+
+static char *
+ostree_repo_file_get_basename (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ return g_strdup (self->name);
+}
+
+static char *
+ostree_repo_file_get_path (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ OstreeRepoFile *parent;
+ GString *buf;
+ GSList *parents;
+ GSList *iter;
+
+ buf = g_string_new ("");
+ parents = NULL;
+
+ for (parent = self->parent; parent; parent = parent->parent)
+ parents = g_slist_prepend (parents, parent);
+
+ if (parents->next)
+ {
+ for (iter = parents->next; iter; iter = iter->next)
+ {
+ parent = iter->data;
+ g_string_append_c (buf, '/');
+ g_string_append (buf, parent->name);
+ }
+ }
+ g_string_append_c (buf, '/');
+ g_string_append (buf, self->name);
+
+ g_slist_free (parents);
+
+ return g_string_free (buf, FALSE);
+}
+
+static char *
+ostree_repo_file_get_uri (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ char *path;
+ char *uri_path;
+ char *ret;
+
+ path = g_file_get_path (file);
+ uri_path = g_filename_to_uri (path, NULL, NULL);
+ g_free (path);
+ g_assert (g_str_has_prefix (uri_path, "file://"));
+ ret = g_strconcat ("ostree://", self->commit, uri_path+strlen("file://"), NULL);
+ g_free (uri_path);
+
+ return ret;
+}
+
+static char *
+ostree_repo_file_get_parse_name (GFile *file)
+{
+ return ostree_repo_file_get_uri (file);
+}
+
+static GFile *
+ostree_repo_file_get_parent (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ return g_object_ref (self->parent);
+}
+
+static GFile *
+ostree_repo_file_dup (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->parent)
+ return _ostree_repo_file_new_child (self->parent, self->name);
+ else
+ return _ostree_repo_file_new_root (self->repo, self->commit);
+}
+
+static guint
+ostree_repo_file_hash (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->parent)
+ return g_file_hash (self->parent) + g_str_hash (self->name);
+ else
+ return g_str_hash (self->commit);
+}
+
+static gboolean
+ostree_repo_file_equal (GFile *file1,
+ GFile *file2)
+{
+ OstreeRepoFile *self1 = OSTREE_REPO_FILE (file1);
+ OstreeRepoFile *self2 = OSTREE_REPO_FILE (file2);
+
+ if (self1->parent && self2->parent)
+ {
+ return g_str_equal (self1->name, self2->name)
+ && g_file_equal ((GFile*)self1->parent, (GFile*)self2->parent);
+ }
+ else if (!self1->parent && !self2->parent)
+ {
+ return g_str_equal (self1->commit, self2->commit);
+ }
+ else
+ return FALSE;
+}
+
+static const char *
+match_prefix (const char *path,
+ const char *prefix)
+{
+ int prefix_len;
+
+ prefix_len = strlen (prefix);
+ if (strncmp (path, prefix, prefix_len) != 0)
+ return NULL;
+
+ /* Handle the case where prefix is the root, so that
+ * the IS_DIR_SEPRARATOR check below works */
+ if (prefix_len > 0 &&
+ G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
+ prefix_len--;
+
+ return path + prefix_len;
+}
+
+static gboolean
+ostree_repo_file_prefix_matches (GFile *parent,
+ GFile *descendant)
+{
+ const char *remainder;
+ char *parent_path;
+ char *descendant_path;
+
+ parent_path = g_file_get_path (parent);
+ descendant_path = g_file_get_path (descendant);
+ remainder = match_prefix (descendant_path, parent_path);
+ g_free (parent_path);
+ g_free (descendant_path);
+ if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+ return TRUE;
+ return FALSE;
+}
+
+static char *
+ostree_repo_file_get_relative_path (GFile *parent,
+ GFile *descendant)
+{
+ const char *remainder;
+ char *parent_path;
+ char *descendant_path;
+
+ parent_path = g_file_get_path (parent);
+ descendant_path = g_file_get_path (descendant);
+ remainder = match_prefix (descendant_path, parent_path);
+ g_free (parent_path);
+ g_free (descendant_path);
+
+ if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+ return g_strdup (remainder + 1);
+ return NULL;
+}
+
+static GFile *
+ostree_repo_file_resolve_relative_path (GFile *file,
+ const char *relative_path)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ OstreeRepoFile *parent;
+ char *filename;
+ const char *rest;
+ GFile *ret;
+
+ if (g_path_is_absolute (relative_path) && self->parent)
+ {
+ g_assert (*relative_path == '/');
+ return ostree_repo_file_resolve_relative_path ((GFile*)_ostree_repo_file_get_root (self),
+ relative_path+1);
+ }
+
+ rest = strchr (relative_path, '/');
+ if (rest)
+ {
+ rest += 1;
+ filename = g_strndup (relative_path, rest - relative_path);
+ }
+ else
+ filename = g_strdup (relative_path);
+
+ parent = (OstreeRepoFile*)_ostree_repo_file_new_child (self, filename);
+ g_free (filename);
+
+ if (!rest)
+ ret = (GFile*)parent;
+ else
+ {
+ ret = ostree_repo_file_resolve_relative_path ((GFile*)parent, rest);
+ g_clear_object (&parent);
+ }
+ return ret;
+}
+
+static GFileEnumerator *
+ostree_repo_file_enumerate_children (GFile *file,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ return _ostree_repo_file_enumerator_new (self,
+ attributes, flags,
+ cancellable, error);
+}
+
+static GFile *
+ostree_repo_file_get_child_for_display_name (GFile *file,
+ const char *display_name,
+ GError **error)
+{
+ return g_file_get_child (file, display_name);
+}
+
+static GFile *
+get_child_local_file (OstreeRepo *repo,
+ const char *checksum)
+{
+ char *path;
+ GFile *ret;
+
+ path = ostree_repo_get_object_path (repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+ ret = ot_util_new_file_for_path (path);
+ g_free (path);
+
+ return ret;
+}
+
+static gboolean
+query_child_info_file_nonarchive (OstreeRepo *repo,
+ const char *checksum,
+ GFileAttributeMatcher *matcher,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFileInfo *local_info = NULL;
+ GFile *local_file = NULL;
+ int i ;
+ const char *mapped_boolean[] = {
+ "standard::is-symlink"
+ };
+ const char *mapped_string[] = {
+ };
+ const char *mapped_byte_string[] = {
+ "standard::symlink-target"
+ };
+ const char *mapped_uint32[] = {
+ "standard::type",
+ "unix::device",
+ "unix::mode",
+ "unix::nlink",
+ "unix::uid",
+ "unix::gid",
+ "unix::rdev"
+ };
+ const char *mapped_uint64[] = {
+ "standard::size",
+ "standard::allocated-size",
+ "unix::inode"
+ };
+
+ if (!(g_file_attribute_matcher_matches (matcher, "unix::mode")
+ || g_file_attribute_matcher_matches (matcher, "standard::type")))
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ local_file = get_child_local_file (repo, checksum);
+ local_info = g_file_query_info (local_file,
+ OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ error);
+ if (!local_info)
+ goto out;
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_boolean); i++)
+ g_file_info_set_attribute_boolean (info, mapped_boolean[i], g_file_info_get_attribute_boolean (local_info, mapped_boolean[i]));
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_string); i++)
+ {
+ const char *string = g_file_info_get_attribute_string (local_info, mapped_string[i]);
+ if (string)
+ g_file_info_set_attribute_string (info, mapped_string[i], string);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_byte_string); i++)
+ {
+ const char *byte_string = g_file_info_get_attribute_byte_string (local_info, mapped_byte_string[i]);
+ if (byte_string)
+ g_file_info_set_attribute_byte_string (info, mapped_byte_string[i], byte_string);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_uint32); i++)
+ g_file_info_set_attribute_uint32 (info, mapped_uint32[i], g_file_info_get_attribute_uint32 (local_info, mapped_uint32[i]));
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_uint64); i++)
+ g_file_info_set_attribute_uint64 (info, mapped_uint64[i], g_file_info_get_attribute_uint64 (local_info, mapped_uint64[i]));
+
+ ret = TRUE;
+ out:
+ g_clear_object (&local_info);
+ g_clear_object (&local_file);
+ return ret;
+}
+
+static gboolean
+query_child_info_file_archive (OstreeRepo *repo,
+ const char *checksum,
+ GFileAttributeMatcher *matcher,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *local_file = NULL;
+ GVariant *metadata = NULL;
+ GInputStream *input = NULL;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ guint32 file_type;
+ gsize bytes_read;
+ char *buf = NULL;
+
+ local_file = get_child_local_file (repo, checksum);
+
+ if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+ goto out;
+
+ g_variant_get (metadata, "(uuuu a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ NULL, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ g_file_info_set_attribute_boolean (info, "standard::is-symlink",
+ S_ISLNK (mode));
+ if (S_ISLNK (mode))
+ file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+ else if (S_ISREG (mode))
+ file_type = G_FILE_TYPE_REGULAR;
+ else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode))
+ file_type = G_FILE_TYPE_SPECIAL;
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile %s: Invalid mode", checksum);
+ goto out;
+ }
+ g_file_info_set_attribute_uint32 (info, "standard::type", file_type);
+
+ g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+ g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+ g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+
+ if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ g_file_info_set_attribute_uint64 (info, "standard::size", content_len);
+ }
+ else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
+ {
+ gsize len = MIN (PATH_MAX, content_len) + 1;
+ buf = g_malloc (len);
+
+ if (!g_input_stream_read_all (input, buf, len, &bytes_read, cancellable, error))
+ goto out;
+ buf[bytes_read] = '\0';
+
+ g_file_info_set_attribute_byte_string (info, "standard::symlink-target", buf);
+ }
+ else if (file_type == G_FILE_TYPE_SPECIAL)
+ {
+ guint32 device;
+
+ if (!g_input_stream_read_all (input, &device, 4, &bytes_read, cancellable, error))
+ goto out;
+
+ device = GUINT32_FROM_BE (device);
+ g_file_info_set_attribute_uint32 (info, "unix::device", device);
+ }
+
+ ret = TRUE;
+ out:
+ g_free (buf);
+ if (metadata)
+ g_variant_unref (metadata);
+ g_clear_object (&local_file);
+ g_clear_object (&input);
+ return ret;
+}
+
+static void
+set_info_from_dirmeta (GFileInfo *info,
+ GVariant *metadata)
+{
+ guint32 version, uid, gid, mode;
+
+ g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_DIRECTORY);
+
+ /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+ g_variant_get (metadata, "(uuuu a(ayay))",
+ &version, &uid, &gid, &mode,
+ NULL);
+ version = GUINT32_FROM_BE (version);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+
+ g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+ g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+ g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+}
+
+static gboolean
+query_child_info_dir (OstreeRepo *repo,
+ const char *metadata_checksum,
+ GFileAttributeMatcher *matcher,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *metadata = NULL;
+
+ if (!g_file_attribute_matcher_matches (matcher, "unix::mode"))
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ metadata_checksum, &metadata, error))
+ goto out;
+
+ set_info_from_dirmeta (info, metadata);
+
+ ret = TRUE;
+ out:
+ if (metadata)
+ g_variant_unref (metadata);
+ return ret;
+}
+
+static gboolean
+bsearch_in_file_variant (GVariant *variant,
+ const char *name,
+ int *out_pos)
+{
+ int i, n;
+ int m;
+
+ i = 0;
+ n = g_variant_n_children (variant) - 1;
+ m = 0;
+
+ while (i <= n)
+ {
+ GVariant *child;
+ const char *cur;
+ int cmp;
+
+ m = i + ((n - i) / 2);
+
+ child = g_variant_get_child_value (variant, m);
+ g_variant_get_child (child, 0, "&s", &cur, NULL);
+
+ cmp = strcmp (cur, name);
+ if (cmp < 0)
+ i = m + 1;
+ else if (cmp > 0)
+ n = m - 1;
+ else
+ {
+ g_variant_unref (child);
+ *out_pos = m;
+ return TRUE;
+ }
+ g_variant_unref (child);
+ }
+
+ *out_pos = m;
+ return FALSE;
+}
+
+static GVariant *
+remove_variant_child (GVariant *variant,
+ int n)
+{
+ GVariantBuilder builder;
+ GVariantIter *iter;
+ int i;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (variant));
+ iter = g_variant_iter_new (variant);
+
+ i = 0;
+ while ((child = g_variant_iter_next_value (iter)) != NULL)
+ {
+ if (i != n)
+ g_variant_builder_add_value (&builder, child);
+ g_variant_unref (child);
+ }
+ g_variant_iter_free (iter);
+
+ return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+insert_variant_child (GVariant *variant,
+ int n,
+ GVariant *item)
+{
+ GVariantBuilder builder;
+ GVariantIter *iter;
+ int i;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (variant));
+ iter = g_variant_iter_new (variant);
+
+ i = 0;
+ while ((child = g_variant_iter_next_value (iter)) != NULL)
+ {
+ if (i == n)
+ g_variant_builder_add_value (&builder, item);
+ g_variant_builder_add_value (&builder, child);
+ g_variant_unref (child);
+ }
+ g_variant_iter_free (iter);
+
+ return g_variant_builder_end (&builder);
+}
+
+static void
+tree_replace_contents (OstreeRepoFile *self,
+ GVariant *new_files,
+ GVariant *new_dirs)
+{
+ guint version;
+ GVariant *metadata;
+ GVariant *tmp_files = NULL;
+ GVariant *tmp_dirs = NULL;
+
+ if (!(new_files || new_dirs) && self->tree_contents)
+ return;
+ else if (!self->tree_contents)
+ {
+ version = GUINT32_TO_BE (0);
+ metadata = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+ tmp_dirs = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+ tmp_files = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+ }
+ else
+ {
+ g_variant_get_child (self->tree_contents, 0, "u", &version);
+ metadata = g_variant_get_child_value (self->tree_contents, 1);
+ if (!new_files)
+ tmp_files = g_variant_get_child_value (self->tree_contents, 2);
+ if (!new_dirs)
+ tmp_dirs = g_variant_get_child_value (self->tree_contents, 3);
+ }
+
+ if (self->tree_contents)
+ g_variant_unref (self->tree_contents);
+ self->tree_contents = g_variant_new ("(u a{sv}@a(ss)@a(sss))", version, metadata,
+ new_files ? new_files : tmp_files,
+ new_dirs ? new_dirs : tmp_dirs);
+
+ g_variant_unref (metadata);
+ if (tmp_files)
+ g_variant_unref (tmp_files);
+ if (tmp_dirs)
+ g_variant_unref (tmp_dirs);
+}
+
+void
+_ostree_repo_file_tree_remove_child (OstreeRepoFile *self,
+ const char *name)
+{
+ int i;
+ GVariant *files_variant;
+ GVariant *new_files_variant = NULL;
+ GVariant *dirs_variant;
+ GVariant *new_dirs_variant = NULL;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ if (bsearch_in_file_variant (files_variant, name, &i))
+ {
+ new_files_variant = remove_variant_child (files_variant, i);
+ }
+ else
+ {
+ if (bsearch_in_file_variant (dirs_variant, name, &i))
+ {
+ new_dirs_variant = remove_variant_child (dirs_variant, i);
+ }
+ }
+
+ tree_replace_contents (self, new_files_variant, new_dirs_variant);
+
+ g_variant_unref (files_variant);
+ g_variant_unref (dirs_variant);
+}
+
+void
+_ostree_repo_file_tree_add_file (OstreeRepoFile *self,
+ const char *name,
+ const char *checksum)
+{
+ int n;
+ GVariant *files_variant;
+ GVariant *new_files_variant;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+ if (!bsearch_in_file_variant (files_variant, name, &n))
+ {
+ new_files_variant = insert_variant_child (files_variant, n,
+ g_variant_new ("(ss)", name, checksum));
+ g_variant_ref_sink (new_files_variant);
+ tree_replace_contents (self, new_files_variant, NULL);
+ g_variant_unref (new_files_variant);
+ }
+ g_variant_unref (files_variant);
+}
+
+void
+_ostree_repo_file_tree_add_dir (OstreeRepoFile *self,
+ const char *name,
+ const char *content_checksum,
+ const char *metadata_checksum)
+{
+ int n;
+ GVariant *dirs_variant;
+ GVariant *new_dirs_variant;
+
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ if (!bsearch_in_file_variant (dirs_variant, name, &n))
+ {
+ new_dirs_variant = insert_variant_child (dirs_variant, n,
+ g_variant_new ("(sss)", name, content_checksum,
+ metadata_checksum));
+ g_variant_ref_sink (new_dirs_variant);
+ tree_replace_contents (self, NULL, new_dirs_variant);
+ g_variant_unref (new_dirs_variant);
+ }
+ g_variant_unref (dirs_variant);
+}
+
+int
+_ostree_repo_file_tree_find_child (OstreeRepoFile *self,
+ const char *name,
+ gboolean *is_dir,
+ GVariant **out_container)
+{
+ int i;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ GVariant *ret_container = NULL;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ i = -1;
+ if (bsearch_in_file_variant (files_variant, name, &i))
+ {
+ *is_dir = FALSE;
+ ret_container = files_variant;
+ files_variant = NULL;
+ }
+ else
+ {
+ if (bsearch_in_file_variant (dirs_variant, name, &i))
+ {
+ *is_dir = TRUE;
+ ret_container = dirs_variant;
+ dirs_variant = NULL;
+ }
+ else
+ {
+ i = -1;
+ }
+ }
+ if (ret_container && out_container)
+ {
+ *out_container = ret_container;
+ ret_container = NULL;
+ }
+ if (ret_container)
+ g_variant_unref (ret_container);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return i;
+}
+
+gboolean
+_ostree_repo_file_tree_query_child (OstreeRepoFile *self,
+ int n,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GFileInfo **out_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const char *name = NULL;
+ gboolean ret = FALSE;
+ GFileInfo *ret_info = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ GVariant *tree_child_metadata = NULL;
+ GFileAttributeMatcher *matcher = NULL;
+ int c;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ matcher = g_file_attribute_matcher_new (attributes);
+
+ ret_info = g_file_info_new ();
+
+ g_assert (self->tree_contents);
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ c = g_variant_n_children (files_variant);
+ if (n < c)
+ {
+ const char *checksum;
+
+ g_variant_get_child (files_variant, n, "(&s&s)", &name, &checksum);
+
+ if (ostree_repo_is_archive (self->repo))
+ {
+ if (!query_child_info_file_archive (self->repo, checksum, matcher, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ else
+ {
+ if (!query_child_info_file_nonarchive (self->repo, checksum, matcher, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ }
+ else
+ {
+ const char *tree_checksum;
+ const char *meta_checksum;
+
+ n -= c;
+
+ c = g_variant_n_children (dirs_variant);
+
+ if (n < c)
+ {
+ g_variant_get_child (dirs_variant, n, "(&s&s&s)",
+ &name, &tree_checksum, &meta_checksum);
+
+ if (!query_child_info_dir (self->repo, meta_checksum,
+ matcher, flags, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ else
+ n -= c;
+ }
+
+ if (name)
+ {
+ g_file_info_set_attribute_byte_string (ret_info, "standard::name",
+ name);
+ g_file_info_set_attribute_string (ret_info, "standard::display-name",
+ name);
+ if (*name == '.')
+ g_file_info_set_is_hidden (ret_info, TRUE);
+ }
+ else
+ {
+ g_clear_object (&ret_info);
+ }
+
+ ret = TRUE;
+ *out_info = ret_info;
+ ret_info = NULL;
+ out:
+ g_clear_object (&ret_info);
+ if (matcher)
+ g_file_attribute_matcher_unref (matcher);
+ if (tree_child_metadata)
+ g_variant_unref (tree_child_metadata);
+ g_variant_unref (files_variant);
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static GFileInfo *
+ostree_repo_file_query_info (GFile *file,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ gboolean ret = FALSE;
+ GFileInfo *info = NULL;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ if (!self->parent)
+ {
+ info = g_file_info_new ();
+ set_info_from_dirmeta (info, self->tree_metadata);
+ }
+ else
+ {
+ if (!_ostree_repo_file_tree_query_child (self->parent, self->index,
+ attributes, flags,
+ &info, cancellable, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ g_clear_object (&info);
+ return info;
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_settable_attributes (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_file_attribute_info_list_new ();
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_writable_namespaces (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_file_attribute_info_list_new ();
+}
+
+static GFileInputStream *
+ostree_repo_file_read (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *local_file = NULL;
+ GFileInputStream *ret_stream = NULL;
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->tree_contents)
+ {
+ g_set_error_literal (error, G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ "Can't open directory");
+ goto out;
+ }
+
+ if (ostree_repo_is_archive (self->repo))
+ {
+ g_set_error_literal (error, G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Can't open archived file (yet)");
+ goto out;
+ }
+ else
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ ret_stream = g_file_read (local_file, cancellable, error);
+ if (!ret_stream)
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&local_file);
+ if (!ret)
+ g_clear_object (&ret_stream);
+ return ret_stream;
+}
+
+static void
+ostree_repo_file_file_iface_init (GFileIface *iface)
+{
+ iface->dup = ostree_repo_file_dup;
+ iface->hash = ostree_repo_file_hash;
+ iface->equal = ostree_repo_file_equal;
+ iface->is_native = ostree_repo_file_is_native;
+ iface->has_uri_scheme = ostree_repo_file_has_uri_scheme;
+ iface->get_uri_scheme = ostree_repo_file_get_uri_scheme;
+ iface->get_basename = ostree_repo_file_get_basename;
+ iface->get_path = ostree_repo_file_get_path;
+ iface->get_uri = ostree_repo_file_get_uri;
+ iface->get_parse_name = ostree_repo_file_get_parse_name;
+ iface->get_parent = ostree_repo_file_get_parent;
+ iface->prefix_matches = ostree_repo_file_prefix_matches;
+ iface->get_relative_path = ostree_repo_file_get_relative_path;
+ iface->resolve_relative_path = ostree_repo_file_resolve_relative_path;
+ iface->get_child_for_display_name = ostree_repo_file_get_child_for_display_name;
+ iface->set_display_name = NULL;
+ iface->enumerate_children = ostree_repo_file_enumerate_children;
+ iface->query_info = ostree_repo_file_query_info;
+ iface->query_filesystem_info = NULL;
+ iface->find_enclosing_mount = NULL;
+ iface->query_settable_attributes = ostree_repo_file_query_settable_attributes;
+ iface->query_writable_namespaces = ostree_repo_file_query_writable_namespaces;
+ iface->set_attribute = NULL;
+ iface->set_attributes_from_info = NULL;
+ iface->read_fn = ostree_repo_file_read;
+ iface->append_to = NULL;
+ iface->create = NULL;
+ iface->replace = NULL;
+ iface->open_readwrite = NULL;
+ iface->create_readwrite = NULL;
+ iface->replace_readwrite = NULL;
+ iface->delete_file = NULL;
+ iface->trash = NULL;
+ iface->make_directory = NULL;
+ iface->make_symbolic_link = NULL;
+ iface->copy = NULL;
+ iface->move = NULL;
+ iface->monitor_dir = NULL;
+ iface->monitor_file = NULL;
+
+ iface->supports_thread_contexts = TRUE;
+}
diff --git a/src/libostree/ostree-repo-file.h b/src/libostree/ostree-repo-file.h
new file mode 100644
index 0000000..2074965
--- /dev/null
+++ b/src/libostree/ostree-repo-file.h
@@ -0,0 +1,115 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_REPO_FILE
+#define _OSTREE_REPO_FILE
+
+#include "ostree-repo.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE (_ostree_repo_file_get_type ())
+#define OSTREE_REPO_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFile))
+#define OSTREE_REPO_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+#define OSTREE_IS_LOCAL_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_IS_LOCAL_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_REPO_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+
+typedef struct _OstreeRepoFile OstreeRepoFile;
+typedef struct _OstreeRepoFileClass OstreeRepoFileClass;
+
+struct _OstreeRepoFileClass
+{
+ GObjectClass parent_class;
+};
+
+GType _ostree_repo_file_get_type (void) G_GNUC_CONST;
+
+GFile * _ostree_repo_file_new_root (OstreeRepo *repo,
+ const char *commit);
+
+OstreeRepoFile * _ostree_repo_file_new_empty_tree (OstreeRepo *repo);
+
+GFile * _ostree_repo_file_new_child (OstreeRepoFile *parent,
+ const char *name);
+
+gboolean _ostree_repo_file_ensure_resolved (OstreeRepoFile *self,
+ GError **error);
+
+gboolean _ostree_repo_file_get_xattrs (OstreeRepoFile *self,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+OstreeRepo * _ostree_repo_file_get_repo (OstreeRepoFile *self);
+OstreeRepoFile * _ostree_repo_file_get_root (OstreeRepoFile *self);
+
+void _ostree_repo_file_make_empty_tree (OstreeRepoFile *self);
+
+void _ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+ const char *checksum,
+ GVariant *metadata);
+
+void _ostree_repo_file_tree_set_content_checksum (OstreeRepoFile *self,
+ const char *checksum);
+const char *_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile *self);
+
+gboolean _ostree_repo_file_is_tree (OstreeRepoFile *self);
+
+const char * _ostree_repo_file_nontree_get_checksum (OstreeRepoFile *self);
+
+GFile *_ostree_repo_file_nontree_get_local (OstreeRepoFile *self);
+
+void _ostree_repo_file_tree_remove_child (OstreeRepoFile *self,
+ const char *name);
+
+void _ostree_repo_file_tree_add_file (OstreeRepoFile *self,
+ const char *name,
+ const char *checksum);
+
+void _ostree_repo_file_tree_add_dir (OstreeRepoFile *self,
+ const char *name,
+ const char *content_checksum,
+ const char *metadata_checksum);
+
+int _ostree_repo_file_tree_find_child (OstreeRepoFile *self,
+ const char *name,
+ gboolean *is_dir,
+ GVariant **out_container);
+
+const char *_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile *self,
+ int n);
+
+gboolean _ostree_repo_file_tree_query_child (OstreeRepoFile *self,
+ int n,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GFileInfo **out_info,
+ GCancellable *cancellable,
+ GError **error);
+
+GVariant *_ostree_repo_file_tree_get_contents (OstreeRepoFile *self);
+GVariant *_ostree_repo_file_tree_get_metadata (OstreeRepoFile *self);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
new file mode 100644
index 0000000..2bdb56d
--- /dev/null
+++ b/src/libostree/ostree-repo.c
@@ -0,0 +1,1877 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+#include "ostree-repo-file-enumerator.h"
+
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+
+enum {
+ PROP_0,
+
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
+
+typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
+
+struct _OstreeRepoPrivate {
+ char *path;
+ GFile *repo_file;
+ GFile *tmp_dir;
+ GFile *local_heads_dir;
+ GFile *remote_heads_dir;
+ char *objects_path;
+ char *config_path;
+
+ gboolean inited;
+
+ GKeyFile *config;
+ gboolean archive;
+};
+
+static void
+ostree_repo_finalize (GObject *object)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo_file);
+ g_clear_object (&priv->tmp_dir);
+ 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)
+ g_key_file_free (priv->config);
+
+ G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_repo_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_repo_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeRepoPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ priv->repo_file = ot_util_new_file_for_path (priv->path);
+ priv->tmp_dir = g_file_resolve_relative_path (priv->repo_file, "tmp");
+ 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);
+
+ return object;
+}
+
+static void
+ostree_repo_class_init (OstreeRepoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
+
+ object_class->constructor = ostree_repo_constructor;
+ object_class->get_property = ostree_repo_get_property;
+ object_class->set_property = ostree_repo_set_property;
+ object_class->finalize = ostree_repo_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path",
+ "",
+ "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_repo_init (OstreeRepo *self)
+{
+}
+
+OstreeRepo*
+ostree_repo_new (const char *path)
+{
+ return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
+}
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error) G_GNUC_UNUSED;
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GError *temp_error = NULL;
+ gboolean ret = FALSE;
+ char *rev = NULL;
+
+ rev = ot_util_get_file_contents_utf8 (path, &temp_error);
+ if (rev == NULL)
+ {
+ if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (rev);
+ }
+
+ if (g_str_has_prefix (rev, "ref: "))
+ {
+ GFile *ref;
+ char *ref_path;
+ char *ref_sha256;
+ gboolean subret;
+
+ ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
+ ref_path = g_file_get_path (ref);
+
+ subret = parse_rev_file (self, ref_path, &ref_sha256, error);
+ g_clear_object (&ref);
+ g_free (ref_path);
+
+ if (!subret)
+ {
+ g_free (ref_sha256);
+ goto out;
+ }
+
+ g_free (rev);
+ rev = ref_sha256;
+ }
+ else
+ {
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+ }
+
+ *sha256 = rev;
+ rev = NULL;
+ ret = TRUE;
+ out:
+ g_free (rev);
+ return ret;
+}
+
+static gboolean
+resolve_rev (OstreeRepo *self,
+ const char *rev,
+ gboolean allow_noent,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *tmp = NULL;
+ char *tmp2 = NULL;
+ char *ret_rev = NULL;
+ GFile *child = NULL;
+ char *child_path = NULL;
+ GError *temp_error = NULL;
+ GVariant *commit = NULL;
+
+ if (strlen (rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty rev");
+ goto out;
+ }
+ else if (strlen (rev) == 64)
+ {
+ ret_rev = g_strdup (rev);
+ }
+ else if (g_str_has_suffix (rev, "^"))
+ {
+ tmp = g_strdup (rev);
+ tmp[strlen(tmp) - 1] = '\0';
+
+ if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
+ goto out;
+
+ g_variant_get_child (commit, 2, "s", &ret_rev);
+ if (strlen (ret_rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit %s has no parent", tmp2);
+ goto out;
+
+ }
+ }
+ else
+ {
+ child = g_file_get_child (priv->local_heads_dir, rev);
+ child_path = g_file_get_path (child);
+ if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
+ {
+ if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_free (ret_rev);
+ ret_rev = NULL;
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (ret_rev);
+
+ if (!ostree_validate_checksum_string (ret_rev, error))
+ goto out;
+ }
+ }
+
+ *sha256 = ret_rev;
+ ret_rev = NULL;
+ ret = TRUE;
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ g_free (tmp);
+ g_free (tmp2);
+ g_clear_object (&child);
+ g_free (child_path);
+ g_free (ret_rev);
+ return ret;
+}
+
+gboolean
+ostree_repo_resolve_rev (OstreeRepo *self,
+ const char *rev,
+ char **sha256,
+ GError **error)
+{
+ g_return_val_if_fail (rev != NULL, FALSE);
+ return resolve_rev (self, rev, FALSE, sha256, error);
+}
+
+static gboolean
+write_checksum_file (GFile *parentdir,
+ const char *name,
+ const char *sha256,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *child = NULL;
+ GOutputStream *out = NULL;
+ gsize bytes_written;
+
+ child = g_file_get_child (parentdir, name);
+
+ if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
+ goto out;
+ if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_close (out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&child);
+ g_clear_object (&out);
+ 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_free (priv->config);
+ priv->config = g_key_file_new ();
+ if (!g_key_file_load_from_data (priv->config, data, len, 0, error))
+ goto out;
+
+ 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);
+
+ if (priv->inited)
+ return TRUE;
+
+ if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Couldn't find objects directory '%s'", priv->objects_path);
+ goto out;
+ }
+
+ priv->config = g_key_file_new ();
+ if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
+ {
+ g_prefix_error (error, "Couldn't parse config file: ");
+ 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)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid repository version '%s'", version);
+ 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;
+ out:
+ g_free (version);
+ 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
+write_gvariant_to_tmp (OstreeRepo *self,
+ OstreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GVariant *serialized = NULL;
+ gboolean ret = FALSE;
+ gsize bytes_written;
+ char *tmp_name = NULL;
+ char *dest_name = NULL;
+ int fd = -1;
+ GUnixOutputStream *stream = NULL;
+ GChecksum *checksum = NULL;
+
+ serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
+
+ tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), "variant-tmp-XXXXXX", NULL);
+ fd = g_mkstemp (tmp_name);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
+ if (!g_output_stream_write_all ((GOutputStream*)stream,
+ g_variant_get_data (serialized),
+ g_variant_get_size (serialized),
+ &bytes_written,
+ NULL,
+ error))
+ goto out;
+
+ g_checksum_update (checksum, (guint8*)g_variant_get_data (serialized), g_variant_get_size (serialized));
+
+ if (!g_output_stream_close ((GOutputStream*)stream,
+ NULL, error))
+ goto out;
+
+ dest_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), g_checksum_get_string (checksum), NULL);
+ if (rename (tmp_name, dest_name) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_checksum = checksum;
+ checksum = NULL;
+ out:
+ /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+ (void) unlink (tmp_name);
+ if (fd != -1)
+ close (fd);
+ if (checksum)
+ g_checksum_free (checksum);
+ if (serialized != NULL)
+ g_variant_unref (serialized);
+ g_free (tmp_name);
+ g_free (dest_name);
+ g_clear_object (&stream);
+ return ret;
+}
+
+static gboolean
+import_gvariant_object (OstreeRepo *self,
+ OstreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *tmp_name = NULL;
+ GChecksum *ret_checksum = NULL;
+ gboolean did_exist;
+
+ if (!write_gvariant_to_tmp (self, type, variant, &ret_checksum, error))
+ goto out;
+
+ tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir),
+ g_checksum_get_string (ret_checksum), NULL);
+
+ if (!ostree_repo_store_object_trusted (self, tmp_name,
+ g_checksum_get_string (ret_checksum),
+ OSTREE_OBJECT_TYPE_META,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ (void) unlink (tmp_name);
+ g_free (tmp_name);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType type;
+ GVariant *ret_variant = NULL;
+
+ if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
+ goto out;
+
+ if (type != expected_type)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+ type, (guint32)expected_type);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ return ret;
+}
+
+static gboolean
+import_directory_meta (OstreeRepo *self,
+ const char *path,
+ GVariant **out_variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ GChecksum *ret_checksum = NULL;
+ GVariant *dirmeta = NULL;
+ GVariant *xattrs = NULL;
+
+ if (lstat (path, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (!S_ISDIR(stbuf.st_mode))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Not a directory: '%s'", path);
+ goto out;
+ }
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+
+ dirmeta = g_variant_new ("(uuuu a(ayay))",
+ OSTREE_DIR_META_VERSION,
+ GUINT32_TO_BE ((guint32)stbuf.st_uid),
+ GUINT32_TO_BE ((guint32)stbuf.st_gid),
+ GUINT32_TO_BE ((guint32)stbuf.st_mode),
+ xattrs);
+ g_variant_ref_sink (dirmeta);
+
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ dirmeta, &ret_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ {
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ if (dirmeta != NULL)
+ g_variant_unref (dirmeta);
+ }
+ else
+ {
+ *out_checksum = ret_checksum;
+ *out_variant = dirmeta;
+ }
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+char *
+ostree_repo_get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *ret;
+ char *relpath;
+
+ 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,
+ const char *checksum,
+ OstreeObjectType type,
+ GError **error)
+{
+ char *checksum_dir = NULL;
+ char *object_path = NULL;
+
+ object_path = ostree_repo_get_object_path (self, checksum, type);
+ checksum_dir = g_path_get_dirname (object_path);
+
+ if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
+ goto out;
+
+ out:
+ g_free (checksum_dir);
+ return object_path;
+}
+
+static gboolean
+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;
+ DIR *src_dir = NULL;
+ DIR *dest_dir = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+
+ src_basename = g_path_get_basename (path);
+ src_dirname = g_path_get_dirname (path);
+
+ src_dir = opendir (src_dirname);
+ if (src_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_basename = g_path_get_basename (dest_path);
+ dest_dirname = g_path_get_dirname (dest_path);
+ dest_dir = opendir (dest_dirname);
+ if (dest_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (force)
+ {
+ tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+ else
+ tmp_dest_basename = g_strdup (dest_basename);
+
+ if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
+ {
+ if (errno != EEXIST || !ignore_exists)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ else
+ *did_exist = TRUE;
+ }
+ else
+ *did_exist = FALSE;
+
+ if (force)
+ {
+ if (renameat (dirfd (dest_dir), tmp_dest_basename,
+ dirfd (dest_dir), dest_basename) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+
+ ret = TRUE;
+ out:
+ if (src_dir != NULL)
+ closedir (src_dir);
+ if (dest_dir != NULL)
+ closedir (dest_dir);
+ g_free (src_basename);
+ g_free (src_dirname);
+ g_free (dest_basename);
+ g_free (tmp_dest_basename);
+ g_free (dest_dirname);
+ g_free (dest_path);
+ 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);
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GString *tempfile_path = NULL;
+ GChecksum *checksum = NULL;
+ gboolean did_exist;
+
+ tempfile_path = g_string_new (priv->path);
+ g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
+
+ if (!ostree_unpack_object (path, objtype, tempfile_path->str, &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_path ? tempfile_path->str : path,
+ expected_checksum,
+ objtype,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (tempfile_path)
+ {
+ (void) unlink (tempfile_path->str);
+ g_string_free (tempfile_path, TRUE);
+ }
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+static void parsed_tree_data_free (ParsedTreeData *pdata);
+
+struct _ParsedDirectoryData {
+ ParsedTreeData *tree_data;
+ char *metadata_sha256;
+ GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ parsed_tree_data_free (pdata->tree_data);
+ g_free (pdata->metadata_sha256);
+ g_variant_unref (pdata->meta_data);
+ g_free (pdata);
+}
+
+struct _ParsedTreeData {
+ GHashTable *files; /* char* filename -> char* checksum */
+ GHashTable *directories; /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+ ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+ ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)parsed_directory_data_free);
+ return ret;
+}
+
+static void
+parsed_tree_data_free (ParsedTreeData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ g_hash_table_destroy (pdata->files);
+ g_hash_table_destroy (pdata->directories);
+ g_free (pdata);
+}
+
+static GVariant *
+create_empty_gvariant_dict (void)
+{
+ GVariantBuilder builder;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
+ return g_variant_builder_end (&builder);
+}
+
+static gboolean
+import_parsed_tree (OstreeRepo *self,
+ ParsedTreeData *tree,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *serialized_tree = NULL;
+ gboolean builders_initialized = FALSE;
+ GVariantBuilder files_builder;
+ GVariantBuilder dirs_builder;
+ GHashTableIter hash_iter;
+ GSList *sorted_filenames = NULL;
+ GSList *iter;
+ gpointer key, value;
+
+ g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+ g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+ builders_initialized = TRUE;
+
+ g_hash_table_iter_init (&hash_iter, tree->files);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+ }
+
+ sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+ for (iter = sorted_filenames; iter; iter = iter->next)
+ {
+ const char *name = iter->data;
+ const char *value;
+
+ value = g_hash_table_lookup (tree->files, name);
+ g_variant_builder_add (&files_builder, "(ss)", name, value);
+ }
+
+ g_slist_free (sorted_filenames);
+ sorted_filenames = NULL;
+
+ g_hash_table_iter_init (&hash_iter, tree->directories);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+ }
+
+ sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+ for (iter = sorted_filenames; iter; iter = iter->next)
+ {
+ const char *name = iter->data;
+ GChecksum *dir_checksum = NULL;
+ ParsedDirectoryData *dir;
+
+ dir = g_hash_table_lookup (tree->directories, name);
+
+ if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+ goto out;
+
+ g_variant_builder_add (&dirs_builder, "(sss)",
+ name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+ g_checksum_free (dir_checksum);
+ }
+
+ g_slist_free (sorted_filenames);
+ sorted_filenames = NULL;
+
+ serialized_tree = g_variant_new ("(u a{sv}@a(ss)@a(sss))",
+ GUINT32_TO_BE (0),
+ create_empty_gvariant_dict (),
+ g_variant_builder_end (&files_builder),
+ g_variant_builder_end (&dirs_builder));
+ builders_initialized = FALSE;
+ g_variant_ref_sink (serialized_tree);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_slist_free (sorted_filenames);
+ if (builders_initialized)
+ {
+ g_variant_builder_clear (&files_builder);
+ g_variant_builder_clear (&dirs_builder);
+ }
+ if (serialized_tree)
+ g_variant_unref (serialized_tree);
+ return ret;
+}
+
+static gboolean
+check_path (const char *filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (!*filename)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty filename");
+ goto out;
+ }
+
+ if (strcmp (filename, ".") == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (ot_util_filename_has_dotdot (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (g_path_is_absolute (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Absolute filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ ParsedDirectoryData **dir, /*inout*/
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *dirmeta = NULL;
+ GChecksum *dir_meta_checksum = NULL;
+ ParsedDirectoryData *dir_value = *dir;
+
+ g_assert (tree != NULL);
+
+ if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+ goto out;
+
+ if (dir_value)
+ {
+ g_variant_unref (dir_value->meta_data);
+ dir_value->meta_data = dirmeta;
+ }
+ else
+ {
+ dir_value = g_new0 (ParsedDirectoryData, 1);
+ dir_value->tree_data = parsed_tree_data_new ();
+ dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+ dir_value->meta_data = dirmeta;
+ g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
+ }
+
+ ret = TRUE;
+ *dir = dir_value;
+ out:
+ if (dir_meta_checksum)
+ g_checksum_free (dir_meta_checksum);
+ return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *checksum = NULL;
+ struct stat stbuf;
+ gboolean did_exist;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_assert (tree != NULL);
+
+ if (!ostree_stat_and_checksum_file (-1, abspath, OSTREE_OBJECT_TYPE_FILE, &checksum, &stbuf, error))
+ goto out;
+
+ if (!ostree_repo_store_object_trusted (self, abspath, g_checksum_get_string (checksum),
+ OSTREE_OBJECT_TYPE_FILE, TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ g_hash_table_replace (tree->files, g_strdup (basename),
+ g_strdup (g_checksum_get_string (checksum)));
+
+ ret = TRUE;
+ out:
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (OstreeRepo *self,
+ const char *base,
+ const char *filename,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ struct stat stbuf;
+ char *component_abspath = NULL;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1;
+ char *abspath = NULL;
+ ParsedDirectoryData *dir;
+ int i;
+ gboolean is_directory;
+
+ if (!check_path (filename, error))
+ goto out;
+
+ abspath = g_build_filename (base, filename, NULL);
+
+ if (lstat (abspath, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ is_directory = S_ISDIR(stbuf.st_mode);
+
+ if (components)
+ g_ptr_array_free (components, TRUE);
+ components = ot_util_path_split (filename);
+ g_assert (components->len > 0);
+
+ current_tree = tree;
+ for (i = 0; i < components->len; i++)
+ {
+ component = components->pdata[i];
+ g_free (component_abspath);
+ component_abspath = ot_util_path_join_n (base, components, i);
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ g_assert_cmpstr (component, !=, ".");
+
+ if (i < components->len - 1)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ component,
+ filename);
+ goto out;
+ }
+ /* Implicitly add intermediate directories */
+ if (!add_one_directory_to_tree_and_import (self, component,
+ component_abspath, current_tree, &dir,
+ error))
+ goto out;
+ g_assert (dir != NULL);
+ current_tree = dir->tree_data;
+ }
+ else if (is_directory)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_directory_to_tree_and_import (self, component,
+ abspath, current_tree, &dir,
+ error))
+ goto out;
+ }
+ else
+ {
+ g_assert (!is_directory);
+ if (dir != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_file_to_tree_and_import (self, component, abspath,
+ current_tree, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (components)
+ g_ptr_array_unref (components);
+ g_free (component_abspath);
+ g_free (abspath);
+ 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,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ ParsedDirectoryData *root,
+ GChecksum **out_commit,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *root_checksum = NULL;
+ GChecksum *ret_commit = NULL;
+ GVariant *commit = NULL;
+ GDateTime *now = NULL;
+
+ g_assert (branch != NULL);
+ g_assert (subject != NULL);
+
+ if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
+ goto out;
+
+ now = g_date_time_new_now_utc ();
+ commit = g_variant_new ("(u a{sv}ssstss)",
+ GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
+ metadata ? metadata : create_empty_gvariant_dict (),
+ parent ? parent : "",
+ subject, body ? body : "",
+ GUINT64_TO_BE (g_date_time_to_unix (now)),
+ g_checksum_get_string (root_checksum),
+ root->metadata_sha256);
+ g_variant_ref_sink (commit);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ commit, &ret_commit, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit;
+ out:
+ if (root_checksum)
+ g_checksum_free (root_checksum);
+ if (commit)
+ g_variant_unref (commit);
+ if (now)
+ g_date_time_unref (now);
+ return ret;
+}
+
+static gboolean
+import_root (OstreeRepo *self,
+ const char *base,
+ ParsedDirectoryData **out_root,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ParsedDirectoryData *ret_root = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+
+ if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+ goto out;
+
+ ret_root = g_new0 (ParsedDirectoryData, 1);
+ ret_root->tree_data = parsed_tree_data_new ();
+ ret_root->meta_data = root_metadata;
+ root_metadata = NULL;
+ ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+
+ ret = TRUE;
+ *out_root = ret_root;
+ ret_root = NULL;
+ out:
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ parsed_directory_data_free (ret_root);
+ return ret;
+}
+
+gboolean
+ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedDirectoryData *root = NULL;
+ GChecksum *ret_commit_checksum = NULL;
+ GUnixInputStream *in = NULL;
+ GDataInputStream *datain = NULL;
+ char *filename = NULL;
+ gsize filename_len;
+ GError *temp_error = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+ char *current_head = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (branch != NULL, FALSE);
+ g_return_val_if_fail (subject != NULL, FALSE);
+ g_return_val_if_fail (metadata == NULL || g_variant_is_of_type (metadata, G_VARIANT_TYPE ("a{sv}")), FALSE);
+
+ if (parent == NULL)
+ parent = branch;
+
+ /* We're overwriting the tree */
+ if (!import_root (self, base, &root, error))
+ goto out;
+
+ if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
+ goto out;
+
+ in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+ datain = g_data_input_stream_new ((GInputStream*)in);
+
+ while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+ &filename_len, NULL, &temp_error)) != NULL)
+ {
+ if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+ {
+ if (temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ }
+ if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
+ goto out;
+ g_free (filename);
+ filename = NULL;
+ }
+ if (filename == NULL && temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
+ root, &ret_commit_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit_checksum;
+ ret_commit_checksum = NULL;
+ out:
+ if (ret_commit_checksum)
+ g_checksum_free (ret_commit_checksum);
+ g_free (current_head);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ g_clear_object (&datain);
+ g_clear_object (&in);
+ g_free (filename);
+ parsed_directory_data_free (root);
+ return ret;
+
+}
+
+static gboolean
+iter_object_dir (OstreeRepo *self,
+ GFile *dir,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileEnumerator *enumerator = NULL;
+ GFileInfo *file_info = NULL;
+ char *dirpath = NULL;
+
+ dirpath = g_file_get_path (dir);
+
+ enumerator = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type != G_FILE_TYPE_DIRECTORY
+ && (g_str_has_suffix (name, ".meta")
+ || g_str_has_suffix (name, ".file")
+ || g_str_has_suffix (name, ".packfile")))
+ {
+ char *dot;
+ char *path;
+
+ dot = strrchr (name, '.');
+ g_assert (dot);
+
+ if ((dot - name) == 62)
+ {
+ path = g_build_filename (dirpath, name, NULL);
+ callback (self, path, file_info, user_data);
+ g_free (path);
+ }
+ }
+
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dirpath);
+ return ret;
+}
+
+gboolean
+ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GFile *objectdir = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean ret = FALSE;
+ GFileInfo *file_info = NULL;
+ GError *temp_error = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ objectdir = ot_util_new_file_for_path (priv->objects_path);
+ enumerator = g_file_enumerate_children (objectdir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+ {
+ GFile *objdir = g_file_get_child (objectdir, name);
+ if (!iter_object_dir (self, objdir, callback, user_data, error))
+ {
+ g_object_unref (objdir);
+ goto out;
+ }
+ g_object_unref (objdir);
+ }
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&file_info);
+ g_clear_object (&enumerator);
+ g_clear_object (&objectdir);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant (OstreeRepo *self,
+ const char *sha256,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType ret_type;
+ GVariant *ret_variant = NULL;
+ char *path = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ path = ostree_repo_get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
+ if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
+ goto out;
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ g_free (path);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ OstreeRepoFile *dir,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean
+checkout_one_directory (OstreeRepo *self,
+ const char *destination,
+ const char *dirname,
+ OstreeRepoFile *dir,
+ GFileInfo *dir_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ GVariant *xattr_variant = NULL;
+
+ dest_path = g_build_filename (destination, dirname, NULL);
+
+ if (!_ostree_repo_file_get_xattrs (dir, &xattr_variant, NULL, error))
+ goto out;
+
+ if (mkdir (dest_path, (mode_t)g_file_info_get_attribute_uint32 (dir_info, "unix::mode")) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
+ goto out;
+ }
+
+ if (!ostree_set_xattrs (dest_path, xattr_variant, cancellable, error))
+ goto out;
+
+ if (!checkout_tree (self, dir, dest_path, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ if (xattr_variant)
+ g_variant_unref (xattr_variant);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ OstreeRepoFile *dir,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileInfo *file_info = NULL;
+ GFileEnumerator *dir_enum = NULL;
+ GFile *child = NULL;
+ char *object_path = NULL;
+ char *dest_path = NULL;
+
+ dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ error);
+ if (!dir_enum)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ child = g_file_get_child ((GFile*)dir, name);
+
+ if (type == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!checkout_one_directory (self, destination, name, (OstreeRepoFile*)child, file_info, cancellable, error))
+ goto out;
+ }
+ else
+ {
+ const char *checksum = _ostree_repo_file_nontree_get_checksum ((OstreeRepoFile*)child);
+
+ dest_path = g_build_filename (destination, name, NULL);
+ object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+
+ if (priv->archive)
+ {
+ if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+ goto out;
+ }
+ else
+ {
+ if (link (object_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ }
+
+ g_free (object_path);
+ object_path = NULL;
+ g_free (dest_path);
+ dest_path = NULL;
+ g_clear_object (&file_info);
+ g_clear_object (&child);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&dir_enum);
+ g_clear_object (&file_info);
+ g_clear_object (&child);
+ g_free (object_path);
+ g_free (dest_path);
+ return ret;
+}
+
+gboolean
+ostree_repo_checkout (OstreeRepo *self,
+ const char *rev,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *resolved = NULL;
+ OstreeRepoFile *root = NULL;
+ GFileInfo *root_info = NULL;
+
+ if (g_file_test (destination, G_FILE_TEST_EXISTS))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Destination path '%s' already exists",
+ destination);
+ goto out;
+ }
+
+ if (!resolve_rev (self, rev, FALSE, &resolved, error))
+ goto out;
+
+ root = (OstreeRepoFile*)_ostree_repo_file_new_root (self, resolved);
+ if (!_ostree_repo_file_ensure_resolved (root, error))
+ goto out;
+
+ root_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error);
+ if (!root_info)
+ goto out;
+
+ if (!checkout_one_directory (self, destination, NULL, root, root_info, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (resolved);
+ g_clear_object (&root);
+ g_clear_object (&root_info);
+ return ret;
+}
+
+gboolean
+ostree_repo_diff (OstreeRepo *self,
+ const char *ref,
+ GFile *target,
+ GPtrArray **out_modified,
+ GPtrArray **out_removed,
+ GPtrArray **out_added,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *ret_modified = NULL;
+ GPtrArray *ret_removed = NULL;
+ GPtrArray *ret_added = NULL;
+
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Not implemented yet");
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (ret_modified)
+ g_ptr_array_free (ret_modified, TRUE);
+ if (ret_removed)
+ g_ptr_array_free (ret_removed, TRUE);
+ if (ret_added)
+ g_ptr_array_free (ret_added, TRUE);
+ return ret;
+}
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
new file mode 100644
index 0000000..47a896e
--- /dev/null
+++ b/src/libostree/ostree-repo.h
@@ -0,0 +1,162 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef _OSTREE_REPO
+#define _OSTREE_REPO
+
+#include "ostree-core.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO ostree_repo_get_type()
+#define OSTREE_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
+#define OSTREE_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
+#define OSTREE_IS_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
+#define OSTREE_IS_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
+#define OSTREE_REPO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
+
+typedef struct {
+ GObject parent;
+} OstreeRepo;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeRepoClass;
+
+GType ostree_repo_get_type (void);
+
+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);
+
+char * ostree_repo_get_object_path (OstreeRepo *self,
+ const char *object,
+ OstreeObjectType type);
+
+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,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error);
+
+gboolean ostree_repo_checkout (OstreeRepo *self,
+ const char *ref,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error);
+
+typedef struct {
+ guint content_differs : 1;
+ guint xattrs_differs : 1;
+ guint unused : 30;
+
+ GFileInfo *src_info;
+ GFileInfo *target_info;
+
+ char *src_file_checksum;
+ char *target_file_checksum;
+
+ GVariant *src_xattrs;
+ GVariant *target_xattrs;
+} OstreeRepoDiffItem;
+
+gboolean ostree_repo_diff (OstreeRepo *self,
+ const char *ref,
+ GFile *target,
+ GPtrArray **out_modified, /* OstreeRepoDiffItem */
+ GPtrArray **out_removed, /* OstreeRepoDiffItem */
+ GPtrArray **out_added, /* OstreeRepoDiffItem */
+ GCancellable *cancellable,
+ GError **error);
+
+typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
+ GFileInfo *fileinfo, gpointer user_data);
+
+gboolean ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_REPO */
diff --git a/libotutil/otutil.h b/src/libostree/ostree.h
similarity index 85%
copy from libotutil/otutil.h
copy to src/libostree/ostree.h
index a93dc84..d683080 100644
--- a/libotutil/otutil.h
+++ b/src/libostree/ostree.h
@@ -20,12 +20,10 @@
* Author: Colin Walters <walters verbum org>
*/
-#ifndef __OSTREE_UTIL_H__
+#ifndef __OSTREE_H__
-#include <ot-gio-utils.h>
-#include <ot-glib-compat.h>
-#include <ot-opt-utils.h>
-#include <ot-unix-utils.h>
-#include <ot-variant-utils.h>
+#include <ostree-core.h>
+#include <ostree-repo.h>
+#include <ostree-checkout.h>
#endif
diff --git a/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c
similarity index 100%
rename from libotutil/ot-gio-utils.c
rename to src/libotutil/ot-gio-utils.c
diff --git a/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h
similarity index 100%
rename from libotutil/ot-gio-utils.h
rename to src/libotutil/ot-gio-utils.h
diff --git a/libotutil/ot-glib-compat.c b/src/libotutil/ot-glib-compat.c
similarity index 100%
rename from libotutil/ot-glib-compat.c
rename to src/libotutil/ot-glib-compat.c
diff --git a/libotutil/ot-glib-compat.h b/src/libotutil/ot-glib-compat.h
similarity index 100%
rename from libotutil/ot-glib-compat.h
rename to src/libotutil/ot-glib-compat.h
diff --git a/libotutil/ot-opt-utils.c b/src/libotutil/ot-opt-utils.c
similarity index 100%
rename from libotutil/ot-opt-utils.c
rename to src/libotutil/ot-opt-utils.c
diff --git a/libotutil/ot-opt-utils.h b/src/libotutil/ot-opt-utils.h
similarity index 100%
rename from libotutil/ot-opt-utils.h
rename to src/libotutil/ot-opt-utils.h
diff --git a/libotutil/ot-unix-utils.c b/src/libotutil/ot-unix-utils.c
similarity index 100%
rename from libotutil/ot-unix-utils.c
rename to src/libotutil/ot-unix-utils.c
diff --git a/libotutil/ot-unix-utils.h b/src/libotutil/ot-unix-utils.h
similarity index 100%
rename from libotutil/ot-unix-utils.h
rename to src/libotutil/ot-unix-utils.h
diff --git a/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c
similarity index 100%
rename from libotutil/ot-variant-utils.c
rename to src/libotutil/ot-variant-utils.c
diff --git a/libotutil/ot-variant-utils.h b/src/libotutil/ot-variant-utils.h
similarity index 100%
rename from libotutil/ot-variant-utils.h
rename to src/libotutil/ot-variant-utils.h
diff --git a/libotutil/otutil.h b/src/libotutil/otutil.h
similarity index 100%
rename from libotutil/otutil.h
rename to src/libotutil/otutil.h
diff --git a/osbuild/main.c b/src/osbuild/main.c
similarity index 100%
rename from osbuild/main.c
rename to src/osbuild/main.c
diff --git a/osbuild/ob-builtin-buildone.c b/src/osbuild/ob-builtin-buildone.c
similarity index 100%
rename from osbuild/ob-builtin-buildone.c
rename to src/osbuild/ob-builtin-buildone.c
diff --git a/osbuild/ob-builtins.h b/src/osbuild/ob-builtins.h
similarity index 100%
rename from osbuild/ob-builtins.h
rename to src/osbuild/ob-builtins.h
diff --git a/osbuild/osbuild-raw-makeinstall.c b/src/osbuild/osbuild-raw-makeinstall.c
similarity index 100%
rename from osbuild/osbuild-raw-makeinstall.c
rename to src/osbuild/osbuild-raw-makeinstall.c
diff --git a/osbuild/ostree-buildone b/src/osbuild/ostree-buildone
similarity index 100%
rename from osbuild/ostree-buildone
rename to src/osbuild/ostree-buildone
diff --git a/osbuild/ostree-buildone-make b/src/osbuild/ostree-buildone-make
similarity index 100%
rename from osbuild/ostree-buildone-make
rename to src/osbuild/ostree-buildone-make
diff --git a/osbuild/ostree-buildone-makeinstall-split-artifacts b/src/osbuild/ostree-buildone-makeinstall-split-artifacts
similarity index 100%
rename from osbuild/ostree-buildone-makeinstall-split-artifacts
rename to src/osbuild/ostree-buildone-makeinstall-split-artifacts
diff --git a/ostree/main.c b/src/ostree/main.c
similarity index 100%
rename from ostree/main.c
rename to src/ostree/main.c
diff --git a/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c
similarity index 100%
rename from ostree/ot-builtin-checkout.c
rename to src/ostree/ot-builtin-checkout.c
diff --git a/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c
similarity index 100%
rename from ostree/ot-builtin-commit.c
rename to src/ostree/ot-builtin-commit.c
diff --git a/ostree/ot-builtin-compose.c b/src/ostree/ot-builtin-compose.c
similarity index 100%
rename from ostree/ot-builtin-compose.c
rename to src/ostree/ot-builtin-compose.c
diff --git a/ostree/ot-builtin-diff.c b/src/ostree/ot-builtin-diff.c
similarity index 100%
rename from ostree/ot-builtin-diff.c
rename to src/ostree/ot-builtin-diff.c
diff --git a/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c
similarity index 100%
rename from ostree/ot-builtin-fsck.c
rename to src/ostree/ot-builtin-fsck.c
diff --git a/ostree/ot-builtin-init.c b/src/ostree/ot-builtin-init.c
similarity index 100%
rename from ostree/ot-builtin-init.c
rename to src/ostree/ot-builtin-init.c
diff --git a/ostree/ot-builtin-log.c b/src/ostree/ot-builtin-log.c
similarity index 100%
rename from ostree/ot-builtin-log.c
rename to src/ostree/ot-builtin-log.c
diff --git a/ostree/ot-builtin-pull.c b/src/ostree/ot-builtin-pull.c
similarity index 100%
rename from ostree/ot-builtin-pull.c
rename to src/ostree/ot-builtin-pull.c
diff --git a/ostree/ot-builtin-remote.c b/src/ostree/ot-builtin-remote.c
similarity index 100%
rename from ostree/ot-builtin-remote.c
rename to src/ostree/ot-builtin-remote.c
diff --git a/ostree/ot-builtin-rev-parse.c b/src/ostree/ot-builtin-rev-parse.c
similarity index 100%
rename from ostree/ot-builtin-rev-parse.c
rename to src/ostree/ot-builtin-rev-parse.c
diff --git a/ostree/ot-builtin-run-triggers.c b/src/ostree/ot-builtin-run-triggers.c
similarity index 100%
rename from ostree/ot-builtin-run-triggers.c
rename to src/ostree/ot-builtin-run-triggers.c
diff --git a/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c
similarity index 100%
rename from ostree/ot-builtin-show.c
rename to src/ostree/ot-builtin-show.c
diff --git a/ostree/ot-builtins.h b/src/ostree/ot-builtins.h
similarity index 100%
rename from ostree/ot-builtins.h
rename to src/ostree/ot-builtins.h
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]