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
This commit is contained in:
Alexander Larsson 2015-12-14 10:58:53 +01:00 committed by Atomic Bot
parent c81252c1e0
commit 2a3f17c7aa

View File

@ -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;