Thread (12 messages) flat view 12 messages, 4 authors, 3h ago
HOTtoday

[PATCH 2/3] treewide: use utimensat(2) instead of legacy utime(3p)

From: Alexey Samsonov via GitGitGadget <hidden>
Date: 2026-08-21 14:23:28
Subsystem: the rest · Maintainer: Linus Torvalds

From: Alexey Samsonov <redacted>

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 <redacted>
---
 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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help