diff --git a/Makefile-ostree.am b/Makefile-ostree.am index a1d2e5a5..27ace0db 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -22,6 +22,7 @@ bin_PROGRAMS += ostree ostree_SOURCES = src/ostree/main.c \ src/ostree/ot-builtins.h \ src/ostree/ot-builtin-cat.c \ + src/ostree/ot-builtin-config.c \ src/ostree/ot-builtin-checkout.c \ src/ostree/ot-builtin-checksum.c \ src/ostree/ot-builtin-commit.c \ diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index a871498b..3aad7858 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -40,6 +40,16 @@ #include "ostree-libarchive-input-stream.h" #endif +static gboolean +repo_find_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + GFile **out_stored_path, + char **out_pack_checksum, + guint64 *out_pack_offset, + GCancellable *cancellable, + GError **error); + enum { PROP_0, @@ -73,6 +83,8 @@ struct _OstreeRepoPrivate { GKeyFile *config; OstreeRepoMode mode; + OstreeRepo *parent_repo; + GHashTable *pack_index_mappings; GHashTable *pack_data_mappings; }; @@ -83,6 +95,8 @@ ostree_repo_finalize (GObject *object) OstreeRepo *self = OSTREE_REPO (object); OstreeRepoPrivate *priv = GET_PRIVATE (self); + g_clear_object (&priv->parent_repo); + g_clear_object (&priv->repodir); g_clear_object (&priv->tmp_dir); g_clear_object (&priv->pending_dir); @@ -397,7 +411,14 @@ ostree_repo_resolve_rev (OstreeRepo *self, if (child == NULL) { - if (!allow_noent) + if (priv->parent_repo) + { + if (!ostree_repo_resolve_rev (priv->parent_repo, rev, + allow_noent, &ret_rev, + error)) + goto out; + } + else if (!allow_noent) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Rev '%s' not found", rev); @@ -641,8 +662,9 @@ ostree_repo_check (OstreeRepo *self, GError **error) gboolean ret = FALSE; OstreeRepoPrivate *priv = GET_PRIVATE (self); gboolean is_archive; - ot_lfree char *version = NULL;; - ot_lfree char *mode = NULL;; + ot_lfree char *version = NULL; + ot_lfree char *mode = NULL; + ot_lfree char *parent_repo_path = NULL; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -702,6 +724,24 @@ ostree_repo_check (OstreeRepo *self, GError **error) } } + if (!keyfile_get_value_with_default (priv->config, "core", "parent", + NULL, &parent_repo_path, error)) + goto out; + + if (parent_repo_path && parent_repo_path[0]) + { + ot_lobj GFile *parent_repo_f = ot_gfile_new_for_path (parent_repo_path); + + priv->parent_repo = ostree_repo_new (parent_repo_f); + + if (!ostree_repo_check (priv->parent_repo, error)) + { + g_prefix_error (error, "While checking parent repository '%s': ", + ot_gfile_get_path_cached (parent_repo_f)); + goto out; + } + } + priv->inited = TRUE; ret = TRUE; @@ -793,6 +833,7 @@ impl_stage_archive_file_object (OstreeRepo *self, GFileInfo *file_info, GVariant *xattrs, GInputStream *input, + gboolean store_if_packed, const char *expected_checksum, guchar **out_csum, GCancellable *cancellable, @@ -801,6 +842,8 @@ impl_stage_archive_file_object (OstreeRepo *self, gboolean ret = FALSE; OstreeRepoPrivate *priv = GET_PRIVATE (self); const char *actual_checksum; + gboolean have_obj; + gboolean do_commit; ot_lvariant GVariant *archive_metadata = NULL; ot_lobj GFileInfo *temp_info = NULL; ot_lobj GFile *temp_file = NULL; @@ -850,9 +893,25 @@ impl_stage_archive_file_object (OstreeRepo *self, else actual_checksum = g_checksum_get_string (checksum); - if (!commit_tmpfile_trusted (self, actual_checksum, OSTREE_OBJECT_TYPE_FILE, - temp_file, cancellable, error)) - goto out; + if (!store_if_packed) + { + if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_FILE, actual_checksum, &have_obj, + cancellable, error)) + goto out; + + do_commit = !have_obj; + } + else + do_commit = TRUE; + + if (do_commit) + { + if (!commit_tmpfile_trusted (self, actual_checksum, OSTREE_OBJECT_TYPE_FILE, + temp_file, cancellable, error)) + goto out; + + g_clear_object (&temp_file); + } if (checksum) ret_csum = ot_csum_from_gchecksum (checksum); @@ -860,6 +919,8 @@ impl_stage_archive_file_object (OstreeRepo *self, ret = TRUE; ot_transfer_out_value (out_csum, &ret_csum); out: + if (temp_file) + (void) unlink (ot_gfile_get_path_cached (temp_file)); ot_clear_checksum (&checksum); return ret; } @@ -880,6 +941,7 @@ stage_object_impl (OstreeRepo *self, OstreeRepoPrivate *priv = GET_PRIVATE (self); guint64 pack_offset; const char *actual_checksum; + gboolean do_commit; ot_lobj GFileInfo *temp_info = NULL; ot_lobj GFile *temp_file = NULL; ot_lobj GFile *stored_path = NULL; @@ -900,16 +962,16 @@ stage_object_impl (OstreeRepo *self, { if (!store_if_packed) { - if (!ostree_repo_find_object (self, objtype, expected_checksum, - &stored_path, &pack_checksum, &pack_offset, - cancellable, error)) + if (!repo_find_object (self, objtype, expected_checksum, + &stored_path, &pack_checksum, &pack_offset, + cancellable, error)) goto out; } else { - if (!ostree_repo_find_object (self, objtype, expected_checksum, - &stored_path, NULL, NULL, - cancellable, error)) + if (!repo_find_object (self, objtype, expected_checksum, + &stored_path, NULL, NULL, + cancellable, error)) goto out; } } @@ -933,6 +995,7 @@ stage_object_impl (OstreeRepo *self, if (objtype == OSTREE_OBJECT_TYPE_FILE && priv->mode == OSTREE_REPO_MODE_ARCHIVE) { if (!impl_stage_archive_file_object (self, file_info, xattrs, input, + store_if_packed, expected_checksum, out_csum ? &ret_csum : NULL, cancellable, error)) @@ -977,12 +1040,29 @@ stage_object_impl (OstreeRepo *self, expected_checksum, actual_checksum); goto out; } + } - if (!commit_tmpfile_trusted (self, actual_checksum, objtype, - temp_file, cancellable, error)) - goto out; - g_clear_object (&temp_file); + if (!store_if_packed) + { + gboolean have_obj; + + if (!ostree_repo_has_object (self, objtype, actual_checksum, &have_obj, + cancellable, error)) + goto out; + + do_commit = !have_obj; + } + else + do_commit = TRUE; + + if (do_commit) + { + if (!commit_tmpfile_trusted (self, actual_checksum, objtype, + temp_file, cancellable, error)) + goto out; + g_clear_object (&temp_file); + } if (checksum) ret_csum = ot_csum_from_gchecksum (checksum); @@ -3140,6 +3220,7 @@ ostree_repo_load_file (OstreeRepo *self, GError **error) { gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); guchar *pack_data; guint64 pack_len; guint64 pack_offset; @@ -3154,10 +3235,10 @@ ostree_repo_load_file (OstreeRepo *self, ot_lobj GFileInfo *ret_file_info = NULL; ot_lvariant GVariant *ret_xattrs = NULL; - if (!ostree_repo_find_object (self, OSTREE_OBJECT_TYPE_FILE, - checksum, &loose_path, - &pack_checksum, &pack_offset, - cancellable, error)) + if (!repo_find_object (self, OSTREE_OBJECT_TYPE_FILE, + checksum, &loose_path, + &pack_checksum, &pack_offset, + cancellable, error)) goto out; if (loose_path) @@ -3212,6 +3293,15 @@ ostree_repo_load_file (OstreeRepo *self, cancellable, error)) goto out; } + else if (priv->parent_repo) + { + if (!ostree_repo_load_file (priv->parent_repo, checksum, + out_input ? &ret_input : NULL, + out_file_info ? &ret_file_info : NULL, + out_xattrs ? &ret_xattrs : NULL, + cancellable, error)) + goto out; + } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, @@ -3399,15 +3489,15 @@ find_object_in_packs (OstreeRepo *self, return ret; } -gboolean -ostree_repo_find_object (OstreeRepo *self, - OstreeObjectType objtype, - const char *checksum, - GFile **out_stored_path, - char **out_pack_checksum, - guint64 *out_pack_offset, - GCancellable *cancellable, - GError **error) +static gboolean +repo_find_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + GFile **out_stored_path, + char **out_pack_checksum, + guint64 *out_pack_offset, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; guint64 ret_pack_offset = 0; @@ -3445,6 +3535,41 @@ out: return ret; } +gboolean +ostree_repo_has_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + gboolean *out_have_object, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + gboolean ret_have_object; + ot_lobj GFile *loose_path = NULL; + ot_lfree char *pack_checksum = NULL; + + if (!repo_find_object (self, objtype, checksum, &loose_path, + &pack_checksum, NULL, + cancellable, error)) + goto out; + + ret_have_object = (loose_path != NULL) || (pack_checksum != NULL); + + if (!ret_have_object && priv->parent_repo) + { + if (!ostree_repo_has_object (priv->parent_repo, objtype, checksum, + &ret_have_object, cancellable, error)) + goto out; + } + + ret = TRUE; + if (out_have_object) + *out_have_object = ret_have_object; + out: + return ret; +} + gboolean ostree_repo_load_variant_c (OstreeRepo *self, OstreeObjectType objtype, @@ -3473,6 +3598,7 @@ ostree_repo_load_variant (OstreeRepo *self, GError **error) { gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); guchar *pack_data; guint64 pack_len; guint64 object_offset; @@ -3484,9 +3610,9 @@ ostree_repo_load_variant (OstreeRepo *self, g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (objtype), FALSE); - if (!ostree_repo_find_object (self, objtype, sha256, &object_path, - &pack_checksum, &object_offset, - cancellable, error)) + if (!repo_find_object (self, objtype, sha256, &object_path, + &pack_checksum, &object_offset, + cancellable, error)) goto out; /* Prefer loose metadata for now */ @@ -3508,6 +3634,11 @@ ostree_repo_load_variant (OstreeRepo *self, g_variant_get_child (packed_object, 2, "v", &ret_variant); } + else if (priv->parent_repo) + { + if (!ostree_repo_load_variant (priv->parent_repo, objtype, sha256, &ret_variant, error)) + goto out; + } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -3562,12 +3693,22 @@ ostree_repo_list_objects (OstreeRepo *self, { if (!list_loose_objects (self, ret_objects, cancellable, error)) goto out; + if (priv->parent_repo) + { + if (!list_loose_objects (priv->parent_repo, ret_objects, cancellable, error)) + goto out; + } } if (flags & OSTREE_REPO_LIST_OBJECTS_PACKED) { if (!list_packed_objects (self, ret_objects, cancellable, error)) goto out; + if (priv->parent_repo) + { + if (!list_packed_objects (priv->parent_repo, ret_objects, cancellable, error)) + goto out; + } } ret = TRUE; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 9d7e56f1..2896f5f2 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -92,14 +92,12 @@ gboolean ostree_repo_abort_transaction (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean ostree_repo_find_object (OstreeRepo *self, - OstreeObjectType objtype, - const char *checksum, - GFile **out_stored_path, - char **out_pack_checksum, - guint64 *out_pack_offset, - GCancellable *cancellable, - GError **error); +gboolean ostree_repo_has_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + gboolean *out_have_object, + GCancellable *cancellable, + GError **error); gboolean ostree_repo_stage_object (OstreeRepo *self, OstreeObjectType objtype, diff --git a/src/ostree/main.c b/src/ostree/main.c index 95a81b2d..aa5daa47 100644 --- a/src/ostree/main.c +++ b/src/ostree/main.c @@ -31,6 +31,7 @@ static OstreeBuiltin builtins[] = { { "cat", ostree_builtin_cat, 0 }, + { "config", ostree_builtin_config, 0 }, { "checkout", ostree_builtin_checkout, 0 }, { "checksum", ostree_builtin_checksum, OSTREE_BUILTIN_FLAG_NO_REPO }, { "diff", ostree_builtin_diff, 0 }, diff --git a/src/ostree/ostree-pull.c b/src/ostree/ostree-pull.c index 84d43ec6..5fab1b78 100644 --- a/src/ostree/ostree-pull.c +++ b/src/ostree/ostree-pull.c @@ -514,17 +514,12 @@ find_object_ensure_indexes (OtPullData *pull_data, { gboolean ret = FALSE; gboolean ret_is_stored; - ot_lobj GFile *stored_path = NULL; - ot_lfree char *local_pack_checksum = NULL; ot_lfree char *ret_remote_pack_checksum = NULL; - if (!ostree_repo_find_object (pull_data->repo, objtype, checksum, - &stored_path, &local_pack_checksum, NULL, - cancellable, error)) + if (!ostree_repo_has_object (pull_data->repo, objtype, checksum, &ret_is_stored, + cancellable, error)) goto out; - ret_is_stored = (stored_path != NULL || local_pack_checksum != NULL); - if (!ret_is_stored) { if (!pull_data->fetched_packs) diff --git a/src/ostree/ot-builtin-config.c b/src/ostree/ot-builtin-config.c new file mode 100644 index 00000000..8ad6a17c --- /dev/null +++ b/src/ostree/ot-builtin-config.c @@ -0,0 +1,144 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2011 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#include "config.h" + +#include "ot-builtins.h" +#include "ostree.h" + +#include + +static GOptionEntry options[] = { + { NULL } +}; + +static gboolean +split_key_string (const char *k, + char **out_section, + char **out_value, + GError **error) +{ + const char *dot = strchr (k, '.'); + + if (!dot) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Key must be of the form \"sectionname.keyname\""); + return FALSE; + } + + *out_section = g_strndup (k, dot - k); + *out_value = g_strdup (dot + 1); + + return TRUE; +} + +gboolean +ostree_builtin_config (int argc, char **argv, GFile *repo_path, GError **error) +{ + GOptionContext *context = NULL; + gboolean ret = FALSE; + const char *op; + const char *section_key; + const char *value; + ot_lobj OstreeRepo *repo = NULL; + ot_lfree char *section = NULL; + ot_lfree char *key = NULL; + GKeyFile *config = NULL; + + context = g_option_context_new ("- Change configuration settings"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + repo = ostree_repo_new (repo_path); + if (!ostree_repo_check (repo, error)) + goto out; + + if (argc < 2) + { + ot_util_usage_error (context, "OPERATION must be specified", error); + goto out; + } + + op = argv[1]; + + if (!strcmp (op, "set")) + { + if (argc < 4) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "KEY and VALUE must be specified"); + goto out; + } + + section_key = argv[2]; + value = argv[3]; + + if (!split_key_string (section_key, §ion, &key, error)) + goto out; + + config = ostree_repo_copy_config (repo); + g_key_file_set_string (config, section, key, value); + + if (!ostree_repo_write_config (repo, config, error)) + goto out; + } + else if (!strcmp (op, "set")) + { + ot_lfree char *value = NULL; + if (argc < 3) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "KEY must be specified"); + goto out; + } + + section_key = argv[2]; + + if (!split_key_string (section_key, §ion, &key, error)) + goto out; + + config = g_key_file_ref (ostree_repo_get_config (repo)); + + value = g_key_file_get_string (config, section, key, error); + if (value == NULL) + goto out; + + g_print ("%s\n", value); + } + else + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown operation %s", op); + goto out; + } + + ret = TRUE; + out: + if (config) + g_key_file_free (config); + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h index 4cb675cf..a0e5dd8c 100644 --- a/src/ostree/ot-builtins.h +++ b/src/ostree/ot-builtins.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS gboolean ostree_builtin_cat (int argc, char **argv, GFile *repo_path, GError **error); +gboolean ostree_builtin_config (int argc, char **argv, GFile *repo_path, GError **error); gboolean ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error); gboolean ostree_builtin_checksum (int argc, char **argv, GFile *repo_path, GError **error); gboolean ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error); diff --git a/tests/t0000-basic.sh b/tests/t0000-basic.sh index 253421f1..03a657c9 100755 --- a/tests/t0000-basic.sh +++ b/tests/t0000-basic.sh @@ -19,7 +19,7 @@ set -e -echo "1..30" +echo "1..31" . libtest.sh @@ -219,3 +219,13 @@ $OSTREE checkout --link-cache=linkcache test2 test2-checkout-from-link-cache cd test2-checkout-from-link-cache assert_file_has_content ./yet/another/tree/green "leaf" echo "ok checkout link cache" + +cd ${test_tmpdir} +rm -rf shadow-repo +mkdir shadow-repo +ostree --repo=shadow-repo init +ostree --repo=shadow-repo config set core.parent $(pwd)/repo +rm -rf test2-checkout +parent_rev_test2=$(ostree --repo=repo rev-parse test2) +ostree --repo=shadow-repo checkout "${parent_rev_test2}" test2-checkout +echo "ok checkout from shadow repo"