All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alexey Samsonov via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Alexey Samsonov <vonosmas@gmail.com>,
	Alexey Samsonov <vonosmas@gmail.com>
Subject: [PATCH 2/3] treewide: use utimensat(2) instead of legacy utime(3p)
Date: Fri, 21 Aug 2026 14:23:22 +0000	[thread overview]
Message-ID: <6f5bd13d8e41e02af92df0274dbd435a395d6835.1787322203.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2209.git.1787322203.gitgitgadget@gmail.com>

From: Alexey Samsonov <vonosmas@gmail.com>

Now that a compatibility wrapper for utimensat(2) has been introduced,
migrate all call sites across the codebase to use utimensat(2) instead of
the legacy utime(3p) interface:

- In `commit-graph.c`, use utimensat(2) with UTIME_OMIT and the computed
  timestamp `now` to bump the commit-graph modification time consistently
  across all files without needing an extra stat(2) call to preserve atime.
- In `copy.c`, use utimensat(2) to copy full sub-second access and
  modification timestamps from the source file.
- In `odb/source-packed.c`, `odb/source-loose.c`, and `object-file.c`,
  use utimensat(2) with `struct timespec` to freshen file timestamps.
- In `builtin/pack-objects.c`, update the pack timestamp with
  utimensat(2).
- In `rerere.c`, touch the postimage file with utimensat(2) passing NULL
  to set both atime and mtime to current time.
- In `t/helper/test-chmtime.c`, update file modification times using
  utimensat(2).

Signed-off-by: Alexey Samsonov <vonosmas@gmail.com>
---
 builtin/pack-objects.c  | 12 +++++++-----
 commit-graph.c          | 17 ++++++-----------
 copy.c                  | 10 ++++++----
 object-file.c           | 12 +++++++-----
 odb/source-loose.c      | 10 +++++-----
 odb/source-packed.c     | 12 +++++++-----
 rerere.c                |  4 ++--
 t/helper/test-chmtime.c | 19 ++++++++++++-------
 8 files changed, 52 insertions(+), 44 deletions(-)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1ec5b6f206..35bdbc2b6a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1438,11 +1438,13 @@ static void write_pack_file(void)
 			} else if (!last_mtime) {
 				last_mtime = st.st_mtime;
 			} else {
-				struct utimbuf utb;
-				utb.actime = st.st_atime;
-				utb.modtime = --last_mtime;
-				if (utime(pack_tmp_name, &utb) < 0)
-					warning_errno(_("failed utime() on %s"), pack_tmp_name);
+				struct timespec times[2];
+				times[0].tv_sec = st.st_atime;
+				times[0].tv_nsec = ST_ATIME_NSEC(st);
+				times[1].tv_sec = --last_mtime;
+				times[1].tv_nsec = 0;
+				if (utimensat(AT_FDCWD, pack_tmp_name, times, 0) < 0)
+					warning_errno(_("failed utimensat() on %s"), pack_tmp_name);
 			}
 
 			strbuf_addf(&tmpname, "%s-%s.", base_name,
diff --git a/commit-graph.c b/commit-graph.c
index 49e8f63930..08bbba3d98 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -2484,18 +2484,13 @@ static void mark_commit_graphs(struct write_commit_graph_context *ctx)
 {
 	uint32_t i;
 	time_t now = time(NULL);
+	struct timespec times[2] = {
+		{ .tv_nsec = UTIME_OMIT },
+		{ .tv_sec = now, .tv_nsec = 0 },
+	};
 
-	for (i = ctx->num_commit_graphs_after - 1; i < ctx->num_commit_graphs_before; i++) {
-		struct stat st;
-		struct utimbuf updated_time;
-
-		if (stat(ctx->commit_graph_filenames_before[i], &st) < 0)
-			continue;
-
-		updated_time.actime = st.st_atime;
-		updated_time.modtime = now;
-		utime(ctx->commit_graph_filenames_before[i], &updated_time);
-	}
+	for (i = ctx->num_commit_graphs_after - 1; i < ctx->num_commit_graphs_before; i++)
+		utimensat(AT_FDCWD, ctx->commit_graph_filenames_before[i], times, 0);
 }
 
 static void expire_commit_graphs(struct write_commit_graph_context *ctx)
diff --git a/copy.c b/copy.c
index 6074132050..39673f7829 100644
--- a/copy.c
+++ b/copy.c
@@ -23,12 +23,14 @@ int copy_fd(int ifd, int ofd)
 static int copy_times(const char *dst, const char *src)
 {
 	struct stat st;
-	struct utimbuf times;
+	struct timespec times[2];
 	if (stat(src, &st) < 0)
 		return -1;
-	times.actime = st.st_atime;
-	times.modtime = st.st_mtime;
-	if (utime(dst, &times) < 0)
+	times[0].tv_sec = st.st_atime;
+	times[0].tv_nsec = ST_ATIME_NSEC(st);
+	times[1].tv_sec = st.st_mtime;
+	times[1].tv_nsec = ST_MTIME_NSEC(st);
+	if (utimensat(AT_FDCWD, dst, times, 0) < 0)
 		return -1;
 	return 0;
 }
diff --git a/object-file.c b/object-file.c
index ec35c318bc..5e4ccb36d5 100644
--- a/object-file.c
+++ b/object-file.c
@@ -69,15 +69,17 @@ const char *odb_loose_path(struct odb_source_loose *loose,
 /* Returns 1 if we have successfully freshened the file, 0 otherwise. */
 static int freshen_file(const char *fn, const time_t *mtime)
 {
-	struct utimbuf times, *timesp = NULL;
+	struct timespec times[2], *timesp = NULL;
 
 	if (mtime) {
-		times.actime = *mtime;
-		times.modtime = *mtime;
-		timesp = &times;
+		times[0].tv_sec = *mtime;
+		times[0].tv_nsec = 0;
+		times[1].tv_sec = *mtime;
+		times[1].tv_nsec = 0;
+		timesp = times;
 	}
 
-	return !utime(fn, timesp);
+	return !utimensat(AT_FDCWD, fn, timesp, 0);
 }
 
 /*
diff --git a/odb/source-loose.c b/odb/source-loose.c
index ef0e919277..1fdaa9f88f 100644
--- a/odb/source-loose.c
+++ b/odb/source-loose.c
@@ -807,14 +807,14 @@ static int write_loose_object(struct odb_source_loose *loose,
 	close_loose_object(loose, fd, tmp_file.buf);
 
 	if (mtime) {
-		struct utimbuf utb = {
-			.actime = *mtime,
-			.modtime = *mtime,
+		struct timespec times[2] = {
+			{ .tv_sec = *mtime },
+			{ .tv_sec = *mtime },
 		};
 
-		if (utime(tmp_file.buf, &utb) < 0 &&
+		if (utimensat(AT_FDCWD, tmp_file.buf, times, 0) < 0 &&
 		    !(flags & ODB_WRITE_OBJECT_SILENT))
-			warning_errno(_("failed utime() on %s"), tmp_file.buf);
+			warning_errno(_("failed utimensat() on %s"), tmp_file.buf);
 	}
 
 	return finalize_object_file_flags(loose->base.odb->repo, tmp_file.buf, filename.buf,
diff --git a/odb/source-packed.c b/odb/source-packed.c
index 0890704e76..64871ff8da 100644
--- a/odb/source-packed.c
+++ b/odb/source-packed.c
@@ -574,13 +574,15 @@ static int odb_source_packed_freshen_object(struct odb_source *source,
 					    const time_t *mtime)
 {
 	struct odb_source_packed *packed = odb_source_packed_downcast(source);
-	struct utimbuf times, *timesp = NULL;
+	struct timespec times[2], *timesp = NULL;
 	struct pack_entry e;
 
 	if (mtime) {
-		times.actime = *mtime;
-		times.modtime = *mtime;
-		timesp = &times;
+		times[0].tv_sec = *mtime;
+		times[0].tv_nsec = 0;
+		times[1].tv_sec = *mtime;
+		times[1].tv_nsec = 0;
+		timesp = times;
 	}
 
 	if (!find_pack_entry(packed, oid, &e))
@@ -589,7 +591,7 @@ static int odb_source_packed_freshen_object(struct odb_source *source,
 		return 0;
 	if (e.p->freshened)
 		return 1;
-	if (utime(e.p->pack_name, timesp))
+	if (utimensat(AT_FDCWD, e.p->pack_name, timesp, 0))
 		return 0;
 	e.p->freshened = 1;
 
diff --git a/rerere.c b/rerere.c
index 3d3bd0db16..b64771f57f 100644
--- a/rerere.c
+++ b/rerere.c
@@ -658,8 +658,8 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 	 * A successful replay of recorded resolution.
 	 * Mark that "postimage" was used to help gc.
 	 */
-	if (utime(rerere_path(&buf, id, "postimage"), NULL) < 0)
-		warning_errno(_("failed utime() on '%s'"),
+	if (utimensat(AT_FDCWD, rerere_path(&buf, id, "postimage"), NULL, 0) < 0)
+		warning_errno(_("failed utimensat() on '%s'"),
 			      rerere_path(&buf, id, "postimage"));
 
 	/* Update "path" with the resolution */
diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c
index 0e5538833a..a9e6eb78b8 100644
--- a/t/helper/test-chmtime.c
+++ b/t/helper/test-chmtime.c
@@ -105,7 +105,8 @@ int cmd__chmtime(int argc, const char **argv)
 
 	for (; i < argc; i++) {
 		struct stat sb;
-		struct utimbuf utb;
+		struct timespec times[2];
+		int64_t mtime_sec;
 		uintmax_t mtime;
 
 		if (stat(argv[i], &sb) < 0) {
@@ -123,22 +124,26 @@ int cmd__chmtime(int argc, const char **argv)
 		}
 #endif
 
-		utb.actime = sb.st_atime;
-		utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
+		mtime_sec = set_eq ? set_time : sb.st_mtime + set_time;
 
-		mtime = utb.modtime < 0 ? 0: utb.modtime;
+		times[0].tv_sec = sb.st_atime;
+		times[0].tv_nsec = ST_ATIME_NSEC(sb);
+		times[1].tv_sec = mtime_sec;
+		times[1].tv_nsec = 0;
+
+		mtime = mtime_sec < 0 ? 0 : mtime_sec;
 		if (get) {
 			printf("%"PRIuMAX"\n", mtime);
 		} else if (verbose) {
 			printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
 		}
 
-		if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
+		if (mtime_sec != sb.st_mtime && utimensat(AT_FDCWD, argv[i], times, 0) < 0) {
 #ifdef GIT_WINDOWS_NATIVE
 			if (S_ISDIR(sb.st_mode)) {
 				/*
-				 * NEEDSWORK: The Windows version of `utime()`
-				 * (aka `mingw_utime()`) does not correctly
+				 * NEEDSWORK: The Windows version of `utimensat()`
+				 * (aka `mingw_utimensat()`) does not correctly
 				 * handle directory arguments, since it uses
 				 * `_wopen()`.  Ignore it for now since this
 				 * is just a test.
-- 
gitgitgadget


  parent reply	other threads:[~2026-08-21 14:23 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-21 14:23 [PATCH 0/3] treewide: migrate from legacy utime.h to utimensat Alexey Samsonov via GitGitGadget
2026-08-21 14:23 ` [PATCH 1/3] compat/posix: introduce utimensat(2) wrapper Alexey Samsonov via GitGitGadget
2026-08-21 14:23 ` Alexey Samsonov via GitGitGadget [this message]
2026-08-21 14:23 ` [PATCH 3/3] compat/posix: drop legacy <utime.h> header and shims Alexey Samsonov via GitGitGadget
2026-08-21 17:33 ` [PATCH 0/3] treewide: migrate from legacy utime.h to utimensat Junio C Hamano
2026-08-22 16:03 ` brian m. carlson
2026-08-22 17:59   ` Junio C Hamano
2026-08-22 21:15     ` brian m. carlson
2026-08-23 15:23       ` Junio C Hamano
2026-08-23 13:26     ` Weijie Yuan
2026-08-23 15:26       ` Junio C Hamano
2026-08-23 15:45         ` Weijie Yuan
2026-08-24  1:49           ` Junio C Hamano
2026-08-24 12:19             ` Weijie Yuan
2026-08-24 15:33             ` Weijie Yuan
2026-08-24 16:06               ` Junio C Hamano
2026-08-24 16:23                 ` Junio C Hamano
2026-08-24 16:33                 ` Weijie Yuan
2026-08-24 20:25                 ` Oswald Buddenhagen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6f5bd13d8e41e02af92df0274dbd435a395d6835.1787322203.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=vonosmas@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.