From 12e916466c99983f296903eebf7994105009da99 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 9 Sep 2016 14:52:18 -0400 Subject: [PATCH 01/17] static-delta: add some error handling We make _ostree_parse_delta_name() a bit more defensive since it handles user input. Closes: #504 Closes: #505 Approved by: cgwalters --- src/libostree/ostree-core-private.h | 5 ++- src/libostree/ostree-core.c | 24 +++++++++-- src/libostree/ostree-repo-static-delta-core.c | 14 +++++-- src/libostree/ostree-repo.c | 4 +- tests/test-checksum.c | 42 +++++++++++++++---- tests/test-delta.sh | 9 +++- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/libostree/ostree-core-private.h b/src/libostree/ostree-core-private.h index cb4d953d..0c5fb0eb 100644 --- a/src/libostree/ostree-core-private.h +++ b/src/libostree/ostree-core-private.h @@ -121,10 +121,11 @@ static inline char * _ostree_get_commitpartial_path (const char *checksum) return g_strconcat ("state/", checksum, ".commitpartial", NULL); } -void +gboolean _ostree_parse_delta_name (const char *delta_name, char **out_from, - char **out_to); + char **out_to, + GError **error); void _ostree_loose_path (char *buf, diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 6e3c4e2e..e3f0a771 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -1584,12 +1584,26 @@ _ostree_get_relative_static_delta_part_path (const char *from, return _ostree_get_relative_static_delta_path (from, to, partstr); } -void -_ostree_parse_delta_name (const char *delta_name, +gboolean +_ostree_parse_delta_name (const char *delta_name, char **out_from, - char **out_to) + char **out_to, + GError **error) { - g_auto(GStrv) parts = g_strsplit (delta_name, "-", 2); + g_auto(GStrv) parts = NULL; + g_return_val_if_fail (delta_name != NULL, FALSE); + + parts = g_strsplit (delta_name, "-", 2); + + /* NB: if delta_name is "", parts[0] is NULL, but the error + * validate_checksum_string() gives for "" is nice enough, + * so we just coerce it here */ + if (!ostree_validate_checksum_string (parts[0] ?: "", error)) + return FALSE; + + if (parts[0] && parts[1] && + !ostree_validate_checksum_string (parts[1], error)) + return FALSE; *out_from = *out_to = NULL; if (parts[0] && parts[1]) @@ -1601,6 +1615,8 @@ _ostree_parse_delta_name (const char *delta_name, { ot_transfer_out_value (out_to, &parts[0]); } + + return TRUE; } /* diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c index 46fa5f86..82c80a2a 100644 --- a/src/libostree/ostree-repo-static-delta-core.c +++ b/src/libostree/ostree-repo-static-delta-core.c @@ -795,7 +795,9 @@ _ostree_repo_static_delta_delete (OstreeRepo *self, g_autofree char *deltadir = NULL; struct stat buf; - _ostree_parse_delta_name (delta_id, &from, &to); + if (!_ostree_parse_delta_name (delta_id, &from, &to, error)) + goto out; + deltadir = _ostree_get_relative_static_delta_path (from, to, NULL); if (fstatat (self->repo_dir_fd, deltadir, &buf, 0) != 0) @@ -830,7 +832,9 @@ _ostree_repo_static_delta_query_exists (OstreeRepo *self, g_autofree char *superblock_path = NULL; struct stat stbuf; - _ostree_parse_delta_name (delta_id, &from, &to); + if (!_ostree_parse_delta_name (delta_id, &from, &to, error)) + return FALSE; + superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to); if (fstatat (self->repo_dir_fd, superblock_path, &stbuf, 0) < 0) @@ -854,7 +858,7 @@ gboolean _ostree_repo_static_delta_dump (OstreeRepo *self, const char *delta_id, GCancellable *cancellable, - GError **error) + GError **error) { gboolean ret = FALSE; g_autofree char *from = NULL; @@ -868,7 +872,9 @@ _ostree_repo_static_delta_dump (OstreeRepo *self, OstreeDeltaEndianness endianness; gboolean swap_endian = FALSE; - _ostree_parse_delta_name (delta_id, &from, &to); + if (!_ostree_parse_delta_name (delta_id, &from, &to, error)) + goto out; + superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to); if (!ot_util_variant_map_at (self->repo_dir_fd, superblock_path, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 59bfbf9e..e4e1ecbf 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -4701,7 +4701,9 @@ ostree_repo_regenerate_summary (OstreeRepo *self, glnx_fd_close int superblock_file_fd = -1; g_autoptr(GInputStream) in_stream = NULL; - _ostree_parse_delta_name (delta_names->pdata[i], &from, &to); + if (!_ostree_parse_delta_name (delta_names->pdata[i], &from, &to, error)) + goto out; + superblock = _ostree_get_relative_static_delta_superblock_path ((from && from[0]) ? from : NULL, to); superblock_file_fd = openat (self->repo_dir_fd, superblock, O_RDONLY | O_CLOEXEC); if (superblock_file_fd == -1) diff --git a/tests/test-checksum.c b/tests/test-checksum.c index 9537804d..7d2a0cea 100644 --- a/tests/test-checksum.c +++ b/tests/test-checksum.c @@ -31,25 +31,49 @@ static void test_ostree_parse_delta_name (void) { { - g_autofree char *from; - g_autofree char *to; - _ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d", &from, &to); + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (_ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d", &from, &to, NULL)); g_assert_cmpstr (to, ==, "30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d"); g_assert_null (from); } { - g_autofree char *from; - g_autofree char *to; - _ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d-5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03", &from, &to); + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (_ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d-5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03", &from, &to, NULL)); g_assert_cmpstr (from, ==, "30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d"); g_assert_cmpstr (to, ==, "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03"); } { - g_autofree char *from; - g_autofree char *to; - _ostree_parse_delta_name ("", &from, &to); + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (!_ostree_parse_delta_name ("", &from, &to, NULL)); + g_assert_null (from); + g_assert_null (to); + } + + { + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (!_ostree_parse_delta_name ("GARBAGE", &from, &to, NULL)); + g_assert_null (from); + g_assert_null (to); + } + + { + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (!_ostree_parse_delta_name ("GARBAGE-5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03", &from, &to, NULL)); + g_assert_null (from); + g_assert_null (to); + } + + { + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_assert (!_ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d-GARBAGE", &from, &to, NULL)); g_assert_null (from); g_assert_null (to); } diff --git a/tests/test-delta.sh b/tests/test-delta.sh index f8209651..bb523b47 100755 --- a/tests/test-delta.sh +++ b/tests/test-delta.sh @@ -26,7 +26,7 @@ skip_without_user_xattrs bindatafiles="bash true ostree" morebindatafiles="false ls" -echo '1..10' +echo '1..11' mkdir repo ${CMD_PREFIX} ostree --repo=repo init --mode=archive-z2 @@ -235,3 +235,10 @@ ${CMD_PREFIX} ostree --repo=repo2 fsck ${CMD_PREFIX} ostree --repo=repo2 ls ${samerev} >/dev/null echo 'ok pull empty delta part' + +if ${CMD_PREFIX} ostree --repo=repo static-delta show GARBAGE 2> err.txt; then + assert_not_reached "static-delta show GARBAGE unexpectedly succeeded" +fi +assert_file_has_content err.txt "Invalid rev 'GARBAGE'" + +echo 'ok handle bad delta name' From 5893b68ef76b10fc4267faa09d27588f2594b2f6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 11 Sep 2016 20:33:42 -0400 Subject: [PATCH 02/17] pull: Do allow executing deltas when mirroring into bare{,-user} In https://github.com/ostreedev/ostree/pull/408 we fixed a bug where we would crash when trying to execute deltas into an archive repo (which isn't presently supported). But that was overly aggressive - we obviously *can* execute deltas when mirroring into a bare repo. This should fix a regression with the way flatpak uses mirroring to pull from a user repo into the system. Closes: #506 Approved by: alexlarsson --- src/libostree/ostree-repo-pull.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 76c29272..01561df2 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -2272,6 +2272,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, const char *url_override = NULL; g_autofree char *base_meta_url = NULL; g_autofree char *base_content_url = NULL; + gboolean mirroring_into_archive; if (options) { @@ -2312,6 +2313,8 @@ ostree_repo_pull_with_options (OstreeRepo *self, pull_data->is_untrusted = (flags & OSTREE_REPO_PULL_FLAGS_UNTRUSTED) > 0; pull_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + mirroring_into_archive = pull_data->is_mirror && self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2; + if (error) pull_data->async_error = &pull_data->cached_async_error; else @@ -2845,7 +2848,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, &from_revision, error)) goto out; - if (!disable_static_deltas && !pull_data->is_mirror && + if (!disable_static_deltas && !mirroring_into_archive && (from_revision == NULL || g_strcmp0 (from_revision, to_revision) != 0)) { if (!request_static_delta_superblock_sync (pull_data, from_revision, to_revision, From a5af1cb6888a2e4de5534be7623d08be277b801a Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Tue, 13 Sep 2016 09:03:53 -0400 Subject: [PATCH 03/17] ostree-repo.c: Fix file descriptor cleanup 0 was used as an "unset" flag for tmp_dir_fd, which is technically incorrect. For cache_dir_fd, -1 was used as the sentinal but 0 was checked for, resulting in close(-1). Closes: #507 Approved by: cgwalters --- src/libostree/ostree-repo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index e4e1ecbf..d4b1f1d6 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -516,9 +516,9 @@ ostree_repo_finalize (GObject *object) g_free (self->commit_stagedir_name); glnx_release_lock_file (&self->commit_stagedir_lock); g_clear_object (&self->tmp_dir); - if (self->tmp_dir_fd) + if (self->tmp_dir_fd != -1) (void) close (self->tmp_dir_fd); - if (self->cache_dir_fd) + if (self->cache_dir_fd != -1) (void) close (self->cache_dir_fd); if (self->objects_dir_fd != -1) (void) close (self->objects_dir_fd); @@ -702,6 +702,7 @@ ostree_repo_init (OstreeRepo *self) self->repo_dir_fd = -1; self->cache_dir_fd = -1; + self->tmp_dir_fd = -1; self->commit_stagedir_fd = -1; self->objects_dir_fd = -1; self->uncompressed_objects_dir_fd = -1; From a8301b909c61d2e326cf5e4557f672ccd9f457bf Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Tue, 13 Sep 2016 09:05:52 -0400 Subject: [PATCH 04/17] ostree_sysroot.c: Don't close sysroot_fd twice. If ostree_sysroot_unload() was called explicitly, then sysroot_fd would be closed again at finalization time, possibly closing a random file descriptor belonging to some other part of the application. Closes: #507 Approved by: cgwalters --- src/libostree/ostree-sysroot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 37063e28..de92691a 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -260,7 +260,10 @@ void ostree_sysroot_unload (OstreeSysroot *self) { if (self->sysroot_fd != -1) - (void) close (self->sysroot_fd); + { + (void) close (self->sysroot_fd); + self->sysroot_fd = -1; + } } /** From 84a9d61e1575d40aa0a68cd833fb5faa36ddefdd Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 13 Sep 2016 21:26:38 -0400 Subject: [PATCH 05/17] sysroot: Port some small cleanup code to fd-relative Just a quick patch since I saw this function scroll by in Emacs and it was too ugly not to be rewritten. Closes: #510 Approved by: giuseppe --- src/libostree/ostree-sysroot-deploy.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index a49428d2..79f9c9f3 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -1635,25 +1635,28 @@ cleanup_legacy_current_symlinks (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; guint i; - g_autoptr(GHashTable) created_current_for_osname = - g_hash_table_new (g_str_hash, g_str_equal); + g_autoptr(GString) buf = g_string_new (""); for (i = 0; i < self->deployments->len; i++) { OstreeDeployment *deployment = self->deployments->pdata[i]; const char *osname = ostree_deployment_get_osname (deployment); - g_autoptr(GFile) osdir = ot_gfile_resolve_path_printf (self->path, "ostree/deploy/%s", osname); - g_autoptr(GFile) legacy_link = g_file_get_child (osdir, "current"); - if (!ot_gfile_ensure_unlinked (legacy_link, cancellable, error)) - goto out; + g_string_truncate (buf, 0); + g_string_append_printf (buf, "ostree/deploy/%s/current", osname); + + if (unlinkat (self->sysroot_fd, buf->str, 0) < 0) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + return FALSE; + } + } } - ret = TRUE; - out: - return ret; + return TRUE; } static gboolean From fcffb732806a10a2be77a4642ccf7370836572e6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 13 Sep 2016 21:05:08 -0400 Subject: [PATCH 06/17] sysroot: Port origin writing code to fd-relative Just preparatory cleanup for a next patch which makes the fsyncing configurable. Closes: #509 Approved by: giuseppe --- src/libostree/ostree-sysroot-deploy.c | 29 +++++++++++-------------- src/libotutil/ot-gio-utils.c | 31 --------------------------- src/libotutil/ot-gio-utils.h | 4 ---- 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 79f9c9f3..8f0ff733 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -913,35 +913,32 @@ ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; GKeyFile *origin = new_origin ? new_origin : ostree_deployment_get_origin (deployment); if (origin) { - g_autoptr(GFile) deployment_path = ostree_sysroot_get_deployment_directory (sysroot, deployment); - g_autoptr(GFile) origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path); - g_autoptr(GFile) origin_parent = g_file_get_parent (origin_path); + g_autofree char *origin_path = NULL; g_autofree char *contents = NULL; gsize len; - g_autoptr(GBytes) contents_bytes = NULL; + + origin_path = g_strdup_printf ("ostree/deploy/%s/deploy/%s.%d.origin", + ostree_deployment_get_osname (deployment), + ostree_deployment_get_csum (deployment), + ostree_deployment_get_deployserial (deployment)); contents = g_key_file_to_data (origin, &len, error); if (!contents) - goto out; - contents_bytes = g_bytes_new_static (contents, len); + return FALSE; - if (!ot_gfile_replace_contents_fsync (origin_path, contents_bytes, - cancellable, error)) - goto out; - - if (!ot_util_fsync_directory (origin_parent, cancellable, error)) - goto out; + if (!glnx_file_replace_contents_at (sysroot->sysroot_fd, + origin_path, (guint8*)contents, len, + GLNX_FILE_REPLACE_DATASYNC_NEW, + cancellable, error)) + return FALSE; } - ret = TRUE; - out: - return ret; + return TRUE; } static gboolean diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c index 683fa48e..06bf1a27 100644 --- a/src/libotutil/ot-gio-utils.c +++ b/src/libotutil/ot-gio-utils.c @@ -314,37 +314,6 @@ ot_gfile_ensure_unlinked (GFile *path, return TRUE; } -/** - * ot_util_fsync_directory: - * @dir: Path to a directory - * @cancellable: Cancellable - * @error: Error - * - * Ensure that all entries in directory @dir are on disk. - */ -gboolean -ot_util_fsync_directory (GFile *dir, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - glnx_fd_close int dfd = -1; - - if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (dir), TRUE, - &dfd, error)) - goto out; - - if (fsync (dfd) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } - - ret = TRUE; - out: - return ret; -} - /** * ot_util_ensure_directory_and_fsync: * @dir: Path to a directory diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h index 0d3c45d2..0750a5b4 100644 --- a/src/libotutil/ot-gio-utils.h +++ b/src/libotutil/ot-gio-utils.h @@ -89,10 +89,6 @@ gboolean ot_util_ensure_directory_and_fsync (GFile *dir, GCancellable *cancellable, GError **error); -gboolean ot_util_fsync_directory (GFile *dir, - GCancellable *cancellable, - GError **error); - #if !GLIB_CHECK_VERSION(2, 44, 0) gboolean ot_file_enumerator_iterate (GFileEnumerator *direnum, From 4f736ac33e8ba7a047c9ef1c4d17c7993c9048ee Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 13 Sep 2016 21:09:48 -0400 Subject: [PATCH 07/17] sysroot: Drop an fsync for origin file when writing deployments More fsync pruning. Since we have a public API for writing the origin file and it did a fsync before, let's preserve that. But when writing deployments as part of a full transaction, we rely on the global `syncfs()`, so add an internal function for origin file writing that doesn't. Closes: #509 Approved by: giuseppe --- src/libostree/ostree-sysroot-deploy.c | 59 +++++++++++++++++---------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 8f0ff733..a3c1401d 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -894,24 +894,13 @@ merge_configuration (OstreeSysroot *sysroot, return ret; } -/** - * ostree_sysroot_write_origin_file: - * @sysroot: System root - * @deployment: Deployment - * @new_origin: (allow-none): Origin content - * @cancellable: Cancellable - * @error: Error - * - * Immediately replace the origin file of the referenced @deployment - * with the contents of @new_origin. If @new_origin is %NULL, - * this function will write the current origin of @deployment. - */ -gboolean -ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, - OstreeDeployment *deployment, - GKeyFile *new_origin, - GCancellable *cancellable, - GError **error) +static gboolean +write_origin_file_internal (OstreeSysroot *sysroot, + OstreeDeployment *deployment, + GKeyFile *new_origin, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error) { GKeyFile *origin = new_origin ? new_origin : ostree_deployment_get_origin (deployment); @@ -933,7 +922,7 @@ ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, if (!glnx_file_replace_contents_at (sysroot->sysroot_fd, origin_path, (guint8*)contents, len, - GLNX_FILE_REPLACE_DATASYNC_NEW, + flags, cancellable, error)) return FALSE; } @@ -941,6 +930,30 @@ ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, return TRUE; } +/** + * ostree_sysroot_write_origin_file: + * @sysroot: System root + * @deployment: Deployment + * @new_origin: (allow-none): Origin content + * @cancellable: Cancellable + * @error: Error + * + * Immediately replace the origin file of the referenced @deployment + * with the contents of @new_origin. If @new_origin is %NULL, + * this function will write the current origin of @deployment. + */ +gboolean +ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, + OstreeDeployment *deployment, + GKeyFile *new_origin, + GCancellable *cancellable, + GError **error) +{ + return write_origin_file_internal (sysroot, deployment, new_origin, + GLNX_FILE_REPLACE_DATASYNC_NEW, + cancellable, error); +} + static gboolean get_kernel_from_tree (int deployment_dfd, int *out_boot_dfd, @@ -2152,8 +2165,12 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self, goto out; } - if (!ostree_sysroot_write_origin_file (self, new_deployment, NULL, - cancellable, error)) + /* Don't fsync here, as we assume that's all done in + * ostree_sysroot_write_deployments(). + */ + if (!write_origin_file_internal (self, new_deployment, NULL, + GLNX_FILE_REPLACE_NODATASYNC, + cancellable, error)) { g_prefix_error (error, "Writing out origin file: "); goto out; From f2b6afd2df73eeb3e09887c852e6459f9d6cac49 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 13 Sep 2016 20:38:21 -0400 Subject: [PATCH 08/17] sysroot: Drop an unnecessary fsync While looking at a slow update issue (which I'm guessing is unpredictable I/O latency in an OpenStack instance), I noticed in one of the traces we were inside a fsync here. Dropping the fsync here is just another of a long series of unwinding them - we `syncfs()` the sysroot fd and `/boot` and we have a big `sync()` anyways. Closes: #508 Approved by: jlebon --- src/libostree/ostree-sysroot-deploy.c | 10 ++-- src/libotutil/ot-gio-utils.c | 67 --------------------------- src/libotutil/ot-gio-utils.h | 4 -- 3 files changed, 5 insertions(+), 76 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index a3c1401d..65173ffe 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -1827,7 +1827,7 @@ _ostree_sysroot_write_deployments_internal (OstreeSysroot *self, { int new_bootversion = self->bootversion ? 0 : 1; glnx_unref_object OstreeBootloader *bootloader = NULL; - g_autoptr(GFile) new_loader_entries_dir = NULL; + g_autofree char* new_loader_entries_dir = NULL; glnx_unref_object OstreeRepo *repo = NULL; gboolean show_osname = FALSE; @@ -1848,11 +1848,11 @@ _ostree_sysroot_write_deployments_internal (OstreeSysroot *self, if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error)) goto out; - new_loader_entries_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d/entries", - new_bootversion); - if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (new_loader_entries_dir), cancellable, error)) + new_loader_entries_dir = g_strdup_printf ("boot/loader.%d/entries", new_bootversion); + if (!glnx_shutil_rm_rf_at (self->sysroot_fd, new_loader_entries_dir, cancellable, error)) goto out; - if (!ot_util_ensure_directory_and_fsync (new_loader_entries_dir, cancellable, error)) + if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, new_loader_entries_dir, 0755, + cancellable, error)) goto out; /* Need the repo to try and extract the versions for deployments. diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c index 06bf1a27..ba21b467 100644 --- a/src/libotutil/ot-gio-utils.c +++ b/src/libotutil/ot-gio-utils.c @@ -314,73 +314,6 @@ ot_gfile_ensure_unlinked (GFile *path, return TRUE; } -/** - * ot_util_ensure_directory_and_fsync: - * @dir: Path to a directory - * @cancellable: Cancellable - * @error: Error - * - * Create @dir (and all intermediate parent directories), ensuring - * that all entries are on disk. - */ -gboolean -ot_util_ensure_directory_and_fsync (GFile *dir, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - glnx_fd_close int parentfd = -1; - const char *basename = glnx_basename (gs_file_get_path_cached (dir)); - g_autoptr(GFile) parent = g_file_get_parent (dir); - - again: - parentfd = open (gs_file_get_path_cached (parent), - O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC); - if (parentfd == -1) - { - if (errno == ENOENT) - { - if (!ot_util_ensure_directory_and_fsync (parent, cancellable, error)) - goto out; - goto again; - } - else - { - int errsv = errno; - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), - "opendir: %s", g_strerror (errsv)); - goto out; - } - } - - if (mkdirat (parentfd, basename, 0777) == -1) - { - if (errno == EEXIST) - { - ; - } - else - { - int errsv = errno; - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), - "mkdirat: %s", g_strerror (errsv)); - goto out; - } - } - - if (fsync (parentfd) == -1) - { - int errsv = errno; - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), - "fsync: %s", g_strerror (errsv)); - goto out; - } - - ret = TRUE; - out: - return ret; -} - #if !GLIB_CHECK_VERSION(2, 44, 0) gboolean diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h index 0750a5b4..34040238 100644 --- a/src/libotutil/ot-gio-utils.h +++ b/src/libotutil/ot-gio-utils.h @@ -85,10 +85,6 @@ gboolean ot_gfile_ensure_unlinked (GFile *path, GCancellable *cancellable, GError **error); -gboolean ot_util_ensure_directory_and_fsync (GFile *dir, - GCancellable *cancellable, - GError **error); - #if !GLIB_CHECK_VERSION(2, 44, 0) gboolean ot_file_enumerator_iterate (GFileEnumerator *direnum, From 450361d89bb937e157994abb15923a881da57ba3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 14 Sep 2016 12:56:07 -0400 Subject: [PATCH 09/17] boot: Ensure we remount /var writable before systemd does journal flush Otherwise, we may not get a persistent journal for the first boot. https://bugzilla.redhat.com/show_bug.cgi?id=1265295 Closes: #511 Approved by: jlebon --- src/boot/ostree-remount.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/ostree-remount.service b/src/boot/ostree-remount.service index 61dd5fa8..8439b495 100644 --- a/src/boot/ostree-remount.service +++ b/src/boot/ostree-remount.service @@ -25,7 +25,7 @@ After=-.mount After=systemd-remount-fs.service Before=local-fs.target umount.target # Other early boot units that need to write to /var -Before=systemd-random-seed.service plymouth-read-write.service +Before=systemd-random-seed.service plymouth-read-write.service systemd-journal-flush.service # tmpfiles.d usually needs write access to a few places Before=systemd-tmpfiles-setup.service From 318430dc700973fbc54c5469343c45aaab4701a7 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 20 Sep 2016 11:45:57 -0400 Subject: [PATCH 10/17] ostree_sysroot_init_osname: also create /var/log /var/log is another one of those core directories that should be made available and properly labeled during early boot before tmpfiles.d starts up. Related: https://bugzilla.redhat.com/show_bug.cgi?id=1265295 Closes: #513 Approved by: cgwalters --- src/libostree/ostree-sysroot.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index de92691a..ee87128d 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1487,6 +1487,16 @@ ostree_sysroot_init_osname (OstreeSysroot *self, goto out; } + /* This needs to be available and properly labeled early during the boot + * process (before tmpfiles.d kicks in), so that journald can flush logs from + * the first boot there. https://bugzilla.redhat.com/show_bug.cgi?id=1265295 + * */ + if (mkdirat (dfd, "var/log", 0755) < 0) + { + glnx_set_prefix_error_from_errno (error, "Creating %s", "var/log"); + goto out; + } + if (symlinkat ("../run", dfd, "var/run") < 0) { glnx_set_prefix_error_from_errno (error, "Symlinking %s", "var/run"); From 056ca71a3b96e21d9e573b41ed83f5fa4bd118b2 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 20 Sep 2016 12:04:41 -0400 Subject: [PATCH 11/17] docs: add mention of rpm-ostree package layering Closes: #514 Approved by: cgwalters --- docs/manual/adapting-existing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/manual/adapting-existing.md b/docs/manual/adapting-existing.md index 77746e94..275479f0 100644 --- a/docs/manual/adapting-existing.md +++ b/docs/manual/adapting-existing.md @@ -157,3 +157,6 @@ will silently override earlier layers. Then to actually deploy this tree for the next boot: `ostree admin deploy $osname/$releasename/$description` + +This is essentially what [rpm-ostree](https://github.com/projectatomic/rpm-ostree/) +does to support its [package layering model](https://rpm-ostree.readthedocs.io/en/latest/manual/administrator-handbook/#package-layering). From 6c84fa43669d2892f5aeb50ca1d6a69976352df5 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 22 Sep 2016 09:13:39 -0700 Subject: [PATCH 12/17] admin: Allow running status unlocked It's useful to let non-root see the current system status. Closes: #515 Closes: #516 Approved by: jlebon --- src/ostree/ot-admin-builtin-status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ostree/ot-admin-builtin-status.c b/src/ostree/ot-admin-builtin-status.c index df4d0745..2550bcea 100644 --- a/src/ostree/ot-admin-builtin-status.c +++ b/src/ostree/ot-admin-builtin-status.c @@ -97,7 +97,7 @@ ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GErro context = g_option_context_new ("List deployments"); if (!ostree_admin_option_context_parse (context, options, &argc, &argv, - OSTREE_ADMIN_BUILTIN_FLAG_NONE, + OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED, &sysroot, cancellable, error)) goto out; From 2eae12220e171a1808768530140456dbb2c64946 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 1 Oct 2016 16:07:08 +0100 Subject: [PATCH 13/17] Fix spelling of "repository" Detected by Debian's Lintian tool. Signed-off-by: Simon McVittie Closes: #519 Approved by: cgwalters --- man/ostree-remote.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/ostree-remote.xml b/man/ostree-remote.xml index c303014e..2e600845 100644 --- a/man/ostree-remote.xml +++ b/man/ostree-remote.xml @@ -75,10 +75,10 @@ Boston, MA 02111-1307, USA. Description - Changes remote respository configurations. The NAME refers to the name of the remote. + Changes remote repository configurations. The NAME refers to the name of the remote. - The gpg-import subcommand can associate GPG keys to a specific remote respository for use when pulling signed commits from that repository (if GPG verification is enabled). + The gpg-import subcommand can associate GPG keys to a specific remote repository for use when pulling signed commits from that repository (if GPG verification is enabled). The GPG keys to import may be in binary OpenPGP format or ASCII armored. The optional KEY-ID list can restrict which keys are imported from a keyring file or input stream. All keys are imported if this list is omitted. If neither nor options are given, then keys are imported from the user's personal GPG keyring. From a981e5fd7685caeca79ae711f30640768a70de3a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 4 Oct 2016 15:39:15 -0400 Subject: [PATCH 14/17] checkout: Fix fsync defaults for new API to be off for real My previous change in https://github.com/ostreedev/ostree/pull/425 actually broke things so we basically used the repository defaults =( This is a subtle mess since we're only trying to flip things off for the *new* API. Clean this up so that the "default repo inheritance" lives only in one place - in the compat layer for the old checkout API. The new checkout API defaults to off period, so the repository state is irrelevant. Closes: #520 Approved by: jlebon --- src/libostree/ostree-repo-checkout.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index bf18c9e4..23258a47 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -106,7 +106,7 @@ static gboolean fsync_is_enabled (OstreeRepo *self, OstreeRepoCheckoutAtOptions *options) { - return options->enable_fsync || !self->disable_fsync; + return options->enable_fsync; } static gboolean @@ -860,7 +860,7 @@ ostree_repo_checkout_tree_at (OstreeRepo *self, new_opts.mode = options->mode; new_opts.overwrite_mode = options->overwrite_mode; new_opts.enable_uncompressed_cache = options->enable_uncompressed_cache; - new_opts.enable_fsync = !options->disable_fsync; + new_opts.enable_fsync = options->disable_fsync ? FALSE : self->disable_fsync; new_opts.process_whiteouts = options->process_whiteouts; new_opts.no_copy_fallback = options->no_copy_fallback; new_opts.subpath = options->subpath; From 524d2d5cb24cb5693406d0cc4973e40ae028d27f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 14 Sep 2016 16:08:24 -0400 Subject: [PATCH 15/17] trivial-httpd: Port mostly to fd-relative We were seeing some weird potential memory corruption in this code when using it for `rpm-ostree-toolbox installer`, which is almost certainly not its fault, but let's use it as an excuse to port (mostly) to fd-relative and away from GFile. Dropping the last GFile use here is a bit tricky as it does have a nice high level wrapper around inotify. Closes: #512 Approved by: jlebon --- src/ostree/ot-builtin-trivial-httpd.c | 100 +++++++++++++++----------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/src/ostree/ot-builtin-trivial-httpd.c b/src/ostree/ot-builtin-trivial-httpd.c index 88a1a74b..2b6bda25 100644 --- a/src/ostree/ot-builtin-trivial-httpd.c +++ b/src/ostree/ot-builtin-trivial-httpd.c @@ -47,7 +47,7 @@ static gint opt_port = 0; static guint emitted_random_500s_count = 0; typedef struct { - GFile *root; + int root_dfd; gboolean running; GOutputStream *log; } OtTrivialHttpd; @@ -101,34 +101,38 @@ compare_strings (gconstpointer a, gconstpointer b) } static GString * -get_directory_listing (const char *path) +get_directory_listing (int dfd, + const char *path) { - GPtrArray *entries; - GString *listing; + g_autoptr(GPtrArray) entries = g_ptr_array_new_with_free_func (g_free); + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + guint i; char *escaped; - DIR *dir; - struct dirent *dent; - int i; + GString *listing; - entries = g_ptr_array_new (); - dir = opendir (path); - if (dir) + listing = g_string_new ("\r\n"); + + if (!glnx_dirfd_iterator_init_at (dfd, path, FALSE, &dfd_iter, error)) + goto out; + + while (TRUE) { - while ((dent = readdir (dir))) - { - if (!strcmp (dent->d_name, ".") || - (!strcmp (dent->d_name, "..") && - !strcmp (path, "./"))) - continue; - escaped = g_markup_escape_text (dent->d_name, -1); - g_ptr_array_add (entries, escaped); - } - closedir (dir); + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, NULL, error)) + goto out; + + if (dent == NULL) + break; + + escaped = g_markup_escape_text (dent->d_name, -1); + g_ptr_array_add (entries, escaped); } g_ptr_array_sort (entries, (GCompareFunc)compare_strings); - listing = g_string_new ("\r\n"); escaped = g_markup_escape_text (strchr (path, '/'), -1); g_string_append_printf (listing, "Index of %s\r\n", escaped); g_string_append_printf (listing, "

Index of %s

\r\n

\r\n", escaped); @@ -138,11 +142,12 @@ get_directory_listing (const char *path) g_string_append_printf (listing, "%s
\r\n", (char *)entries->pdata[i], (char *)entries->pdata[i]); - g_free (entries->pdata[i]); + g_free (g_steal_pointer (&entries->pdata[i])); } g_string_append (listing, "\r\n\r\n"); - - g_ptr_array_free (entries, TRUE); + out: + if (local_error) + g_printerr ("%s\n", local_error->message); return listing; } @@ -192,7 +197,6 @@ do_get (OtTrivialHttpd *self, char *slash; int ret; struct stat stbuf; - g_autofree char *safepath = NULL; httpd_log (self, "serving %s\n", path); if (strstr (path, "../") != NULL) @@ -210,13 +214,11 @@ do_get (OtTrivialHttpd *self, goto out; } - if (path[0] == '/') + while (path[0] == '/') path++; - safepath = g_build_filename (gs_file_get_path_cached (self->root), path, NULL); - do - ret = stat (safepath, &stbuf); + ret = fstatat (self->root_dfd, path, &stbuf, 0); while (ret == -1 && errno == EINTR); if (ret == -1) { @@ -237,7 +239,7 @@ do_get (OtTrivialHttpd *self, if (S_ISDIR (stbuf.st_mode)) { - slash = strrchr (safepath, '/'); + slash = strrchr (path, '/'); if (!slash || slash[1]) { g_autofree char *redir_uri = NULL; @@ -248,15 +250,15 @@ do_get (OtTrivialHttpd *self, } else { - g_autofree char *index_realpath = g_strconcat (safepath, "/index.html", NULL); - if (stat (index_realpath, &stbuf) != -1) + g_autofree char *index_realpath = g_strconcat (path, "/index.html", NULL); + if (fstatat (self->root_dfd, index_realpath, &stbuf, 0) != -1) { g_autofree char *index_path = g_strconcat (path, "/index.html", NULL); do_get (self, server, msg, index_path, context); } else { - GString *listing = get_directory_listing (safepath); + GString *listing = get_directory_listing (self->root_dfd, path); soup_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE, listing->str, listing->len); @@ -275,18 +277,27 @@ do_get (OtTrivialHttpd *self, if (msg->method == SOUP_METHOD_GET) { + glnx_fd_close int fd = -1; g_autoptr(GMappedFile) mapping = NULL; gsize buffer_length, file_size; SoupRange *ranges; int ranges_length; gboolean have_ranges; - mapping = g_mapped_file_new (safepath, FALSE, NULL); + fd = openat (self->root_dfd, path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + goto out; + } + + mapping = g_mapped_file_new_from_fd (fd, FALSE, NULL); if (!mapping) { soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); goto out; } + (void) close (fd); fd = -1; file_size = g_mapped_file_get_length (mapping); have_ranges = soup_message_headers_get_ranges(msg->request_headers, file_size, &ranges, &ranges_length); @@ -401,6 +412,8 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, context = g_option_context_new ("[DIR] - Simple webserver"); + app->root_dfd = -1; + if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error)) goto out; @@ -409,7 +422,8 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, else dirpath = "."; - app->root = g_file_new_for_path (dirpath); + if (!glnx_opendirat (AT_FDCWD, dirpath, TRUE, &app->root_dfd, error)) + goto out; if (!(opt_random_500s_percentage >= 0 && opt_random_500s_percentage <= 99)) { @@ -534,9 +548,11 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, if (opt_autoexit) { gboolean is_symlink = FALSE; + g_autoptr(GFile) root = NULL; g_autoptr(GFileInfo) info = NULL; - info = g_file_query_info (app->root, + root = g_file_new_for_path (dirpath); + info = g_file_query_info (root, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); @@ -546,24 +562,22 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, is_symlink = g_file_info_get_is_symlink (info); if (is_symlink) - dirmon = g_file_monitor_file (app->root, 0, cancellable, error); + dirmon = g_file_monitor_file (root, 0, cancellable, error); else - dirmon = g_file_monitor_directory (app->root, 0, cancellable, error); + dirmon = g_file_monitor_directory (root, 0, cancellable, error); if (!dirmon) goto out; g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app); } - { - g_autofree gchar *path = g_file_get_path (app->root); - httpd_log (app, "serving at root %s\n", path); - } + httpd_log (app, "serving at root %s\n", dirpath); while (app->running) g_main_context_iteration (NULL, TRUE); ret = TRUE; out: - g_clear_object (&app->root); + if (app->root_dfd != -1) + (void) close (app->root_dfd); g_clear_object (&app->log); return ret; } From 54621d9e530f120b3ae40507b53b1dd13a778a5c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 5 Oct 2016 10:13:33 -0400 Subject: [PATCH 16/17] libglnx: Update to latest This pulls in a new compilation flag for wrpseudo compatibility. Also note we need to add some includes since glnx-libcontainer went away, and with it some includes for `sys/mount.h` etc. Closes: #522 Approved by: cgwalters --- configure.ac | 1 + libglnx | 2 +- src/libostree/ostree-sysroot.c | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b739d2a7..0126bdd4 100644 --- a/configure.ac +++ b/configure.ac @@ -335,6 +335,7 @@ echo " libarchive (parse tar files directly): $with_libarchive static deltas: yes (always enabled now) O_TMPFILE: $enable_otmpfile + wrpseudo-compat: $enable_wrpseudo_compat man pages (xsltproc): $enable_man api docs (gtk-doc): $enable_gtk_doc gjs-based tests: $have_gjs diff --git a/libglnx b/libglnx index 4ae5e3be..36396b49 160000 --- a/libglnx +++ b/libglnx @@ -1 +1 @@ -Subproject commit 4ae5e3beaaa674abfabf7404ab6fafcc4ec547db +Subproject commit 36396b49ad6636c9959f3dfac5e04d41584b1a92 diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index ee87128d..97f00c4e 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -21,6 +21,8 @@ #include "config.h" #include "otutil.h" +#include +#include #include "ostree-core-private.h" #include "ostree-sysroot-private.h" From a0e1344cf80f2b3f3d0501d7f3559ad67c32dac4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 6 Oct 2016 12:56:18 -0400 Subject: [PATCH 17/17] Release 2016.11 Closes: #524 Approved by: cgwalters --- configure.ac | 2 +- src/libostree/libostree.sym | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0126bdd4..e69d94e7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.63]) dnl If incrementing the version here, remember to update libostree.sym too -AC_INIT([ostree], [2016.10], [walters@verbum.org]) +AC_INIT([ostree], [2016.11], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([buildutil]) AC_CONFIG_AUX_DIR([build-aux]) diff --git a/src/libostree/libostree.sym b/src/libostree/libostree.sym index 5a2d71be..4aa8d6cf 100644 --- a/src/libostree/libostree.sym +++ b/src/libostree/libostree.sym @@ -355,6 +355,7 @@ global: /* No new symbols in 2016.9 */ /* No new symbols in 2016.10 */ +/* No new symbols in 2016.11 */ /* NOTE NOTE NOTE * Versions above here are released. Only add symbols below this line. @@ -362,7 +363,7 @@ global: */ /* Remove comment when first new symbol is added -LIBOSTREE_2016.11 +LIBOSTREE_2016.12 global: someostree_symbol_deleteme; } LIBOSTREE_2016.8;