[ostree] libostree: Split off SELinux OstreeSePolicy class



commit 3337334be5c7abbb79796994523e445db53df838
Author: Colin Walters <walters verbum org>
Date:   Wed Feb 19 08:40:29 2014 -0500

    libostree: Split off SELinux OstreeSePolicy class
    
    It's better if this is independent from the OstreeSysroot; for
    example, a policy is active in a given deployment root at once, not
    for a sysroot globally.
    
    We can also collect SELinux-related API in one place.
    
    Unfortunately at the moment there can be only one instance of this
    class per process.

 Makefile-libostree-defines.am         |    1 +
 Makefile-libostree.am                 |    1 +
 src/libostree/ostree-repo-commit.c    |  121 +++++++--
 src/libostree/ostree-repo.h           |    4 +
 src/libostree/ostree-sepolicy.c       |  456 +++++++++++++++++++++++++++++++++
 src/libostree/ostree-sepolicy.h       |   66 +++++
 src/libostree/ostree-sysroot-deploy.c |  363 +++++++--------------------
 src/libostree/ostree-types.h          |    1 +
 8 files changed, 718 insertions(+), 295 deletions(-)
---
diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am
index 6b07d0e..02037ed 100644
--- a/Makefile-libostree-defines.am
+++ b/Makefile-libostree-defines.am
@@ -27,6 +27,7 @@ libostree_public_headers = \
        src/libostree/ostree-types.h \
        src/libostree/ostree-repo-file.h \
        src/libostree/ostree-diff.h \
+       src/libostree/ostree-sepolicy.h \
        src/libostree/ostree-sysroot.h \
        src/libostree/ostree-deployment.h \
        src/libostree/ostree-bootconfig-parser.h \
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index 8e9aaca..04d03be 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -56,6 +56,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-repo-file.c \
        src/libostree/ostree-repo-file-enumerator.c \
        src/libostree/ostree-repo-file-enumerator.h \
+       src/libostree/ostree-sepolicy.c \
        src/libostree/ostree-sysroot-private.h \
        src/libostree/ostree-sysroot.c \
        src/libostree/ostree-sysroot-cleanup.c \
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index 10d3971..1b3136d 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -1618,6 +1618,8 @@ struct OstreeRepoCommitModifier {
   OstreeRepoCommitModifierXattrCallback xattr_callback;
   GDestroyNotify xattr_destroy;
   gpointer xattr_user_data;
+
+  OstreeSePolicy *sepolicy;
 };
 
 OstreeRepoCommitFilterResult
@@ -1684,6 +1686,67 @@ apply_commit_filter (OstreeRepo               *self,
 }
 
 static gboolean
+get_modified_xattrs (OstreeRepo                       *self,
+                     OstreeRepoCommitModifier         *modifier,
+                     const char                       *relpath,
+                     GFileInfo                        *file_info,
+                     GFile                            *path,
+                     GVariant                        **out_xattrs,
+                     GCancellable                     *cancellable,
+                     GError                          **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_variant GVariant *ret_xattrs = NULL;
+
+  if (modifier && modifier->xattr_callback)
+    {
+      ret_xattrs = modifier->xattr_callback (self, relpath, file_info,
+                                             modifier->xattr_user_data);
+    }
+  else if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
+    {
+      if (!gs_file_get_all_xattrs (path, &ret_xattrs, cancellable, error))
+        goto out;
+    }
+
+  if (modifier->sepolicy)
+    {
+      gs_free char *label = NULL;
+
+      if (!ostree_sepolicy_get_label (modifier->sepolicy, relpath,
+                                      g_file_info_get_attribute_uint32 (file_info, "unix::mode"),
+                                      &label, cancellable, error))
+        goto out;
+
+      if (label)
+        {
+          GVariantBuilder *builder;
+
+          if (ret_xattrs)
+            builder = ot_util_variant_builder_from_variant (ret_xattrs,
+                                                            G_VARIANT_TYPE ("a(ayay)"));
+          else
+            builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ayay)"));
+
+          g_variant_builder_add_value (builder,
+                                       g_variant_new ("(@ay ay)",
+                                                      g_variant_new_bytestring ("security.selinux"),
+                                                      g_variant_new_bytestring (label)));
+          if (ret_xattrs)
+            g_variant_unref (ret_xattrs);
+
+          ret_xattrs = g_variant_builder_end (builder);
+          g_variant_ref_sink (ret_xattrs);
+        }
+    }
+  
+  ret = TRUE;
+  gs_transfer_out_value (out_xattrs, &ret_xattrs);
+ out:
+  return ret;
+}
+
+static gboolean
 write_directory_to_mtree_internal (OstreeRepo                  *self,
                                    GFile                       *dir,
                                    OstreeMutableTree           *mtree,
@@ -1751,16 +1814,10 @@ write_directory_to_mtree_internal (OstreeRepo                  *self,
       if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW)
         {
           g_debug ("Adding: %s", gs_file_get_path_cached (dir));
-          if (modifier && modifier->xattr_callback)
-            {
-              xattrs = modifier->xattr_callback (self, relpath, child_info,
-                                                 modifier->xattr_user_data);
-            }
-          else if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
-            {
-              if (!gs_file_get_all_xattrs (dir, &xattrs, cancellable, error))
-                goto out;
-            }
+          if (!get_modified_xattrs (self, modifier, relpath, child_info, dir,
+                                    &xattrs,
+                                    cancellable, error))
+            goto out;
 
           if (!_ostree_repo_write_directory_meta (self, modified_info, xattrs, &child_file_csum,
                                                   cancellable, error))
@@ -1872,17 +1929,11 @@ write_directory_to_mtree_internal (OstreeRepo                  *self,
                             goto out;
                         }
 
-                      if (modifier && modifier->xattr_callback)
-                        {
-                          xattrs = modifier->xattr_callback (self, child_relpath, child_info,
-                                                             modifier->xattr_user_data);
-                        }
-                      else if (!(modifier && (modifier->flags & 
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
-                        {
-                          g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref);
-                          if (!gs_file_get_all_xattrs (child, &xattrs, cancellable, error))
-                            goto out;
-                        }
+                      if (!get_modified_xattrs (self, modifier,
+                                                child_relpath, child_info, child,
+                                                &xattrs,
+                                                cancellable, error))
+                        goto out;
 
                       if (!ostree_raw_file_to_content_stream (file_input,
                                                               modified_info, xattrs,
@@ -2082,6 +2133,8 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
   if (modifier->xattr_destroy)
     modifier->xattr_destroy (modifier->xattr_user_data);
 
+  g_clear_object (&modifier->sepolicy);
+
   g_free (modifier);
   return;
 }
@@ -2094,8 +2147,9 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
  * @user_data: Data for @callback:
  *
  * If set, this function should return extended attributes to use for
- * the given path.  This is useful for things like SELinux, where a build
- * system can label the files as it's committing to the repository.
+ * the given path.  This is useful for things like ACLs and SELinux,
+ * where a build system can label the files as it's committing to the
+ * repository.
  */
 void
 ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier  *modifier,
@@ -2108,6 +2162,27 @@ ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier  *modif
   modifier->xattr_user_data = user_data;
 }
 
+/**
+ * ostree_repo_commit_modifier_set_sepolicy:
+ * @modifier: An #OstreeRepoCommitModifier
+ * @sepolicy: (allow-none): Policy to use for labeling
+ *
+ * If @policy is non-%NULL, use it to look up labels to use for
+ * "security.selinux" extended attributes.
+ *
+ * Note that any policy specified this way operates in addition to any
+ * extended attributes provided via
+ * ostree_repo_commit_modifier_set_xattr_callback().  However if both
+ * specify a value for "security.selinux", then the one from the
+ * policy wins.
+ */
+void
+ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier              *modifier,
+                                          OstreeSePolicy                        *sepolicy)
+{
+  g_clear_object (&modifier->sepolicy);
+  modifier->sepolicy = sepolicy ? g_object_ref (sepolicy) : NULL;
+}
 
 G_DEFINE_BOXED_TYPE(OstreeRepoCommitModifier, ostree_repo_commit_modifier,
                     ostree_repo_commit_modifier_ref,
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index df7f3e0..ad6533a 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -25,6 +25,7 @@
 #include "ostree-core.h"
 #include "ostree-types.h"
 #include "ostree-async-progress.h"
+#include "ostree-sepolicy.h"
 
 G_BEGIN_DECLS
 
@@ -313,6 +314,9 @@ void ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier
                                                      GDestroyNotify                         destroy,
                                                      gpointer                               user_data);
 
+void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier              *modifier,
+                                               OstreeSePolicy                        *sepolicy);
+
 OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier);
 void ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier);
 
diff --git a/src/libostree/ostree-sepolicy.c b/src/libostree/ostree-sepolicy.c
new file mode 100644
index 0000000..8a0457a
--- /dev/null
+++ b/src/libostree/ostree-sepolicy.c
@@ -0,0 +1,456 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#include "otutil.h"
+#include "libgsystem.h"
+
+#include "ostree-sepolicy.h"
+#include "ostree-bootloader-uboot.h"
+#include "ostree-bootloader-syslinux.h"
+
+/**
+ * SECTION:libostree-sepolicy
+ * @title: SELinux policy management
+ * @short_description: Read SELinux policy and manage filesystem labels
+ *
+ * A #OstreeSePolicy object can load the SELinux policy from a given
+ * root and perform labeling.
+ */
+struct OstreeSePolicy {
+  GObject parent;
+
+  GFile *path;
+
+#ifdef HAVE_SELINUX
+  GFile *selinux_policy_root;
+  struct selabel_handle *selinux_hnd;
+  char *selinux_policy_name;
+#endif
+};
+
+typedef struct {
+  GObjectClass parent_class;
+} OstreeSePolicyClass;
+
+static void initable_iface_init       (GInitableIface      *initable_iface);
+
+enum {
+  PROP_0,
+
+  PROP_PATH
+};
+
+G_DEFINE_TYPE_WITH_CODE (OstreeSePolicy, ostree_sepolicy, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
+
+static void
+ostree_sepolicy_finalize (GObject *object)
+{
+  OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+  g_clear_object (&self->path);
+  g_clear_object (&self->selinux_policy_root);
+  g_clear_pointer (&self->selinux_policy_name, g_free);
+#ifdef HAVE_SELINUX
+  if (self->selinux_hnd)
+    {
+      selabel_close (self->selinux_hnd);
+      self->selinux_hnd = NULL;
+    }
+#endif
+
+  G_OBJECT_CLASS (ostree_sepolicy_parent_class)->finalize (object);
+}
+
+static void
+ostree_sepolicy_set_property(GObject         *object,
+                            guint            prop_id,
+                            const GValue    *value,
+                            GParamSpec      *pspec)
+{
+  OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      /* Canonicalize */
+      self->path = g_file_new_for_path (gs_file_get_path_cached (g_value_get_object (value)));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_sepolicy_get_property(GObject         *object,
+                            guint            prop_id,
+                            GValue          *value,
+                            GParamSpec      *pspec)
+{
+  OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      g_value_set_object (value, self->path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_sepolicy_constructed (GObject *object)
+{
+  OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+  g_assert (self->path != NULL);
+
+  G_OBJECT_CLASS (ostree_sepolicy_parent_class)->constructed (object);
+}
+
+static void
+ostree_sepolicy_class_init (OstreeSePolicyClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ostree_sepolicy_constructed;
+  object_class->get_property = ostree_sepolicy_get_property;
+  object_class->set_property = ostree_sepolicy_set_property;
+  object_class->finalize = ostree_sepolicy_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PATH,
+                                   g_param_spec_object ("path",
+                                                        "",
+                                                        "",
+                                                        G_TYPE_FILE,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static gboolean
+initable_init (GInitable     *initable,
+               GCancellable  *cancellable,
+               GError       **error)
+{
+#ifdef HAVE_SELINUX
+  gboolean ret = FALSE;
+  OstreeSePolicy *self = OSTREE_SEPOLICY (initable);
+  gs_unref_object GFile *etc_selinux_dir = NULL;
+  gs_unref_object GFile *policy_config_path = NULL;
+  gs_unref_object GFile *policy_root = NULL;
+  gs_unref_object GFileInputStream *filein = NULL;
+  gs_unref_object GDataInputStream *datain = NULL;
+  gboolean enabled = FALSE;
+  char *policytype = NULL;
+  const char *selinux_prefix = "SELINUX=";
+  const char *selinuxtype_prefix = "SELINUXTYPE=";
+
+  etc_selinux_dir = g_file_resolve_relative_path (self->path, "etc/selinux");
+  if (!g_file_query_exists (etc_selinux_dir, NULL))
+    {
+      g_object_unref (etc_selinux_dir);
+      etc_selinux_dir = g_file_resolve_relative_path (self->path, "usr/etc/selinux");
+    }
+  policy_config_path = g_file_get_child (etc_selinux_dir, "config");
+
+  if (g_file_query_exists (policy_config_path, NULL))
+    {
+      filein = g_file_read (policy_config_path, cancellable, error);
+      if (!filein)
+        goto out;
+
+      datain = g_data_input_stream_new ((GInputStream*)filein);
+
+      while (TRUE)
+        {
+          gsize len;
+          GError *temp_error = NULL;
+          gs_free char *line = g_data_input_stream_read_line_utf8 (datain, &len,
+                                                                   cancellable, &temp_error);
+      
+          if (temp_error)
+            {
+              g_propagate_error (error, temp_error);
+              goto out;
+            }
+
+          if (!line)
+            break;
+      
+          if (g_str_has_prefix (line, selinuxtype_prefix))
+            {
+              policytype = g_strstrip (g_strdup (line + strlen (selinuxtype_prefix))); 
+              policy_root = g_file_get_child (etc_selinux_dir, policytype);
+            }
+          else if (g_str_has_prefix (line, selinux_prefix))
+            {
+              const char *enabled_str = line + strlen (selinux_prefix);
+              if (g_ascii_strncasecmp (enabled_str, "enforcing", strlen ("enforcing")) == 0 ||
+                  g_ascii_strncasecmp (enabled_str, "permissive", strlen ("permissive")) == 0)
+                enabled = TRUE;
+            }
+        }
+    }
+
+  if (enabled)
+    {
+      g_setenv ("LIBSELINUX_DISABLE_PCRE_PRECOMPILED", "1", FALSE);
+      if (selinux_set_policy_root (gs_file_get_path_cached (policy_root)) != 0)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "selinux_set_policy_root(%s): %s",
+                       gs_file_get_path_cached (etc_selinux_dir),
+                       strerror (errno));
+          goto out;
+        }
+
+      self->selinux_hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
+      if (!self->selinux_hnd)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "With policy root '%s': selabel_open(SELABEL_CTX_FILE): %s",
+                       gs_file_get_path_cached (etc_selinux_dir),
+                       strerror (errno));
+          goto out;
+        }
+
+      {
+        char *con = NULL;
+        if (selabel_lookup_raw (self->selinux_hnd, &con, "/", 0755) != 0)
+          {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                         "With policy root '%s': Failed to look up context of /: %s",
+                         gs_file_get_path_cached (etc_selinux_dir),
+                         strerror (errno));
+            goto out;
+          }
+        freecon (con);
+      }
+
+      self->selinux_policy_name = g_strdup (policytype);
+      self->selinux_policy_root = g_object_ref (etc_selinux_dir);
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+#else
+  return TRUE;
+#endif
+}
+
+static void
+ostree_sepolicy_init (OstreeSePolicy *self)
+{
+}
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+  initable_iface->init = initable_init;
+}
+
+/**
+ * ostree_sepolicy_new:
+ * @path: Path to a root directory
+ *
+ * Returns: (transfer full): An accessor object for SELinux policy in root located at @path
+ */
+OstreeSePolicy*
+ostree_sepolicy_new (GFile         *path,
+                     GCancellable  *cancellable,
+                     GError       **error)
+{
+  return g_initable_new (OSTREE_TYPE_SEPOLICY, cancellable, error, "path", path, NULL);
+}
+
+/**
+ * ostree_sepolicy_get_path:
+ * @self:
+ *
+ * Returns: (transfer none): Path to rootfs
+ */
+GFile *
+ostree_sepolicy_get_path (OstreeSePolicy  *self)
+{
+  return self->path;
+}
+
+const char *
+ostree_sepolicy_get_name (OstreeSePolicy *self)
+{
+#ifdef HAVE_SELINUX
+  return self->selinux_policy_name;
+#else
+  return NULL;
+#endif
+}
+
+/**
+ * ostree_sepolicy_get_label:
+ * @self: Self
+ * @relpath: Path
+ * @unix_mode: Unix mode
+ * @out_label: (allow-none) (out) (transfer full): Return location for security context
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Store in @out_label the security context for the given @relpath and
+ * mode @unix_mode.  If the policy does not specify a label, %NULL
+ * will be returned.
+ */
+gboolean
+ostree_sepolicy_get_label (OstreeSePolicy    *self,
+                           const char       *relpath,
+                           guint32           unix_mode,
+                           char            **out_label,
+                           GCancellable     *cancellable,
+                           GError          **error)
+{
+#ifdef HAVE_SELINUX
+  gboolean ret = FALSE;
+  int res;
+  char *con = NULL;
+
+  if (self->selinux_hnd)
+    {
+      res = selabel_lookup_raw (self->selinux_hnd, &con, relpath, unix_mode);
+      if (res != 0)
+        {
+          int errsv = errno;
+          if (errsv != ENOENT)
+            {
+              ot_util_set_error_from_errno (error, errsv);
+              goto out;
+            }
+        }
+      else
+        {
+          /* Ensure we consistently allocate with g_malloc */
+          *out_label = g_strdup (con);
+          freecon (con);
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+#else
+  return TRUE;
+#endif
+}
+
+/**
+ * ostree_sepolicy_restorecon:
+ * @self: Self
+ * @path: Path string to use for policy lookup
+ * @info: (allow-none): File attributes
+ * @target: Physical path to target file
+ * @flags: Flags controlling behavior
+ * @out_new_label: (allow-none) (out): New label, or %NULL if unchanged
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Reset the security context of @target based on the SELinux policy.
+ */
+gboolean
+ostree_sepolicy_restorecon (OstreeSePolicy    *self,
+                            const char       *path,
+                            GFileInfo        *info,
+                            GFile            *target,
+                            OstreeSePolicyRestoreconFlags flags,
+                            char            **out_new_label,
+                            GCancellable     *cancellable,
+                            GError          **error)
+{
+#ifdef HAVE_SELINUX
+  gboolean ret = FALSE;
+  gs_unref_object GFileInfo *src_info = NULL;
+  gs_free char *label = NULL;
+  gboolean do_relabel = TRUE;
+
+  if (info != NULL)
+    src_info = g_object_ref (info);
+  else
+    {
+      src_info = g_file_query_info (target, "unix::mode",
+                                    G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                    cancellable, error);
+      if (!src_info)
+        goto out;
+    }
+
+  if (flags & OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING)
+    {
+      char *existing_con = NULL;
+      if (lgetfilecon_raw (gs_file_get_path_cached (target), &existing_con) > 0
+          && existing_con)
+        {
+          do_relabel = FALSE;
+          freecon (existing_con);
+        }
+    }
+
+  if (do_relabel)
+    {
+      if (!ostree_sepolicy_get_label (self, path, 
+                                      g_file_info_get_attribute_uint32 (src_info, "unix::mode"),
+                                      &label,
+                                      cancellable, error))
+        goto out;
+
+      if (!label)
+        {
+          if (!(flags & OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL))
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "No label found for '%s'", path);
+              goto out;
+            }
+        }
+      else
+        {
+          int res = lsetfilecon (gs_file_get_path_cached (target), label);
+          if (res != 0)
+            {
+              ot_util_set_error_from_errno (error, errno);
+              goto out;
+            }
+        }
+    }
+
+  ret = TRUE;
+  gs_transfer_out_value (out_new_label, &label);
+ out:
+  return ret;
+#else
+  return TRUE;
+#endif
+}
diff --git a/src/libostree/ostree-sepolicy.h b/src/libostree/ostree-sepolicy.h
new file mode 100644
index 0000000..19a067e
--- /dev/null
+++ b/src/libostree/ostree-sepolicy.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#pragma once
+
+#include "ostree-types.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_SEPOLICY ostree_sepolicy_get_type()
+#define OSTREE_SEPOLICY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_SEPOLICY, OstreeSePolicy))
+#define OSTREE_IS_SEPOLICY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SEPOLICY))
+
+GType ostree_sepolicy_get_type (void);
+
+OstreeSePolicy* ostree_sepolicy_new (GFile         *path,
+                                     GCancellable  *cancellable,
+                                     GError       **error);
+
+GFile * ostree_sepolicy_get_path (OstreeSePolicy  *self);
+
+const char *ostree_sepolicy_get_name (OstreeSePolicy *self);
+
+gboolean ostree_sepolicy_get_label (OstreeSePolicy    *self,
+                                    const char       *relpath,
+                                    guint32           unix_mode,
+                                    char            **out_label,
+                                    GCancellable     *cancellable,
+                                    GError          **error);
+
+typedef enum {
+  OSTREE_SEPOLICY_RESTORECON_FLAGS_NONE,
+  OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL = (1 << 0),
+  OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING = (1 << 1)
+} OstreeSePolicyRestoreconFlags;
+
+gboolean ostree_sepolicy_restorecon (OstreeSePolicy   *self,
+                                     const char       *path,
+                                     GFileInfo        *info,
+                                     GFile            *target,
+                                     OstreeSePolicyRestoreconFlags  flags,
+                                     char            **out_new_label,
+                                     GCancellable     *cancellable,
+                                     GError          **error);
+
+G_END_DECLS
+
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index aa55db2..f5ee56f 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -1,6 +1,6 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
  *
- * Copyright (C) 2012 Colin Walters <walters verbum org>
+ * Copyright (C) 2012,2014 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
@@ -16,17 +16,10 @@
  * 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"
 
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#endif
-
 #include "ostree-sysroot-private.h"
 #include "ostree-core-private.h"
 #include "otutil.h"
@@ -243,74 +236,6 @@ checkout_deployment_tree (OstreeSysroot     *sysroot,
   return ret;
 }
 
-#ifdef HAVE_SELINUX
-static gboolean
-get_selinux_policy_root (GFile          *deployment_etc,
-                         GFile         **out_policy_root,
-                         GCancellable   *cancellable,
-                         GError        **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *etc_selinux_dir = NULL;
-  gs_unref_object GFile *policy_config_path = NULL;
-  gs_unref_object GFile *ret_policy_root = NULL;
-  gs_unref_object GFileInputStream *filein = NULL;
-  gs_unref_object GDataInputStream *datain = NULL;
-  gboolean enabled = FALSE;
-  char *policytype = NULL;
-  const char *selinux_prefix = "SELINUX=";
-  const char *selinuxtype_prefix = "SELINUXTYPE=";
-
-  etc_selinux_dir = g_file_get_child (deployment_etc, "selinux");
-  policy_config_path = g_file_get_child (etc_selinux_dir, "config");
-
-  if (g_file_query_exists (policy_config_path, NULL))
-    {
-      filein = g_file_read (policy_config_path, cancellable, error);
-      if (!filein)
-        goto out;
-
-      datain = g_data_input_stream_new ((GInputStream*)filein);
-
-      while (TRUE)
-        {
-          gsize len;
-          GError *temp_error = NULL;
-          gs_free char *line = g_data_input_stream_read_line_utf8 (datain, &len,
-                                                                   cancellable, &temp_error);
-      
-          if (temp_error)
-            {
-              g_propagate_error (error, temp_error);
-              goto out;
-            }
-
-          if (!line)
-            break;
-      
-          if (g_str_has_prefix (line, selinuxtype_prefix))
-            {
-              policytype = g_strstrip (g_strdup (line + strlen (selinuxtype_prefix))); 
-            }
-          else if (g_str_has_prefix (line, selinux_prefix))
-            {
-              const char *enabled_str = line + strlen (selinux_prefix);
-              if (g_ascii_strncasecmp (enabled_str, "enforcing", strlen ("enforcing")) == 0 ||
-                  g_ascii_strncasecmp (enabled_str, "permissive", strlen ("permissive")) == 0)
-                enabled = TRUE;
-            }
-        }
-    }
-
-  if (enabled)
-    ret_policy_root = g_file_get_child (etc_selinux_dir, policytype);
-    
-  ret = TRUE;
-  gs_transfer_out_value (out_policy_root, &ret_policy_root);
- out:
-  return ret;
-}
-
 static char *
 ptrarray_path_join (GPtrArray  *path)
 {
@@ -336,69 +261,43 @@ ptrarray_path_join (GPtrArray  *path)
 }
 
 static gboolean
-relabel_one_path (GFile         *path,
+relabel_one_path (OstreeSysroot  *sysroot,
+                  OstreeSePolicy *sepolicy,
+                  GFile         *path,
                   GFileInfo     *info,
                   GPtrArray     *path_parts,
-                  struct selabel_handle *hnd,
                   GCancellable   *cancellable,
                   GError        **error)
 {
   gboolean ret = FALSE;
-  guint32 mode;
   gs_free char *relpath = NULL;
-  char *con = NULL;
-
-  mode = g_file_info_get_attribute_uint32 (info, "unix::mode");
 
   relpath = ptrarray_path_join (path_parts);
-
-  if (selabel_lookup_raw (hnd, &con, relpath, mode) != 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "selabel_lookup_raw(%s, %u): %s",
-                   relpath, mode, strerror (errno));
-      goto out;
-    }
-
-  if (S_ISLNK (mode))
-    {
-      if (lsetfilecon (gs_file_get_path_cached (path), con) != 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "lsetfilecon(%s): %s",
-                       gs_file_get_path_cached (path), strerror (errno));
-          goto out;
-        }
-    }
-  else
-    {
-      if (setfilecon (gs_file_get_path_cached (path), con) != 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "setfilecon(%s): %s",
-                       gs_file_get_path_cached (path), strerror (errno));
-          goto out;
-        }
-    }
+  if (!ostree_sepolicy_restorecon (sepolicy, relpath,
+                                   info, path,
+                                   OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL,
+                                   NULL,
+                                   cancellable, error))
+    goto out;
 
   ret = TRUE;
  out:
-  if (con) freecon (con);
   return ret;
 }
 
 static gboolean
-relabel_recursively (GFile          *dir,
+relabel_recursively (OstreeSysroot  *sysroot,
+                     OstreeSePolicy *sepolicy,
+                     GFile          *dir,
                      GFileInfo      *dir_info,
                      GPtrArray      *path_parts,
-                     struct selabel_handle *hnd,
                      GCancellable   *cancellable,
                      GError        **error)
 {
   gboolean ret = FALSE;
   gs_unref_object GFileEnumerator *direnum = NULL;
 
-  if (!relabel_one_path (dir, dir_info, path_parts, hnd,
+  if (!relabel_one_path (sysroot, sepolicy, dir, dir_info, path_parts,
                          cancellable, error))
     goto out;
 
@@ -425,13 +324,13 @@ relabel_recursively (GFile          *dir,
       ftype = g_file_info_get_file_type (file_info);
       if (ftype == G_FILE_TYPE_DIRECTORY)
         {
-          if (!relabel_recursively (child, file_info, path_parts, hnd,
+          if (!relabel_recursively (sysroot, sepolicy, child, file_info, path_parts,
                                     cancellable, error))
             goto out;
         }
       else
         {
-          if (!relabel_one_path (child, file_info, path_parts, hnd,
+          if (!relabel_one_path (sysroot, sepolicy, child, file_info, path_parts,
                                  cancellable, error))
             goto out;
         }
@@ -444,202 +343,116 @@ relabel_recursively (GFile          *dir,
   return ret;
 }
 
-#endif
-
-typedef struct {
-  gboolean have_policy;
-#ifdef HAVE_SELINUX
-  struct selabel_handle *hnd;
-#endif
-} OstreeLabelingContext;
-
-static gboolean
-init_labeling_context (GFile                         *deployment_etc,
-                       OstreeLabelingContext         *secontext,
-                       GCancellable                  *cancellable,
-                       GError                       **error)
-{
-#ifdef HAVE_SELINUX
-  gboolean ret = FALSE;
-  gs_unref_object GFile *policy_root = NULL;
-
-  if (!get_selinux_policy_root (deployment_etc, &policy_root,
-                                cancellable, error))
-    goto out;
-
-  if (policy_root)
-    {
-      secontext->have_policy = TRUE;
-
-      g_print ("ostadmin: Using SELinux policy '%s'\n", gs_file_get_basename_cached (policy_root));
-
-      if (selinux_set_policy_root (gs_file_get_path_cached (policy_root)) != 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "selinux_set_policy_root(%s): %s",
-                       gs_file_get_path_cached (policy_root),
-                       strerror (errno));
-          goto out;
-        }
-      secontext->hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
-      if (!secontext->hnd)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "selabel_open(SELABEL_CTX_FILE): %s",
-                       strerror (errno));
-          goto out;
-        }
-    }
-  else
-    secontext->have_policy = FALSE;
-
-  ret = TRUE;
- out:
-  return ret;
-#else
-  secontext->have_policy = FALSE;
-  return TRUE;
-#endif
-}
-
-static void
-ostree_labeling_context_cleanup (OstreeLabelingContext *secontext)
-{
-#ifdef HAVE_SELINUX
-  if (secontext->hnd)
-    selabel_close (secontext->hnd);
-#endif
-}
-
 static gboolean
 selinux_relabel_dir (OstreeSysroot                 *sysroot,
-                     OstreeLabelingContext  *secontext,
+                     OstreeSePolicy                *sepolicy,
                      GFile                         *dir,
                      const char                    *prefix,
                      GCancellable                  *cancellable,
                      GError                       **error)
 {
-#ifdef HAVE_SELINUX
   gboolean ret = FALSE;
   gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
   gs_unref_object GFileInfo *root_info = NULL;
 
-  if (secontext->have_policy)
+  root_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 cancellable, error);
+  if (!root_info)
+    goto out;
+  
+  g_ptr_array_add (path_parts, (char*)prefix);
+  if (!relabel_recursively (sysroot, sepolicy, dir, root_info, path_parts,
+                            cancellable, error))
     {
-      root_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO,
-                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                     cancellable, error);
-      if (!root_info)
-        goto out;
-
-      g_ptr_array_add (path_parts, (char*)prefix);
-      if (!relabel_recursively (dir, root_info, path_parts, secontext->hnd,
-                                cancellable, error))
-        {
-          g_prefix_error (error, "Relabeling /%s: ", prefix);
-          goto out;
-        }
+      g_prefix_error (error, "Relabeling /%s: ", prefix);
+      goto out;
     }
 
   ret = TRUE;
  out:
   return ret;
-#else
-  return TRUE;
-#endif
 }
 
-#ifdef HAVE_SELINUX
 static gboolean
-selinux_relabel_file (OstreeLabelingContext         *secontext,
+selinux_relabel_file (OstreeSysroot                 *sysroot,
+                      OstreeSePolicy                *sepolicy,
                       GFile                         *path,
                       const char                    *prefix,
                       GCancellable                  *cancellable,
                       GError                       **error)
 {
   gboolean ret = FALSE;
+  gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
+  gs_unref_object GFileInfo *file_info = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
+                                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                                            cancellable, error);
+  if (!file_info)
+    goto out;
 
-  if (secontext->have_policy)
+  g_ptr_array_add (path_parts, (char*)prefix);
+  g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
+  if (!relabel_one_path (sysroot, sepolicy, path, file_info, path_parts,
+                         cancellable, error))
     {
-      gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
-      gs_unref_object GFileInfo *file_info = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
-                                                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                                                cancellable, error);
-      if (!file_info)
-        goto out;
-
-      g_ptr_array_add (path_parts, (char*)prefix);
-      g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
-      if (!relabel_one_path (path, file_info, path_parts, secontext->hnd,
-                             cancellable, error))
-        {
-          g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
-                          gs_file_get_basename_cached (path));
-          goto out;
-        }
+      g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
+                      gs_file_get_basename_cached (path));
+      goto out;
     }
 
   ret = TRUE;
  out:
   return ret;
 }
-#endif
 
 static gboolean
 selinux_relabel_var_if_needed (OstreeSysroot                 *sysroot,
-                               OstreeLabelingContext  *secontext,
+                               OstreeSePolicy                *sepolicy,
                                GFile                         *deployment_var_path,
                                GCancellable                  *cancellable,
                                GError                       **error)
 {
-#ifdef HAVE_SELINUX
   gboolean ret = FALSE;
-
-  if (secontext->have_policy)
-    {
-      /* This is a bit of a hack; we should change the code at some
-       * point in the distant future to only create (and label) /var
-       * when doing a deployment.
-       */
-      gs_unref_object GFile *deployment_var_labeled = 
-        g_file_get_child (deployment_var_path, ".ostree-selabeled");
-      gs_unref_object GFile *deployment_var_labeled_tmp = 
-        g_file_get_child (deployment_var_path, ".ostree-selabeled.tmp");
+  /* This is a bit of a hack; we should change the code at some
+   * point in the distant future to only create (and label) /var
+   * when doing a deployment.
+   */
+  gs_unref_object GFile *deployment_var_labeled = 
+    g_file_get_child (deployment_var_path, ".ostree-selabeled");
+  gs_unref_object GFile *deployment_var_labeled_tmp = 
+    g_file_get_child (deployment_var_path, ".ostree-selabeled.tmp");
       
-      if (!g_file_query_exists (deployment_var_labeled, NULL))
+  if (!g_file_query_exists (deployment_var_labeled, NULL))
+    {
+      g_print ("ostadmin: Didn't find '%s', relabeling /var\n",
+               gs_file_get_path_cached (deployment_var_labeled));
+
+      if (!selinux_relabel_dir (sysroot, sepolicy,
+                                deployment_var_path, "var",
+                                cancellable, error))
         {
-          g_print ("ostadmin: Didn't find '%s', relabeling /var\n",
-                   gs_file_get_path_cached (deployment_var_labeled));
+          g_prefix_error (error, "Relabeling /var: ");
+          goto out;
+        }
 
-          if (!selinux_relabel_dir (sysroot, secontext,
-                                    deployment_var_path, "var",
+      if (!g_file_replace_contents (deployment_var_labeled_tmp, "", 0, NULL, FALSE,
+                                    G_FILE_CREATE_REPLACE_DESTINATION, NULL,
                                     cancellable, error))
-            {
-              g_prefix_error (error, "Relabeling /var: ");
-              goto out;
-            }
-
-          if (!g_file_replace_contents (deployment_var_labeled_tmp, "", 0, NULL, FALSE,
-                                        G_FILE_CREATE_REPLACE_DESTINATION, NULL,
-                                        cancellable, error))
-            goto out;
+        goto out;
           
-          if (!selinux_relabel_file (secontext, deployment_var_labeled_tmp, "var",
-                                     cancellable, error))
-            goto out;
+      if (!selinux_relabel_file (sysroot, sepolicy,
+                                 deployment_var_labeled_tmp, "var",
+                                 cancellable, error))
+        goto out;
 
-          if (!gs_file_rename (deployment_var_labeled_tmp, deployment_var_labeled,
-                               cancellable, error))
-            goto out;
-        }
+      if (!gs_file_rename (deployment_var_labeled_tmp, deployment_var_labeled,
+                           cancellable, error))
+        goto out;
     }
 
   ret = TRUE;
  out:
   return ret;
-#else
-  return TRUE;
-#endif
 }
 
 static gboolean
@@ -647,6 +460,7 @@ merge_configuration (OstreeSysroot         *sysroot,
                      OstreeDeployment      *previous_deployment,
                      OstreeDeployment      *deployment,
                      GFile                 *deployment_path,
+                     OstreeSePolicy       **out_sepolicy,
                      GCancellable          *cancellable,
                      GError               **error)
 {
@@ -655,6 +469,7 @@ merge_configuration (OstreeSysroot         *sysroot,
   gs_unref_object GFile *source_etc_pristine_path = NULL;
   gs_unref_object GFile *deployment_usretc_path = NULL;
   gs_unref_object GFile *deployment_etc_path = NULL;
+  gs_unref_object OstreeSePolicy *sepolicy = NULL;
   gboolean etc_exists;
   gboolean usretc_exists;
 
@@ -703,21 +518,26 @@ merge_configuration (OstreeSysroot         *sysroot,
   
   if (usretc_exists)
     {
-      __attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext new_default_secontext 
= { 0, };
-
       /* TODO - set out labels as we copy files */
       g_assert (!etc_exists);
       if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
                            cancellable, error))
         goto out;
 
-      if (!init_labeling_context (deployment_etc_path, &new_default_secontext,
-                                  cancellable, error))
+      /* Here, we initialize SELinux policy from the /usr/etc inside
+       * the root - this is before we've finalized the configuration
+       * merge into /etc. */
+      sepolicy = ostree_sepolicy_new (deployment_path, cancellable, error);
+      if (!sepolicy)
         goto out;
 
-      if (!selinux_relabel_dir (sysroot, &new_default_secontext, deployment_etc_path, "etc",
-                                cancellable, error))
-        goto out;
+      if (ostree_sepolicy_get_name (sepolicy) != NULL)
+        {
+          g_print ("ostadmin: Using SELinux policy '%s'\n", ostree_sepolicy_get_name (sepolicy));
+          if (!selinux_relabel_dir (sysroot, sepolicy, deployment_etc_path, "etc",
+                                    cancellable, error))
+            goto out;
+        }
       g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
     }
 
@@ -733,6 +553,7 @@ merge_configuration (OstreeSysroot         *sysroot,
     }
 
   ret = TRUE;
+  gs_transfer_out_value (out_sepolicy, &sepolicy);
  out:
   return ret;
 }
@@ -1507,7 +1328,6 @@ ostree_sysroot_deploy_tree (OstreeSysroot     *self,
 {
   gboolean ret = FALSE;
   gint new_deployserial;
-  __attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext secontext = { 0, };
   gs_unref_object OstreeDeployment *new_deployment = NULL;
   gs_unref_object OstreeDeployment *merge_deployment = NULL;
   gs_unref_object OstreeRepo *repo = NULL;
@@ -1518,6 +1338,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot     *self,
   gs_unref_object GFile *tree_kernel_path = NULL;
   gs_unref_object GFile *tree_initramfs_path = NULL;
   gs_unref_object GFile *new_deployment_path = NULL;
+  gs_unref_object OstreeSePolicy *sepolicy = NULL;
   gs_free char *new_bootcsum = NULL;
   gs_unref_object OstreeBootconfigParser *bootconfig = NULL;
 
@@ -1590,6 +1411,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot     *self,
 
   if (!merge_configuration (self, merge_deployment, new_deployment,
                             new_deployment_path,
+                            &sepolicy,
                             cancellable, error))
     {
       g_prefix_error (error, "During /etc merge: ");
@@ -1598,10 +1420,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot     *self,
 
   deployment_etc = g_file_get_child (new_deployment_path, "etc");
 
-  if (!init_labeling_context (deployment_etc, &secontext, cancellable, error))
-    goto out;
-  
-  if (!selinux_relabel_var_if_needed (self, &secontext, deployment_var,
+  if (!selinux_relabel_var_if_needed (self, sepolicy, deployment_var,
                                       cancellable, error))
     goto out;
 
diff --git a/src/libostree/ostree-types.h b/src/libostree/ostree-types.h
index b15cbbc..8be1258 100644
--- a/src/libostree/ostree-types.h
+++ b/src/libostree/ostree-types.h
@@ -27,6 +27,7 @@
 G_BEGIN_DECLS
 
 typedef struct OstreeRepo OstreeRepo;
+typedef struct OstreeSePolicy OstreeSePolicy;
 typedef struct OstreeSysroot OstreeSysroot;
 typedef struct OstreeMutableTree OstreeMutableTree;
 typedef struct OstreeRepoFile OstreeRepoFile;



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