From 2a3f17c7aa8e5dc328ee9169dfa51d9cb579ba3a Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 14 Dec 2015 10:58:53 +0100 Subject: [PATCH] repo: After renaming in all loose objects, ensure metadata is stable When a transaction is finished and we have moved all the staged loose objects into the repo we fsync all the object directory, to ensure the filenames are stable before we update the refs files to point to the new commits. With out this an unclean shutdown after the transaction is finished could result in a refs file that points to an incomplete commit. https://bugzilla.gnome.org/show_bug.cgi?id=759442 Closes: #918 Approved by: cgwalters --- src/libostree/ostree-repo-commit.c | 36 +++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 93d22f65..be92a627 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -1202,7 +1202,9 @@ rename_pending_loose_objects (OstreeRepo *self, while (TRUE) { struct dirent *dent; + gboolean renamed_some_object = FALSE; g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, }; + char loose_objpath[_OSTREE_LOOSE_PATH_MAX]; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) return FALSE; @@ -1220,21 +1222,20 @@ rename_pending_loose_objects (OstreeRepo *self, &child_dfd_iter, error)) return FALSE; + loose_objpath[0] = dent->d_name[0]; + loose_objpath[1] = dent->d_name[1]; + loose_objpath[2] = '/'; + /* Iterate over inner checksum dir */ while (TRUE) { struct dirent *child_dent; - char loose_objpath[_OSTREE_LOOSE_PATH_MAX]; if (!glnx_dirfd_iterator_next_dent (&child_dfd_iter, &child_dent, cancellable, error)) return FALSE; if (child_dent == NULL) break; - loose_objpath[0] = dent->d_name[0]; - loose_objpath[1] = dent->d_name[1]; - loose_objpath[2] = '/'; - g_strlcpy (loose_objpath + 3, child_dent->d_name, sizeof (loose_objpath)-3); if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_objpath, @@ -1244,9 +1245,34 @@ rename_pending_loose_objects (OstreeRepo *self, if (G_UNLIKELY (renameat (child_dfd_iter.fd, loose_objpath + 3, self->objects_dir_fd, loose_objpath) < 0)) return glnx_throw_errno (error); + + renamed_some_object = TRUE; + } + + if (renamed_some_object) + { + /* Ensure that in the case of a power cut all the directory metadata that + we want has reached the disk. In particular, we want this before we + update the refs to point to these objects. */ + glnx_fd_close int target_dir_fd = -1; + + loose_objpath[2] = 0; + + if (!glnx_opendirat (self->objects_dir_fd, + loose_objpath, FALSE, + &target_dir_fd, + error)) + return FALSE; + + if (fsync (target_dir_fd) == -1) + return glnx_throw_errno_prefix (error, "fsync"); } } + /* In case we created any loose object subdirs, make sure they are on disk */ + if (fsync (self->objects_dir_fd) == -1) + return glnx_throw_errno_prefix (error, "fsync"); + if (!glnx_shutil_rm_rf_at (self->tmp_dir_fd, self->commit_stagedir_name, cancellable, error)) return FALSE;