From bc29e711ec0670bb16f1087e994d5fd5c45114ca Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 28 Jan 2015 12:31:51 +0100 Subject: [PATCH] transactions: move all tmpobject-%s files under a per-boot subdir This prevents to use files after a kernel crash or power failure and that can be not completely synced to disk. Signed-off-by: Giuseppe Scrivano --- src/libostree/ostree-repo-commit.c | 170 +++++++++++++++++++++------- src/libostree/ostree-repo-private.h | 6 +- src/libostree/ostree-repo.c | 8 +- 3 files changed, 142 insertions(+), 42 deletions(-) diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 67ffb34eb0..308cd60c59 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -60,15 +60,41 @@ _ostree_repo_ensure_loose_objdir_at (int dfd, return TRUE; } -void +static gboolean +ostree_repo_get_tmpobject_bootid (char **out, + GCancellable *cancellable, + GError **error) +{ + gs_unref_object GFile *file = g_file_new_for_path ("/proc/sys/kernel/random/boot_id"); + if (! g_file_load_contents (file, + cancellable, + out, + NULL, + NULL, + error)) + return FALSE; + + g_strdelimit (*out, "\n", '\0'); + return TRUE; +} + +gboolean _ostree_repo_get_tmpobject_path (char *output, const char *checksum, - OstreeObjectType objtype) + OstreeObjectType objtype, + GCancellable *cancellable, + GError **error) { + gs_free char *boot_id = NULL; + if (! ostree_repo_get_tmpobject_bootid (&boot_id, cancellable, error)) + return FALSE; + g_sprintf (output, - "tmpobject-%s.%s", + "%s/tmpobject-%s.%s", + boot_id, checksum, ostree_object_type_to_string (objtype)); + return TRUE; } static GVariant * @@ -289,7 +315,9 @@ commit_loose_object_trusted (OstreeRepo *self, if (self->in_transaction) { char tmpbuf[_OSTREE_LOOSE_PATH_MAX]; - _ostree_repo_get_tmpobject_path (tmpbuf, checksum, objtype); + if (! _ostree_repo_get_tmpobject_path (tmpbuf, checksum, objtype, + cancellable, error)) + goto out; tmp_dest = g_strdup (tmpbuf); dir = self->tmp_dir_fd; dest = tmp_dest; @@ -944,6 +972,7 @@ ostree_repo_prepare_transaction (OstreeRepo *self, gboolean ret = FALSE; gboolean ret_transaction_resume = FALSE; gs_free char *transaction_str = NULL; + gs_free char *boot_id = NULL; g_return_val_if_fail (self->in_transaction == FALSE, FALSE); @@ -963,6 +992,20 @@ ostree_repo_prepare_transaction (OstreeRepo *self, if (!ot_gfile_ensure_unlinked (self->transaction_lock_path, cancellable, error)) goto out; } + + if (! ostree_repo_get_tmpobject_bootid (&boot_id, cancellable, error)) + goto out; + + if (mkdirat (self->tmp_dir_fd, boot_id, 0777) == -1) + { + int errsv = errno; + if (G_UNLIKELY (errsv != EEXIST)) + { + gs_set_error_from_errno (error, errsv); + goto out; + } + } + transaction_str = g_strdup_printf ("pid=%llu", (unsigned long long) getpid ()); if (!g_file_make_symbolic_link (self->transaction_lock_path, transaction_str, cancellable, error)) @@ -976,30 +1019,37 @@ ostree_repo_prepare_transaction (OstreeRepo *self, } static gboolean -cleanup_tmpdir (OstreeRepo *self, - gboolean move_tmpobject, - GCancellable *cancellable, - GError **error) +move_tmpobjects (OstreeRepo *self, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; + gs_free char *boot_id = NULL; gs_unref_object GFileEnumerator *enumerator = NULL; - guint64 curtime_secs; + gs_unref_object GFile *tmpobjectsdir = NULL; + int dir_fd = -1; - enumerator = g_file_enumerate_children (self->tmp_dir, "standard::name,time::modified", + if (! ostree_repo_get_tmpobject_bootid (&boot_id, cancellable, error)) + goto out; + + tmpobjectsdir = g_file_get_child (self->tmp_dir, boot_id); + if (! tmpobjectsdir) + goto out; + + enumerator = g_file_enumerate_children (tmpobjectsdir, "standard::name,time::modified", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, - error); + error); if (!enumerator) goto out; - curtime_secs = g_get_real_time () / 1000000; + if (!gs_file_open_dir_fd (tmpobjectsdir, &dir_fd, cancellable, error)) + goto out; while (TRUE) { GFileInfo *file_info; GFile *path; - guint64 mtime; - guint64 delta; gs_free char *basename = NULL; if (!gs_file_enumerator_iterate (enumerator, &file_info, &path, @@ -1008,38 +1058,82 @@ cleanup_tmpdir (OstreeRepo *self, if (file_info == NULL) break; - if (move_tmpobject) + basename = g_file_get_basename (path); + if (strncmp (basename, "tmpobject-", 10) == 0) { - basename = g_file_get_basename (path); - if (strncmp (basename, "tmpobject-", 10) == 0) - { - char loose_path[_OSTREE_LOOSE_PATH_MAX]; - gs_free gchar *checksum = NULL; - OstreeObjectType type; - ostree_object_from_string (basename + 10, - &checksum, - &type); + char loose_path[_OSTREE_LOOSE_PATH_MAX]; + gs_free gchar *checksum = NULL; + OstreeObjectType type; + ostree_object_from_string (basename + 10, + &checksum, + &type); - _ostree_loose_path (loose_path, checksum, type, self->mode); + _ostree_loose_path (loose_path, checksum, type, self->mode); - if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path, - cancellable, error)) - goto out; + if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path, + cancellable, error)) + goto out; - if (G_UNLIKELY (renameat (self->tmp_dir_fd, basename, - self->objects_dir_fd, loose_path) < 0)) + if (G_UNLIKELY (renameat (dir_fd, basename, + self->objects_dir_fd, loose_path) < 0)) + { + (void) unlinkat (self->tmp_dir_fd, basename, 0); + if (errno != EEXIST) { - (void) unlinkat (self->tmp_dir_fd, basename, 0); - if (errno != EEXIST) - { - gs_set_error_from_errno (error, errno); - g_prefix_error (error, "Storing file '%s': ", loose_path); - goto out; - } + gs_set_error_from_errno (error, errno); + g_prefix_error (error, "Storing file '%s': ", loose_path); + goto out; } - continue; } + continue; } + } + + if (!gs_shutil_rm_rf (tmpobjectsdir, cancellable, error)) + goto out; + + ret = TRUE; + out: + if (dir_fd >= 0) + close (dir_fd); + return ret; +} + +static gboolean +cleanup_tmpdir (OstreeRepo *self, + gboolean move_tmpobject, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object GFileEnumerator *enumerator = NULL; + guint64 curtime_secs; + + if (move_tmpobject) + if (! move_tmpobjects (self, cancellable, error)) + goto out; + + enumerator = g_file_enumerate_children (self->tmp_dir, "standard::name,time::modified", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, + error); + if (!enumerator) + goto out; + + curtime_secs = g_get_real_time () / 1000000; + + while (TRUE) + { + GFileInfo *file_info; + GFile *path; + guint64 mtime; + guint64 delta; + + if (!gs_file_enumerator_iterate (enumerator, &file_info, &path, + cancellable, error)) + goto out; + if (file_info == NULL) + break; mtime = g_file_info_get_attribute_uint64 (file_info, "time::modified"); if (mtime > curtime_secs) diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index d569a28008..0395fc947f 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -82,10 +82,12 @@ _ostree_repo_ensure_loose_objdir_at (int dfd, const char *loose_path, GCancellable *cancellable, GError **error); -void +gboolean _ostree_repo_get_tmpobject_path (char *output, const char *checksum, - OstreeObjectType objtype); + OstreeObjectType objtype, + GCancellable *cancellable, + GError **error); gboolean _ostree_repo_find_object (OstreeRepo *self, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index d54150c23e..a1a16dbe78 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1717,7 +1717,9 @@ load_metadata_internal (OstreeRepo *self, if (self->in_transaction && fd < 0) { - _ostree_repo_get_tmpobject_path (loose_path_buf, sha256, objtype); + if (! _ostree_repo_get_tmpobject_path (loose_path_buf, sha256, objtype, + cancellable, error)) + goto out; if (!openat_allow_noent (self->tmp_dir_fd, loose_path_buf, &fd, cancellable, error)) goto out; } @@ -2129,7 +2131,9 @@ _ostree_repo_has_loose_object (OstreeRepo *self, if (self->in_transaction) { - _ostree_repo_get_tmpobject_path (loose_path_buf, checksum, objtype); + if (! _ostree_repo_get_tmpobject_path (loose_path_buf, checksum, objtype, + cancellable, error)) + goto out; do res = fstatat (self->tmp_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW); while (G_UNLIKELY (res == -1 && errno == EINTR));