core: Add --union mode to checkout
This is another step towards ostbuild using this instead of the "compose" builtin.
This commit is contained in:
parent
83fb6d56e1
commit
76bc35186e
|
|
@ -915,6 +915,9 @@ ostree_create_temp_file_from_input (GFile *dir,
|
||||||
/* 128 attempts seems reasonable... */
|
/* 128 attempts seems reasonable... */
|
||||||
for (i = 0; i < 128; i++)
|
for (i = 0; i < 128; i++)
|
||||||
{
|
{
|
||||||
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
g_free (possible_name);
|
g_free (possible_name);
|
||||||
possible_name = subst_xxxxxx (tmp_name->str);
|
possible_name = subst_xxxxxx (tmp_name->str);
|
||||||
g_clear_object (&possible_file);
|
g_clear_object (&possible_file);
|
||||||
|
|
@ -941,7 +944,7 @@ ostree_create_temp_file_from_input (GFile *dir,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 128)
|
if (i >= 128)
|
||||||
{
|
{
|
||||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
"Exhausted 128 attempts to create a temporary file");
|
"Exhausted 128 attempts to create a temporary file");
|
||||||
|
|
@ -990,3 +993,64 @@ ostree_create_temp_regular_file (GFile *dir,
|
||||||
g_clear_object (&ret_stream);
|
g_clear_object (&ret_stream);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,14 @@ gboolean ostree_create_temp_regular_file (GFile *dir,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error);
|
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 *ostree_create_archive_file_metadata (GFileInfo *file_info,
|
||||||
GVariant *xattrs);
|
GVariant *xattrs);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2453,6 +2453,7 @@ ostree_repo_iter_objects (OstreeRepo *self,
|
||||||
static gboolean
|
static gboolean
|
||||||
checkout_file_from_input (GFile *file,
|
checkout_file_from_input (GFile *file,
|
||||||
OstreeRepoCheckoutMode mode,
|
OstreeRepoCheckoutMode mode,
|
||||||
|
OstreeRepoCheckoutOverwriteMode overwrite_mode,
|
||||||
GFileInfo *finfo,
|
GFileInfo *finfo,
|
||||||
GVariant *xattrs,
|
GVariant *xattrs,
|
||||||
GInputStream *input,
|
GInputStream *input,
|
||||||
|
|
@ -2460,6 +2461,9 @@ checkout_file_from_input (GFile *file,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
gboolean ret = FALSE;
|
gboolean ret = FALSE;
|
||||||
|
GError *temp_error = NULL;
|
||||||
|
GFile *dir = NULL;
|
||||||
|
GFile *temp_file = NULL;
|
||||||
GFileInfo *temp_info = NULL;
|
GFileInfo *temp_info = NULL;
|
||||||
|
|
||||||
if (mode == OSTREE_REPO_CHECKOUT_MODE_USER)
|
if (mode == OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||||
|
|
@ -2475,20 +2479,119 @@ checkout_file_from_input (GFile *file,
|
||||||
xattrs = NULL;
|
xattrs = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ostree_create_file_from_input (file, temp_info ? temp_info : finfo,
|
if (overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
|
||||||
xattrs, input, OSTREE_OBJECT_TYPE_RAW_FILE,
|
{
|
||||||
NULL, cancellable, error))
|
if (g_file_info_get_file_type (temp_info ? temp_info : finfo) == G_FILE_TYPE_DIRECTORY)
|
||||||
goto out;
|
{
|
||||||
|
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;
|
ret = TRUE;
|
||||||
out:
|
out:
|
||||||
g_clear_object (&temp_info);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
ostree_repo_checkout_tree (OstreeRepo *self,
|
ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
OstreeRepoCheckoutMode mode,
|
OstreeRepoCheckoutMode mode,
|
||||||
|
OstreeRepoCheckoutOverwriteMode overwrite_mode,
|
||||||
GFile *destination,
|
GFile *destination,
|
||||||
OstreeRepoFile *source,
|
OstreeRepoFile *source,
|
||||||
GFileInfo *source_info,
|
GFileInfo *source_info,
|
||||||
|
|
@ -2511,7 +2614,7 @@ ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error))
|
if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!checkout_file_from_input (destination, mode, source_info,
|
if (!checkout_file_from_input (destination, mode, overwrite_mode, source_info,
|
||||||
xattrs, NULL,
|
xattrs, NULL,
|
||||||
cancellable, error))
|
cancellable, error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -2541,7 +2644,8 @@ ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
|
|
||||||
if (type == G_FILE_TYPE_DIRECTORY)
|
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))
|
cancellable, error))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -2554,11 +2658,8 @@ ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
g_clear_object (&object_path);
|
g_clear_object (&object_path);
|
||||||
object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
|
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)
|
if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, dest_path, cancellable, error) < 0)
|
||||||
{
|
goto out;
|
||||||
ot_util_set_error_from_errno (error, errno);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (priv->mode == OSTREE_REPO_MODE_ARCHIVE)
|
else if (priv->mode == OSTREE_REPO_MODE_ARCHIVE)
|
||||||
{
|
{
|
||||||
|
|
@ -2581,7 +2682,7 @@ ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
goto out;
|
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))
|
content_input, cancellable, error))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -2590,11 +2691,8 @@ ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
g_clear_object (&object_path);
|
g_clear_object (&object_path);
|
||||||
object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE);
|
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)
|
if (!checkout_file_hardlink (self, mode, overwrite_mode, object_path, dest_path, cancellable, error) < 0)
|
||||||
{
|
goto out;
|
||||||
ot_util_set_error_from_errno (error, errno);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,13 +201,19 @@ gboolean ostree_repo_stage_commit (OstreeRepo *self,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OSTREE_REPO_CHECKOUT_MODE_NONE,
|
OSTREE_REPO_CHECKOUT_MODE_NONE = 0,
|
||||||
OSTREE_REPO_CHECKOUT_MODE_USER
|
OSTREE_REPO_CHECKOUT_MODE_USER = 1
|
||||||
} OstreeRepoCheckoutMode;
|
} OstreeRepoCheckoutMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE = 0,
|
||||||
|
OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1
|
||||||
|
} OstreeRepoCheckoutOverwriteMode;
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
ostree_repo_checkout_tree (OstreeRepo *self,
|
ostree_repo_checkout_tree (OstreeRepo *self,
|
||||||
OstreeRepoCheckoutMode mode,
|
OstreeRepoCheckoutMode mode,
|
||||||
|
OstreeRepoCheckoutOverwriteMode overwrite_mode,
|
||||||
GFile *destination,
|
GFile *destination,
|
||||||
OstreeRepoFile *source,
|
OstreeRepoFile *source,
|
||||||
GFileInfo *source_info,
|
GFileInfo *source_info,
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,12 @@
|
||||||
|
|
||||||
static gboolean user_mode;
|
static gboolean user_mode;
|
||||||
static char *subpath;
|
static char *subpath;
|
||||||
|
static gboolean opt_union;
|
||||||
|
|
||||||
static GOptionEntry options[] = {
|
static GOptionEntry options[] = {
|
||||||
{ "user-mode", 'U', 0, G_OPTION_ARG_NONE, &user_mode, "Do not change file ownership or initialze extended attributes", NULL },
|
{ "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" },
|
{ "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 }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -99,6 +101,7 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!ostree_repo_checkout_tree (repo, user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
|
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))
|
destf, subtree, file_info, cancellable, error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "1..27"
|
echo "1..28"
|
||||||
|
|
||||||
. libtest.sh
|
. libtest.sh
|
||||||
|
|
||||||
|
|
@ -196,3 +196,13 @@ $OSTREE checkout --subpath /yet/another test2 checkout-test2-subpath
|
||||||
cd checkout-test2-subpath
|
cd checkout-test2-subpath
|
||||||
assert_file_has_content tree/green "leaf"
|
assert_file_has_content tree/green "leaf"
|
||||||
echo "ok checkout subpath"
|
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"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue