Skip to content

Commit 11b7327

Browse files
committed
repo-commit: preserve existing object inode when staging to objects/
When ostree commit --consume is used and an object with the same checksum already exists in objects/, rename_pending_loose_objects() was unconditionally renaming the staging copy over it. On Linux, renameat(2) atomically replaces the destination for two regular files, silently changing the inode of the existing repo object. Fix this by checking whether the object already exists in objects/ before renaming. If it does, the content is identical by definition (the object store is content-addressed by SHA256), so we can simply unlink the staging copy and keep the existing object with its original inode. Exception: .commitmeta objects are keyed by commit checksum rather than their own content, so they can be updated in place (e.g. when GPG signatures are added or deleted via ostree gpg-sign). These are always renamed unconditionally.
1 parent 170ac98 commit 11b7327

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

src/libostree/ostree-repo-commit.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,8 +1801,33 @@ rename_pending_loose_objects (OstreeRepo *self, GCancellable *cancellable, GErro
18011801
cancellable, error))
18021802
return FALSE;
18031803

1804-
if (!glnx_renameat (child_dfd_iter.fd, loose_objpath + 3, self->objects_dir_fd,
1805-
loose_objpath, error))
1804+
/* If the object already exists in objects/, preserve it (and its
1805+
* inode) rather than replacing it with the staging copy. The store
1806+
* is content-addressed, so an existing file at this path has the
1807+
* same content by definition.
1808+
*
1809+
* Exception: .commitmeta objects are keyed by commit checksum, not
1810+
* by their own content, so they can be updated in place (e.g. when
1811+
* GPG signatures are added or deleted). Always rename those.
1812+
*/
1813+
gboolean obj_exists = FALSE;
1814+
if (!g_str_has_suffix (child_dent->d_name, ".commitmeta"))
1815+
{
1816+
struct stat obj_stbuf;
1817+
errno = 0;
1818+
if (!glnx_fstatat_allow_noent (self->objects_dir_fd, loose_objpath, &obj_stbuf,
1819+
AT_SYMLINK_NOFOLLOW, error))
1820+
return FALSE;
1821+
obj_exists = (errno != ENOENT);
1822+
}
1823+
if (obj_exists)
1824+
{
1825+
/* Object already present with same content; drop staging copy */
1826+
if (!glnx_unlinkat (child_dfd_iter.fd, loose_objpath + 3, 0, error))
1827+
return FALSE;
1828+
}
1829+
else if (!glnx_renameat (child_dfd_iter.fd, loose_objpath + 3, self->objects_dir_fd,
1830+
loose_objpath, error))
18061831
return FALSE;
18071832
}
18081833
}

0 commit comments

Comments
 (0)