lib/commit: Report whether committed object already existed
authorJonathan Lebon <jlebon@redhat.com>
Thu, 23 Jul 2026 02:08:29 +0000 (22:08 -0400)
committerJonathan Lebon <jonathan@jlebon.com>
Thu, 23 Jul 2026 15:54:38 +0000 (11:54 -0400)
Add an `out_existed` output parameter to
`_ostree_repo_commit_tmpf_final()` and `commit_loose_regfile_object()`.
This requires switching from `GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST`
to `GLNX_LINK_TMPFILE_NOREPLACE` so we can detect `G_IO_ERROR_EXISTS`
ourselves.

No users for now. Prep for a follow-up patch using this.

Assisted-by: AI
src/libostree/ostree-repo-commit.c
src/libostree/ostree-repo-private.h
src/libostree/ostree-repo-pull.c

index b7773e1db1480c6d2af4a04e1d97250ebdcfec03..498376090a10efbf1c9edd3a644cebd55566ddd9 100644 (file)
@@ -181,7 +181,8 @@ ot_security_smack_reset_fd (int fd)
 /* Given an O_TMPFILE regular file, link it into place. */
 gboolean
 _ostree_repo_commit_tmpf_final (OstreeRepo *self, const char *checksum, OstreeObjectType objtype,
-                                GLnxTmpfile *tmpf, GCancellable *cancellable, GError **error)
+                                GLnxTmpfile *tmpf, gboolean *out_existed, GCancellable *cancellable,
+                                GError **error)
 {
   char tmpbuf[_OSTREE_LOOSE_PATH_MAX];
   _ostree_loose_path (tmpbuf, checksum, objtype, self->mode);
@@ -193,10 +194,19 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self, const char *checksum, OstreeOb
   if (!_ostree_tmpf_fsverity (self, tmpf, NULL, error))
     return FALSE;
 
-  if (!glnx_link_tmpfile_at (tmpf, GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST, dest_dfd, tmpbuf,
-                             error))
-    return FALSE;
-  /* We're done with the fd */
+  gboolean existed = FALSE;
+  g_autoptr (GError) local_error = NULL;
+  if (!glnx_link_tmpfile_at (tmpf, GLNX_LINK_TMPFILE_NOREPLACE, dest_dfd, tmpbuf, &local_error))
+    {
+      if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+        {
+          g_propagate_error (error, g_steal_pointer (&local_error));
+          return FALSE;
+        }
+      existed = TRUE;
+    }
+  if (out_existed)
+    *out_existed = existed;
   glnx_tmpfile_clear (tmpf);
   return TRUE;
 }
@@ -237,8 +247,8 @@ commit_path_final (OstreeRepo *self, const char *checksum, OstreeObjectType objt
  */
 static gboolean
 commit_loose_regfile_object (OstreeRepo *self, const char *checksum, GLnxTmpfile *tmpf, guint32 uid,
-                             guint32 gid, guint32 mode, GVariant *xattrs, GCancellable *cancellable,
-                             GError **error)
+                             guint32 gid, guint32 mode, GVariant *xattrs, gboolean *out_existed,
+                             GCancellable *cancellable, GError **error)
 {
   if (self->mode == OSTREE_REPO_MODE_BARE)
     {
@@ -308,8 +318,8 @@ commit_loose_regfile_object (OstreeRepo *self, const char *checksum, GLnxTmpfile
         return glnx_throw_errno_prefix (error, "fsync");
     }
 
-  if (!_ostree_repo_commit_tmpf_final (self, checksum, OSTREE_OBJECT_TYPE_FILE, tmpf, cancellable,
-                                       error))
+  if (!_ostree_repo_commit_tmpf_final (self, checksum, OSTREE_OBJECT_TYPE_FILE, tmpf, out_existed,
+                                       cancellable, error))
     return FALSE;
 
   return TRUE;
@@ -569,7 +579,7 @@ _ostree_repo_bare_content_commit (OstreeRepo *self, OstreeRepoBareContent *barew
     return FALSE;
 
   if (!commit_loose_regfile_object (self, checksum_buf, &real->tmpf, real->uid, real->gid,
-                                    real->mode, real->xattrs, cancellable, error))
+                                    real->mode, real->xattrs, NULL, cancellable, error))
     return FALSE;
 
   /* Let's have a guarantee that after commit the object is cleaned up */
@@ -1168,7 +1178,7 @@ write_content_object (OstreeRepo *self, const char *expected_checksum, GInputStr
         return FALSE;
 
       /* This path is for regular files */
-      if (!commit_loose_regfile_object (self, actual_checksum, &tmpf, uid, gid, mode, xattrs,
+      if (!commit_loose_regfile_object (self, actual_checksum, &tmpf, uid, gid, mode, xattrs, NULL,
                                         cancellable, error))
         return FALSE;
 
@@ -1389,7 +1399,8 @@ write_metadata_object (OstreeRepo *self, OstreeObjectType objtype, const char *e
     return FALSE;
 
   /* And commit it into place */
-  if (!_ostree_repo_commit_tmpf_final (self, actual_checksum, objtype, &tmpf, cancellable, error))
+  if (!_ostree_repo_commit_tmpf_final (self, actual_checksum, objtype, &tmpf, NULL, cancellable,
+                                       error))
     return FALSE;
 
   if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
@@ -4466,8 +4477,8 @@ import_one_object_direct (OstreeRepo *dest_repo, OstreeRepo *src_repo, const cha
           (void)futimens (tmp_dest.fd, ts);
         }
 
-      if (!_ostree_repo_commit_tmpf_final (dest_repo, checksum, objtype, &tmp_dest, cancellable,
-                                           error))
+      if (!_ostree_repo_commit_tmpf_final (dest_repo, checksum, objtype, &tmp_dest, NULL,
+                                           cancellable, error))
         return FALSE;
     }
 
index 77ea2a6bccd253ffc6845856a42cc347491ff8de..ddaf5e78883e6b6c55028bcc4570cf993fb5f23c 100644 (file)
@@ -360,7 +360,8 @@ gboolean _ostree_repo_import_object (OstreeRepo *self, OstreeRepo *source, Ostre
 
 gboolean _ostree_repo_commit_tmpf_final (OstreeRepo *self, const char *checksum,
                                          OstreeObjectType objtype, GLnxTmpfile *tmpf,
-                                         GCancellable *cancellable, GError **error);
+                                         gboolean *out_existed, GCancellable *cancellable,
+                                         GError **error);
 
 typedef struct
 {
index 9989415fa22cdcd39b2595ded79eaff0eb1cfc3f..527ec2cb9712ba8e6cfc236645ef45d78881d511 100644 (file)
@@ -952,8 +952,8 @@ content_fetch_on_complete (GObject *object, GAsyncResult *result, gpointer user_
   if (pull_data->trusted_http_direct)
     {
       g_assert (!verifying_bareuseronly);
-      if (!_ostree_repo_commit_tmpf_final (pull_data->repo, checksum, objtype, &tmpf, cancellable,
-                                           error))
+      if (!_ostree_repo_commit_tmpf_final (pull_data->repo, checksum, objtype, &tmpf, NULL,
+                                           cancellable, error))
         goto out;
       pull_data->n_fetched_content++;
     }