[ostree] core: Add --union mode to checkout



commit 76bc35186eb19b1c55c891f8e6e513f2c19eb99a
Author: Colin Walters <walters verbum org>
Date:   Tue Mar 6 11:37:50 2012 -0500

    core: Add --union mode to checkout
    
    This is another step towards ostbuild using this instead of the
    "compose" builtin.

 src/libostree/ostree-core.c      |   66 +++++++++++++++++++-
 src/libostree/ostree-core.h      |    8 ++
 src/libostree/ostree-repo.c      |  132 +++++++++++++++++++++++++++++++++-----
 src/libostree/ostree-repo.h      |   10 ++-
 src/ostree/ot-builtin-checkout.c |    3 +
 tests/t0000-basic.sh             |   12 +++-
 6 files changed, 210 insertions(+), 21 deletions(-)
---
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index 8a4cb1f..5f938d1 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -915,6 +915,9 @@ ostree_create_temp_file_from_input (GFile            *dir,
   /* 128 attempts seems reasonable... */
   for (i = 0; i < 128; i++)
     {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto out;
+
       g_free (possible_name);
       possible_name = subst_xxxxxx (tmp_name->str);
       g_clear_object (&possible_file);
@@ -941,7 +944,7 @@ ostree_create_temp_file_from_input (GFile            *dir,
           break;
         }
     }
-  if (i == 128)
+  if (i >= 128)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "Exhausted 128 attempts to create a temporary file");
@@ -990,3 +993,64 @@ ostree_create_temp_regular_file (GFile            *dir,
   g_clear_object (&ret_stream);
   return ret;
 }
+
+gboolean
+ostree_create_temp_hardlink (GFile            *dir,
+                             GFile            *src,
+                             const char       *prefix,
+                             const char       *suffix,
+                             GFile           **out_file,
+                             GCancellable     *cancellable,
+                             GError          **error)
+{
+  gboolean ret = FALSE;
+  GString *tmp_name = NULL;
+  char *possible_name = NULL;
+  GFile *possible_file = NULL;
+  int i = 0;
+
+  tmp_name = create_tmp_string (ot_gfile_get_path_cached (dir),
+                                prefix, suffix);
+  
+  /* 128 attempts seems reasonable... */
+  for (i = 0; i < 128; i++)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto out;
+
+      g_free (possible_name);
+      possible_name = subst_xxxxxx (tmp_name->str);
+      g_clear_object (&possible_file);
+      possible_file = g_file_get_child (dir, possible_name);
+
+      if (link (ot_gfile_get_path_cached (src), ot_gfile_get_path_cached (possible_file)) < 0)
+        {
+          if (errno == EEXIST)
+            continue;
+          else
+            {
+              ot_util_set_error_from_errno (error, errno);
+              goto out;
+            }
+        }
+      else
+        {
+          break;
+        }
+    }
+  if (i >= 128)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Exhausted 128 attempts to create a temporary file");
+      goto out;
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value(out_file, &possible_file);
+ out:
+  if (tmp_name)
+    g_string_free (tmp_name, TRUE);
+  g_free (possible_name);
+  g_clear_object (&possible_file);
+  return ret;
+}
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 24489ce..e6893c9 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -182,6 +182,14 @@ gboolean ostree_create_temp_regular_file (GFile            *dir,
                                           GCancellable     *cancellable,
                                           GError          **error);
 
+gboolean ostree_create_temp_hardlink (GFile            *dir,
+                                      GFile            *src,
+                                      const char       *prefix,
+                                      const char       *suffix,
+                                      GFile           **out_file,
+                                      GCancellable     *cancellable,
+                                      GError          **error);
+
 GVariant *ostree_create_archive_file_metadata (GFileInfo   *file_info,
                                                GVariant    *xattrs);
 
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 8087f06..05ebbc4 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -2453,6 +2453,7 @@ ostree_repo_iter_objects (OstreeRepo  *self,
 static gboolean
 checkout_file_from_input (GFile          *file,
                           OstreeRepoCheckoutMode mode,
+                          OstreeRepoCheckoutOverwriteMode    overwrite_mode,
                           GFileInfo      *finfo,
                           GVariant       *xattrs,
                           GInputStream   *input,
@@ -2460,6 +2461,9 @@ checkout_file_from_input (GFile          *file,
                           GError        **error)
 {
   gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFile *dir = NULL;
+  GFile *temp_file = NULL;
   GFileInfo *temp_info = NULL;
 
   if (mode == OSTREE_REPO_CHECKOUT_MODE_USER)
@@ -2475,20 +2479,119 @@ checkout_file_from_input (GFile          *file,
       xattrs = NULL;
     }
 
-  if (!ostree_create_file_from_input (file, temp_info ? temp_info : finfo,
-                                      xattrs, input, OSTREE_OBJECT_TYPE_RAW_FILE,
-                                      NULL, cancellable, error))
-    goto out;
+  if (overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
+    {
+      if (g_file_info_get_file_type (temp_info ? temp_info : finfo) == G_FILE_TYPE_DIRECTORY)
+        {
+          if (!ostree_create_file_from_input (file, temp_info ? temp_info : finfo,
+                                              xattrs, input, OSTREE_OBJECT_TYPE_RAW_FILE,
+                                              NULL, cancellable, &temp_error))
+            {
+              if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+                {
+                  g_clear_error (&temp_error);
+                }
+              else
+                {
+                  g_propagate_error (error, temp_error);
+                  goto out;
+                }
+            }
+        }
+      else
+        {
+          dir = g_file_get_parent (file);
+          if (!ostree_create_temp_file_from_input (dir, NULL, "checkout",
+                                                   temp_info ? temp_info : finfo,
+                                                   xattrs, input, OSTREE_OBJECT_TYPE_RAW_FILE,
+                                                   &temp_file, NULL,
+                                                   cancellable, error))
+            goto out;
+          
+          if (rename (ot_gfile_get_path_cached (temp_file), ot_gfile_get_path_cached (file)) < 0)
+            {
+              ot_util_set_error_from_errno (error, errno);
+              goto out;
+            }
+        }
+    }
+  else
+    {
+      if (!ostree_create_file_from_input (file, temp_info ? temp_info : finfo,
+                                          xattrs, input, OSTREE_OBJECT_TYPE_RAW_FILE,
+                                          NULL, cancellable, error))
+        goto out;
+    }
 
   ret = TRUE;
  out:
   g_clear_object (&temp_info);
+  g_clear_object (&temp_file);
+  g_clear_object (&dir);
+  return ret;
+}
+
+static gboolean
+checkout_file_hardlink (OstreeRepo                  *self,
+                        OstreeRepoCheckoutMode    mode,
+                        OstreeRepoCheckoutOverwriteMode    overwrite_mode,
+                        GFile                    *source,
+                        GFile                    *destination,
+                        GCancellable             *cancellable,
+                        GError                  **error)
+{
+  gboolean ret = FALSE;
+  GFile *dir = NULL;
+  GFile *temp_file = NULL;
+
+  if (overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
+    {
+      dir = g_file_get_parent (destination);
+      if (!ostree_create_temp_hardlink (dir, (GFile*)source, NULL, "link",
+                                        &temp_file, cancellable, error))
+        goto out;
+
+      /* Idiocy, from man rename(2)
+       *
+       * "If oldpath and newpath are existing hard links referring to
+       * the same file, then rename() does nothing, and returns a
+       * success status."
+       *
+       * So we can't make this atomic.  
+       */
+
+      (void) unlink (ot_gfile_get_path_cached (destination));
+
+      if (rename (ot_gfile_get_path_cached (temp_file),
+                  ot_gfile_get_path_cached (destination)) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      g_clear_object (&temp_file);
+    }
+  else
+    {
+      if (link (ot_gfile_get_path_cached (source), ot_gfile_get_path_cached (destination)) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_object (&dir);
+  if (temp_file)
+    (void) unlink (ot_gfile_get_path_cached (temp_file));
+  g_clear_object (&temp_file);
   return ret;
 }
 
 gboolean
 ostree_repo_checkout_tree (OstreeRepo               *self,
                            OstreeRepoCheckoutMode    mode,
+                           OstreeRepoCheckoutOverwriteMode    overwrite_mode,
                            GFile                    *destination,
                            OstreeRepoFile           *source,
                            GFileInfo                *source_info,
@@ -2511,7 +2614,7 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
   if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error))
     goto out;
 
-  if (!checkout_file_from_input (destination, mode, source_info,
+  if (!checkout_file_from_input (destination, mode, overwrite_mode, source_info,
                                  xattrs, NULL,
                                  cancellable, error))
     goto out;
@@ -2541,7 +2644,8 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
 
       if (type == G_FILE_TYPE_DIRECTORY)
         {
-          if (!ostree_repo_checkout_tree (self, mode, dest_path, (OstreeRepoFile*)src_child, file_info,
+          if (!ostree_repo_checkout_tree (self, mode, overwrite_mode,
+                                          dest_path, (OstreeRepoFile*)src_child, file_info,
                                           cancellable, error))
             goto out;
         }
@@ -2554,11 +2658,8 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
               g_clear_object (&object_path);
               object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
 
-              if (link (ot_gfile_get_path_cached (object_path), ot_gfile_get_path_cached (dest_path)) < 0)
-                {
-                  ot_util_set_error_from_errno (error, errno);
-                  goto out;
-                }
+              if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, dest_path, cancellable, error) < 0)
+                goto out;
             }
           else if (priv->mode == OSTREE_REPO_MODE_ARCHIVE)
             {
@@ -2581,7 +2682,7 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
                     goto out;
                 }
 
-              if (!checkout_file_from_input (dest_path, mode, file_info, xattrs, 
+              if (!checkout_file_from_input (dest_path, mode, overwrite_mode, file_info, xattrs, 
                                              content_input, cancellable, error))
                 goto out;
             }
@@ -2590,11 +2691,8 @@ ostree_repo_checkout_tree (OstreeRepo               *self,
               g_clear_object (&object_path);
               object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE);
 
-              if (link (ot_gfile_get_path_cached (object_path), ot_gfile_get_path_cached (dest_path)) < 0)
-                {
-                  ot_util_set_error_from_errno (error, errno);
-                  goto out;
-                }
+              if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, dest_path, cancellable, error) < 0)
+                goto out;
             }
         }
 
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 5206f37..25ef09e 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -201,13 +201,19 @@ gboolean      ostree_repo_stage_commit (OstreeRepo   *self,
                                         GError      **error);
 
 typedef enum {
-  OSTREE_REPO_CHECKOUT_MODE_NONE,
-  OSTREE_REPO_CHECKOUT_MODE_USER
+  OSTREE_REPO_CHECKOUT_MODE_NONE = 0,
+  OSTREE_REPO_CHECKOUT_MODE_USER = 1
 } OstreeRepoCheckoutMode;
 
+typedef enum {
+  OSTREE_REPO_CHECKOUT_OVERWRITE_NONE = 0,
+  OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1
+} OstreeRepoCheckoutOverwriteMode;
+
 gboolean
 ostree_repo_checkout_tree (OstreeRepo               *self,
                            OstreeRepoCheckoutMode    mode,
+                           OstreeRepoCheckoutOverwriteMode    overwrite_mode,
                            GFile                    *destination,
                            OstreeRepoFile           *source,
                            GFileInfo                *source_info,
diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c
index f473072..fbdac13 100644
--- a/src/ostree/ot-builtin-checkout.c
+++ b/src/ostree/ot-builtin-checkout.c
@@ -29,10 +29,12 @@
 
 static gboolean user_mode;
 static char *subpath;
+static gboolean opt_union;
 
 static GOptionEntry options[] = {
   { "user-mode", 'U', 0, G_OPTION_ARG_NONE, &user_mode, "Do not change file ownership or initialze extended attributes", NULL },
   { "subpath", 0, 0, G_OPTION_ARG_STRING, &subpath, "Checkout sub-directory PATH", "PATH" },
+  { "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL },
   { NULL }
 };
 
@@ -99,6 +101,7 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error
     goto out;
 
   if (!ostree_repo_checkout_tree (repo, user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
+                                  opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0,
                                   destf, subtree, file_info, cancellable, error))
     goto out;
 
diff --git a/tests/t0000-basic.sh b/tests/t0000-basic.sh
index 606ef38..2d55e20 100755
--- a/tests/t0000-basic.sh
+++ b/tests/t0000-basic.sh
@@ -19,7 +19,7 @@
 
 set -e
 
-echo "1..27"
+echo "1..28"
 
 . libtest.sh
 
@@ -196,3 +196,13 @@ $OSTREE checkout --subpath /yet/another test2 checkout-test2-subpath
 cd checkout-test2-subpath
 assert_file_has_content tree/green "leaf"
 echo "ok checkout subpath"
+
+cd ${test_tmpdir}
+$OSTREE checkout --union test2 checkout-test2-union
+find checkout-test2-union | wc -l > union-files-count
+$OSTREE checkout --union test2 checkout-test2-union
+find checkout-test2-union | wc -l > union-files-count.new
+cmp union-files-count{,.new}
+cd checkout-test2-union
+assert_file_has_content ./yet/another/tree/green "leaf"
+echo "ok checkout union 1"



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