ostree_repo_checkout_tree_at: New API for checkouts

rpm-ostree currently uses ostree_repo_checkout_tree(), which as a side
effect will use the uncompressed objects cache by default.  This is
rather annoying if you're using rpm-ostree on a server-side
repository, because if you then rsync the repo, you'll be syncing out
the uncompressed objects unless you exclude them.

We added the ability to disable the uncompressed cache in the
repository config to fix this, but it's better to allow application
control over this.  The uncompressed cache will in some future version
become opt in as well.

This new API further:
 - Drops the `GFile` usage in favor of `openat` APIs
 - Improves ergonomics by avoiding callers having to query the source
   `GFileInfo` (and carry around a copy of `OSTREE_GIO_FAST_QUERYINFO`)
 - Has a more extensible options structure

Per the comment, I rather crudely have the `ostree checkout` builtin
call both APIs to ensure some testing coverage.

However, I'd like to in the future have easier-to-set-up testing code
that calls `libtest.sh` to set up dummy data.
This commit is contained in:
Colin Walters 2015-04-06 18:29:01 -04:00
parent 115e05746b
commit c2aabcac3b
5 changed files with 236 additions and 78 deletions

View File

@ -277,6 +277,7 @@ ostree_repo_write_commit_detached_metadata
OstreeRepoCheckoutMode OstreeRepoCheckoutMode
OstreeRepoCheckoutOverwriteMode OstreeRepoCheckoutOverwriteMode
ostree_repo_checkout_tree ostree_repo_checkout_tree
ostree_repo_checkout_tree_at
ostree_repo_checkout_gc ostree_repo_checkout_gc
ostree_repo_read_commit ostree_repo_read_commit
OstreeRepoListObjectsFlags OstreeRepoListObjectsFlags

View File

@ -173,7 +173,7 @@ write_regular_file_content (OstreeRepo *self,
static gboolean static gboolean
checkout_file_from_input_at (OstreeRepo *self, checkout_file_from_input_at (OstreeRepo *self,
OstreeRepoCheckoutMode mode, OstreeRepoCheckoutOptions *options,
GFileInfo *file_info, GFileInfo *file_info,
GVariant *xattrs, GVariant *xattrs,
GInputStream *input, GInputStream *input,
@ -197,7 +197,7 @@ checkout_file_from_input_at (OstreeRepo *self,
goto out; goto out;
} }
if (mode != OSTREE_REPO_CHECKOUT_MODE_USER) if (options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{ {
if (G_UNLIKELY (fchownat (destination_dfd, destination_name, if (G_UNLIKELY (fchownat (destination_dfd, destination_name,
g_file_info_get_attribute_uint32 (file_info, "unix::uid"), g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
@ -224,7 +224,7 @@ checkout_file_from_input_at (OstreeRepo *self,
file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Don't make setuid files on checkout when we're doing --user */ /* Don't make setuid files on checkout when we're doing --user */
if (mode == OSTREE_REPO_CHECKOUT_MODE_USER) if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
file_mode &= ~(S_ISUID|S_ISGID); file_mode &= ~(S_ISUID|S_ISGID);
do do
@ -238,7 +238,7 @@ checkout_file_from_input_at (OstreeRepo *self,
temp_out = g_unix_output_stream_new (fd, TRUE); temp_out = g_unix_output_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */ fd = -1; /* Transfer ownership */
if (!write_regular_file_content (self, mode, temp_out, file_info, xattrs, input, if (!write_regular_file_content (self, options->mode, temp_out, file_info, xattrs, input,
cancellable, error)) cancellable, error))
goto out; goto out;
} }
@ -256,7 +256,7 @@ checkout_file_from_input_at (OstreeRepo *self,
*/ */
static gboolean static gboolean
checkout_file_unioning_from_input_at (OstreeRepo *repo, checkout_file_unioning_from_input_at (OstreeRepo *repo,
OstreeRepoCheckoutMode mode, OstreeRepoCheckoutOptions *options,
GFileInfo *file_info, GFileInfo *file_info,
GVariant *xattrs, GVariant *xattrs,
GInputStream *input, GInputStream *input,
@ -290,7 +290,7 @@ checkout_file_unioning_from_input_at (OstreeRepo *repo,
file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Don't make setuid files on checkout when we're doing --user */ /* Don't make setuid files on checkout when we're doing --user */
if (mode == OSTREE_REPO_CHECKOUT_MODE_USER) if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
file_mode &= ~(S_ISUID|S_ISGID); file_mode &= ~(S_ISUID|S_ISGID);
if (!gs_file_open_in_tmpdir_at (destination_dfd, file_mode, if (!gs_file_open_in_tmpdir_at (destination_dfd, file_mode,
@ -298,7 +298,7 @@ checkout_file_unioning_from_input_at (OstreeRepo *repo,
cancellable, error)) cancellable, error))
goto out; goto out;
if (!write_regular_file_content (repo, mode, temp_out, file_info, xattrs, input, if (!write_regular_file_content (repo, options->mode, temp_out, file_info, xattrs, input,
cancellable, error)) cancellable, error))
goto out; goto out;
} }
@ -319,8 +319,7 @@ checkout_file_unioning_from_input_at (OstreeRepo *repo,
static gboolean static gboolean
checkout_file_hardlink (OstreeRepo *self, checkout_file_hardlink (OstreeRepo *self,
OstreeRepoCheckoutMode mode, OstreeRepoCheckoutOptions *options,
OstreeRepoCheckoutOverwriteMode overwrite_mode,
const char *loose_path, const char *loose_path,
int destination_dfd, int destination_dfd,
const char *destination_name, const char *destination_name,
@ -348,7 +347,7 @@ checkout_file_hardlink (OstreeRepo *self,
{ {
ret_was_supported = FALSE; ret_was_supported = FALSE;
} }
else if (errno == EEXIST && overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES) else if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
{ {
/* Idiocy, from man rename(2) /* Idiocy, from man rename(2)
* *
@ -377,18 +376,18 @@ checkout_file_hardlink (OstreeRepo *self,
static gboolean static gboolean
checkout_one_file_at (OstreeRepo *repo, checkout_one_file_at (OstreeRepo *repo,
OstreeRepoCheckoutOptions *options,
GFile *source, GFile *source,
GFileInfo *source_info, GFileInfo *source_info,
int destination_dfd, int destination_dfd,
const char *destination_name, const char *destination_name,
OstreeRepoCheckoutMode mode,
OstreeRepoCheckoutOverwriteMode overwrite_mode,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
const char *checksum; const char *checksum;
gboolean is_symlink; gboolean is_symlink;
gboolean can_cache;
gboolean did_hardlink = FALSE; gboolean did_hardlink = FALSE;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX]; char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
gs_unref_object GInputStream *input = NULL; gs_unref_object GInputStream *input = NULL;
@ -408,12 +407,14 @@ checkout_one_file_at (OstreeRepo *repo,
while (current_repo) while (current_repo)
{ {
gboolean is_bare = ((current_repo->mode == OSTREE_REPO_MODE_BARE gboolean is_bare = ((current_repo->mode == OSTREE_REPO_MODE_BARE
&& mode == OSTREE_REPO_CHECKOUT_MODE_NONE) || && options->mode == OSTREE_REPO_CHECKOUT_MODE_NONE) ||
(current_repo->mode == OSTREE_REPO_MODE_BARE_USER (current_repo->mode == OSTREE_REPO_MODE_BARE_USER
&& mode == OSTREE_REPO_CHECKOUT_MODE_USER)); && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER));
gboolean current_can_cache = (options->enable_uncompressed_cache
&& current_repo->enable_uncompressed_cache);
gboolean is_archive_z2_with_cache = (current_repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 gboolean is_archive_z2_with_cache = (current_repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2
&& mode == OSTREE_REPO_CHECKOUT_MODE_USER && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER
&& current_repo->enable_uncompressed_cache); && current_can_cache);
/* But only under these conditions */ /* But only under these conditions */
if (is_bare || is_archive_z2_with_cache) if (is_bare || is_archive_z2_with_cache)
@ -422,7 +423,8 @@ checkout_one_file_at (OstreeRepo *repo,
the cache, which is in "bare" form */ the cache, which is in "bare" form */
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE); _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE);
if (!checkout_file_hardlink (current_repo, if (!checkout_file_hardlink (current_repo,
mode, overwrite_mode, loose_path_buf, options,
loose_path_buf,
destination_dfd, destination_name, destination_dfd, destination_name,
TRUE, &did_hardlink, TRUE, &did_hardlink,
cancellable, error)) cancellable, error))
@ -434,14 +436,17 @@ checkout_one_file_at (OstreeRepo *repo,
} }
} }
can_cache = (options->enable_uncompressed_cache
&& repo->enable_uncompressed_cache);
/* Ok, if we're archive-z2 and we didn't find an object, uncompress /* Ok, if we're archive-z2 and we didn't find an object, uncompress
* it now, stick it in the cache, and then hardlink to that. * it now, stick it in the cache, and then hardlink to that.
*/ */
if (!is_symlink if (can_cache
&& !is_symlink
&& !did_hardlink && !did_hardlink
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2
&& mode == OSTREE_REPO_CHECKOUT_MODE_USER && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
&& repo->enable_uncompressed_cache)
{ {
if (!ostree_repo_load_file (repo, checksum, &input, NULL, NULL, if (!ostree_repo_load_file (repo, checksum, &input, NULL, NULL,
cancellable, error)) cancellable, error))
@ -485,7 +490,7 @@ checkout_one_file_at (OstreeRepo *repo,
} }
g_mutex_unlock (&repo->cache_lock); g_mutex_unlock (&repo->cache_lock);
if (!checkout_file_hardlink (repo, mode, overwrite_mode, loose_path_buf, if (!checkout_file_hardlink (repo, options, loose_path_buf,
destination_dfd, destination_name, destination_dfd, destination_name,
FALSE, &did_hardlink, FALSE, &did_hardlink,
cancellable, error)) cancellable, error))
@ -502,9 +507,9 @@ checkout_one_file_at (OstreeRepo *repo,
cancellable, error)) cancellable, error))
goto out; goto out;
if (overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES) if (options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
{ {
if (!checkout_file_unioning_from_input_at (repo, mode, source_info, xattrs, input, if (!checkout_file_unioning_from_input_at (repo, options, source_info, xattrs, input,
destination_dfd, destination_dfd,
destination_name, destination_name,
cancellable, error)) cancellable, error))
@ -515,7 +520,7 @@ checkout_one_file_at (OstreeRepo *repo,
} }
else else
{ {
if (!checkout_file_from_input_at (repo, mode, source_info, xattrs, input, if (!checkout_file_from_input_at (repo, options, source_info, xattrs, input,
destination_dfd, destination_dfd,
destination_name, destination_name,
cancellable, error)) cancellable, error))
@ -554,8 +559,7 @@ checkout_one_file_at (OstreeRepo *repo,
*/ */
static gboolean static gboolean
checkout_tree_at (OstreeRepo *self, checkout_tree_at (OstreeRepo *self,
OstreeRepoCheckoutMode mode, OstreeRepoCheckoutOptions *options,
OstreeRepoCheckoutOverwriteMode overwrite_mode,
int destination_parent_fd, int destination_parent_fd,
const char *destination_name, const char *destination_name,
OstreeRepoFile *source, OstreeRepoFile *source,
@ -579,7 +583,7 @@ checkout_tree_at (OstreeRepo *self,
while (G_UNLIKELY (res == -1 && errno == EINTR)); while (G_UNLIKELY (res == -1 && errno == EINTR));
if (res == -1) if (res == -1)
{ {
if (errno == EEXIST && overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES) if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
did_exist = TRUE; did_exist = TRUE;
else else
{ {
@ -594,7 +598,7 @@ checkout_tree_at (OstreeRepo *self,
goto out; goto out;
/* Set the xattrs now, so any derived labeling works */ /* Set the xattrs now, so any derived labeling works */
if (!did_exist && mode != OSTREE_REPO_CHECKOUT_MODE_USER) if (!did_exist && options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{ {
if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error)) if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error))
goto out; goto out;
@ -608,11 +612,11 @@ checkout_tree_at (OstreeRepo *self,
if (g_file_info_get_file_type (source_info) != G_FILE_TYPE_DIRECTORY) if (g_file_info_get_file_type (source_info) != G_FILE_TYPE_DIRECTORY)
{ {
ret = checkout_one_file_at (self, (GFile *) source, ret = checkout_one_file_at (self, options,
(GFile *) source,
source_info, source_info,
destination_dfd, destination_dfd,
g_file_info_get_name (source_info), g_file_info_get_name (source_info),
mode, TRUE,
cancellable, error); cancellable, error);
goto out; goto out;
} }
@ -640,7 +644,7 @@ checkout_tree_at (OstreeRepo *self,
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
{ {
if (!checkout_tree_at (self, mode, overwrite_mode, if (!checkout_tree_at (self, options,
destination_dfd, name, destination_dfd, name,
(OstreeRepoFile*)src_child, file_info, (OstreeRepoFile*)src_child, file_info,
cancellable, error)) cancellable, error))
@ -648,9 +652,9 @@ checkout_tree_at (OstreeRepo *self,
} }
else else
{ {
if (!checkout_one_file_at (self, src_child, file_info, if (!checkout_one_file_at (self, options,
src_child, file_info,
destination_dfd, name, destination_dfd, name,
mode, overwrite_mode,
cancellable, error)) cancellable, error))
goto out; goto out;
} }
@ -672,7 +676,7 @@ checkout_tree_at (OstreeRepo *self,
} }
} }
if (!did_exist && mode != OSTREE_REPO_CHECKOUT_MODE_USER) if (!did_exist && options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{ {
do do
res = fchown (destination_dfd, res = fchown (destination_dfd,
@ -748,13 +752,89 @@ ostree_repo_checkout_tree (OstreeRepo *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
return checkout_tree_at (self, mode, overwrite_mode, OstreeRepoCheckoutOptions options = { 0, };
AT_FDCWD,
gs_file_get_path_cached (destination), options.mode = mode;
options.overwrite_mode = overwrite_mode;
/* Backwards compatibility */
options.enable_uncompressed_cache = TRUE;
return checkout_tree_at (self, &options,
AT_FDCWD, gs_file_get_path_cached (destination),
source, source_info, source, source_info,
cancellable, error); cancellable, error);
} }
/**
* ostree_repo_checkout_tree_at:
* @self: Repo
* @options: (allow-none): Options
* @destination_dfd: Directory FD for destination
* @destination_path: Directory for destination
* @commit: Checksum for commit
* @subpath: (allow-none): Subdirectory path
* @cancellable: Cancellable
* @error: Error
*
* Similar to ostree_repo_checkout_tree(), but uses directory-relative
* paths for the destination, uses a new `OstreeRepoCheckoutOptions`,
* and takes a commit checksum and optional subpath pair, rather than
* requiring use of `GFile` APIs for the caller.
*
* Note in addition that unlike ostree_repo_checkout_tree(), the
* default is not to use the repository-internal uncompressed objects
* cache.
*/
gboolean
ostree_repo_checkout_tree_at (OstreeRepo *self,
OstreeRepoCheckoutOptions *options,
int destination_dfd,
const char *destination_path,
const char *commit,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile* commit_root = NULL;
gs_unref_object GFile* target_dir = NULL;
gs_unref_object GFileInfo* target_info = NULL;
OstreeRepoCheckoutOptions default_options = { 0, };
if (!options)
{
default_options.subpath = NULL;
options = &default_options;
}
commit_root = (GFile*) _ostree_repo_file_new_for_commit (self, commit, error);
if (!commit_root)
goto out;
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)commit_root, error))
goto out;
if (options->subpath && strcmp (options->subpath, "/") != 0)
target_dir = g_file_get_child (commit_root, options->subpath);
else
target_dir = g_object_ref (commit_root);
target_info = g_file_query_info (target_dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!target_info)
goto out;
if (!checkout_tree_at (self, options,
destination_dfd,
destination_path,
(OstreeRepoFile*)target_dir, target_info,
cancellable, error))
goto out;
ret = TRUE;
out:
return ret;
}
/** /**
* ostree_repo_checkout_gc: * ostree_repo_checkout_gc:
* @self: Repo * @self: Repo

View File

@ -449,6 +449,36 @@ ostree_repo_checkout_tree (OstreeRepo *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
/**
* OstreeRepoCheckoutOptions:
*
* An extensible options structure controlling checkout. Ensure that
* you have entirely zeroed the structure, then set just the desired
* options. This is used by ostree_repo_checkout_tree_at() which
* supercedes previous separate enumeration usage in
* ostree_repo_checkout_tree().
*/
typedef struct {
OstreeRepoCheckoutMode mode;
OstreeRepoCheckoutOverwriteMode overwrite_mode;
guint enable_uncompressed_cache : 1;
guint unused : 31;
const char *subpath;
guint unused_uints[6];
gpointer unused_ptrs[8];
} OstreeRepoCheckoutOptions;
gboolean ostree_repo_checkout_tree_at (OstreeRepo *self,
OstreeRepoCheckoutOptions *options,
int destination_dfd,
const char *destination_path,
const char *commit,
GCancellable *cancellable,
GError **error);
gboolean ostree_repo_checkout_gc (OstreeRepo *self, gboolean ostree_repo_checkout_gc (OstreeRepo *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);

View File

@ -33,6 +33,7 @@
static gboolean opt_user_mode; static gboolean opt_user_mode;
static gboolean opt_allow_noent; static gboolean opt_allow_noent;
static gboolean opt_disable_cache;
static char *opt_subpath; static char *opt_subpath;
static gboolean opt_union; static gboolean opt_union;
static gboolean opt_from_stdin; static gboolean opt_from_stdin;
@ -57,6 +58,7 @@ parse_fsync_cb (const char *option_name,
static GOptionEntry options[] = { static GOptionEntry options[] = {
{ "user-mode", 'U', 0, G_OPTION_ARG_NONE, &opt_user_mode, "Do not change file ownership or initialize extended attributes", NULL }, { "user-mode", 'U', 0, G_OPTION_ARG_NONE, &opt_user_mode, "Do not change file ownership or initialize extended attributes", NULL },
{ "disable-cache", 0, 0, G_OPTION_ARG_NONE, &opt_disable_cache, "Do not update or use the internal repository uncompressed object cache", NULL },
{ "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" }, { "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" },
{ "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL }, { "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL },
{ "allow-noent", 0, 0, G_OPTION_ARG_NONE, &opt_allow_noent, "Do nothing if specified path does not exist", NULL }, { "allow-noent", 0, 0, G_OPTION_ARG_NONE, &opt_allow_noent, "Do nothing if specified path does not exist", NULL },
@ -70,46 +72,76 @@ static gboolean
process_one_checkout (OstreeRepo *repo, process_one_checkout (OstreeRepo *repo,
const char *resolved_commit, const char *resolved_commit,
const char *subpath, const char *subpath,
GFile *target, const char *destination,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
GError *tmp_error = NULL;
gs_unref_object GFile *root = NULL;
gs_unref_object GFile *subtree = NULL;
gs_unref_object GFileInfo *file_info = NULL;
if (!ostree_repo_read_commit (repo, resolved_commit, &root, NULL, cancellable, error)) /* This strange code structure is to preserve testing
goto out; * coverage of both `ostree_repo_checkout_tree` and
* `ostree_repo_checkout_tree_at` until such time as we have a more
if (subpath) * convenient infrastructure for testing C APIs with data.
subtree = g_file_resolve_relative_path (root, subpath); */
else if (opt_disable_cache)
subtree = g_object_ref (root);
file_info = g_file_query_info (subtree, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, &tmp_error);
if (!file_info)
{ {
if (opt_allow_noent OstreeRepoCheckoutOptions options = { 0, };
&& g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{ if (opt_user_mode)
g_clear_error (&tmp_error); options.mode = OSTREE_REPO_CHECKOUT_MODE_USER;
ret = TRUE; if (opt_union)
} options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
else if (subpath)
{ options.subpath = subpath;
g_propagate_error (error, tmp_error);
}
goto out;
}
if (!ostree_repo_checkout_tree (repo, opt_user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0, if (!ostree_repo_checkout_tree_at (repo, &options,
target, OSTREE_REPO_FILE (subtree), file_info, cancellable, error)) AT_FDCWD, destination,
goto out; resolved_commit,
cancellable, error))
goto out;
}
else
{
GError *tmp_error = NULL;
gs_unref_object GFile *root = NULL;
gs_unref_object GFile *subtree = NULL;
gs_unref_object GFileInfo *file_info = NULL;
gs_unref_object GFile *destination_file = g_file_new_for_path (destination);
if (!ostree_repo_read_commit (repo, resolved_commit, &root, NULL, cancellable, error))
goto out;
if (subpath)
subtree = g_file_resolve_relative_path (root, subpath);
else
subtree = g_object_ref (root);
file_info = g_file_query_info (subtree, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, &tmp_error);
if (!file_info)
{
if (opt_allow_noent
&& g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&tmp_error);
ret = TRUE;
}
else
{
g_propagate_error (error, tmp_error);
}
goto out;
}
if (!ostree_repo_checkout_tree (repo, opt_user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0,
destination_file,
OSTREE_REPO_FILE (subtree), file_info,
cancellable, error))
goto out;
}
ret = TRUE; ret = TRUE;
out: out:
@ -118,7 +150,7 @@ process_one_checkout (OstreeRepo *repo,
static gboolean static gboolean
process_many_checkouts (OstreeRepo *repo, process_many_checkouts (OstreeRepo *repo,
GFile *target, const char *target,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
@ -198,7 +230,6 @@ ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GErro
const char *commit; const char *commit;
const char *destination; const char *destination;
gs_free char *resolved_commit = NULL; gs_free char *resolved_commit = NULL;
gs_unref_object GFile *checkout_target = NULL;
context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree"); context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree");
@ -221,9 +252,8 @@ ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GErro
if (opt_from_stdin || opt_from_file) if (opt_from_stdin || opt_from_file)
{ {
destination = argv[1]; destination = argv[1];
checkout_target = g_file_new_for_path (destination);
if (!process_many_checkouts (repo, checkout_target, cancellable, error)) if (!process_many_checkouts (repo, destination, cancellable, error))
goto out; goto out;
} }
else else
@ -237,10 +267,8 @@ ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GErro
if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error))
goto out; goto out;
checkout_target = g_file_new_for_path (destination);
if (!process_one_checkout (repo, resolved_commit, opt_subpath, if (!process_one_checkout (repo, resolved_commit, opt_subpath,
checkout_target, destination,
cancellable, error)) cancellable, error))
goto out; goto out;
} }

View File

@ -19,7 +19,7 @@
set -e set -e
echo "1..47" echo "1..48"
$OSTREE checkout test2 checkout-test2 $OSTREE checkout test2 checkout-test2
echo "ok checkout" echo "ok checkout"
@ -362,6 +362,25 @@ if $OSTREE commit -b test2 -s "Attempt to commit a FIFO" 2>../errmsg; then
fi fi
echo "ok commit of fifo was rejected" echo "ok commit of fifo was rejected"
cd ${test_tmpdir}
rm repo2 -rf
mkdir repo2
${CMD_PREFIX} ostree --repo=repo2 init --mode=archive-z2
${CMD_PREFIX} ostree --repo=repo2 pull-local repo
rm -rf test2-checkout
${CMD_PREFIX} ostree --repo=repo2 checkout -U --disable-cache test2 test2-checkout
if test -d repo2/uncompressed-objects-cache; then
ls repo2/uncompressed-objects-cache > ls.txt
if test -s ls.txt; then
assert_not_reached "repo has uncompressed objects"
fi
fi
rm test2-checkout -rf
${CMD_PREFIX} ostree --repo=repo2 checkout -U test2 test2-checkout
assert_file_has_content test2-checkout/baz/cow moo
assert_has_dir repo2/uncompressed-objects-cache
echo "ok disable cache checkout"
cd ${test_tmpdir} cd ${test_tmpdir}
rm -rf test2-checkout rm -rf test2-checkout
mkdir -p test2-checkout mkdir -p test2-checkout