repo: After renaming in all loose objects, ensure metadata is stable
authorAlexander Larsson <alexl@redhat.com>
Mon, 14 Dec 2015 09:58:53 +0000 (10:58 +0100)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 8 Jun 2017 20:03:18 +0000 (20:03 +0000)
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

index 93d22f65394c35f88262967f3ae133f3a16fdae8..be92a627e4480109a4e05b1a322596194a6fbc7a 100644 (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;