Merge branch 'as/utimensat-utimes' into jch
The codebase has been updated to use the newer utimensat() POSIX function instead of the obsolescent utime(), allowing high-precision timestamps while preserving fallback compatibility. * as/utimensat-utimes: compat/posix: drop legacy <utime.h> header and shims treewide: use utimensat(2) instead of legacy utime(3p) compat/posix: introduce utimensat(2) wrapperjch
commit
01248c6614
6
Makefile
6
Makefile
|
|
@ -70,6 +70,8 @@ include shared.mak
|
|||
#
|
||||
# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
|
||||
#
|
||||
# Define NO_UTIMENSAT if you don't have utimensat.
|
||||
#
|
||||
# Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash.
|
||||
#
|
||||
# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
|
||||
|
|
@ -2052,6 +2054,10 @@ ifdef NO_WRITEV
|
|||
COMPAT_CFLAGS += -DNO_WRITEV
|
||||
COMPAT_OBJS += compat/writev.o
|
||||
endif
|
||||
ifdef NO_UTIMENSAT
|
||||
COMPAT_CFLAGS += -DNO_UTIMENSAT
|
||||
COMPAT_OBJS += compat/utimensat.o
|
||||
endif
|
||||
ifdef NO_FAST_WORKING_DIRECTORY
|
||||
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1442,11 +1442,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,
|
||||
|
|
|
|||
|
|
@ -2480,18 +2480,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)
|
||||
|
|
|
|||
|
|
@ -384,8 +384,8 @@ int mingw_fstat(int fd, struct stat *buf);
|
|||
#define lstat mingw_lstat
|
||||
|
||||
|
||||
int mingw_utime(const char *file_name, const struct utimbuf *times);
|
||||
#define utime mingw_utime
|
||||
int mingw_utimensat(int fd, const char *path, const struct timespec times[2], int flag);
|
||||
#define utimensat mingw_utimensat
|
||||
size_t mingw_strftime(char *s, size_t max,
|
||||
const char *format, const struct tm *tm);
|
||||
#define strftime mingw_strftime
|
||||
|
|
|
|||
|
|
@ -1391,22 +1391,33 @@ int mingw_fstat(int fd, struct stat *buf)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void time_t_to_filetime(time_t t, FILETIME *ft)
|
||||
static inline void timespec_to_filetime(const struct timespec *ts, FILETIME *ft)
|
||||
{
|
||||
long long winTime = t * 10000000LL + 116444736000000000LL;
|
||||
long long winTime = (long long)ts->tv_sec * 10000000LL + (ts->tv_nsec / 100) + 116444736000000000LL;
|
||||
ft->dwLowDateTime = winTime;
|
||||
ft->dwHighDateTime = winTime >> 32;
|
||||
}
|
||||
|
||||
int mingw_utime (const char *file_name, const struct utimbuf *times)
|
||||
int mingw_utimensat(int fd, const char *path, const struct timespec times[2], int flag)
|
||||
{
|
||||
FILETIME mft, aft;
|
||||
FILETIME *paft = &aft, *pmft = &mft;
|
||||
int rc;
|
||||
DWORD attrs;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
HANDLE osfilehandle;
|
||||
|
||||
if (xutftowcs_path(wfilename, file_name) < 0)
|
||||
if (fd != AT_FDCWD) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xutftowcs_path(wfilename, path) < 0)
|
||||
return -1;
|
||||
|
||||
/* must have write permission */
|
||||
|
|
@ -1433,14 +1444,25 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
|
|||
}
|
||||
|
||||
if (times) {
|
||||
time_t_to_filetime(times->modtime, &mft);
|
||||
time_t_to_filetime(times->actime, &aft);
|
||||
if (times[0].tv_nsec == UTIME_NOW)
|
||||
GetSystemTimeAsFileTime(&aft);
|
||||
else if (times[0].tv_nsec == UTIME_OMIT)
|
||||
paft = NULL;
|
||||
else
|
||||
timespec_to_filetime(×[0], &aft);
|
||||
|
||||
if (times[1].tv_nsec == UTIME_NOW)
|
||||
GetSystemTimeAsFileTime(&mft);
|
||||
else if (times[1].tv_nsec == UTIME_OMIT)
|
||||
pmft = NULL;
|
||||
else
|
||||
timespec_to_filetime(×[1], &mft);
|
||||
} else {
|
||||
GetSystemTimeAsFileTime(&mft);
|
||||
aft = mft;
|
||||
}
|
||||
|
||||
if (!SetFileTime(osfilehandle, NULL, &aft, &mft)) {
|
||||
if (!SetFileTime(osfilehandle, NULL, paft, pmft)) {
|
||||
errno = EINVAL;
|
||||
rc = -1;
|
||||
} else
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@
|
|||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <regex.h>
|
||||
#include <utime.h>
|
||||
#include <syslog.h>
|
||||
#if !defined(NO_POLL_H)
|
||||
#include <poll.h>
|
||||
|
|
@ -348,6 +347,24 @@ struct git_iovec {
|
|||
ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
|
||||
#endif
|
||||
|
||||
#ifndef AT_FDCWD
|
||||
#define AT_FDCWD (-100)
|
||||
#endif
|
||||
#ifndef UTIME_NOW
|
||||
#define UTIME_NOW ((1L << 30) - 1L)
|
||||
#endif
|
||||
#ifndef UTIME_OMIT
|
||||
#define UTIME_OMIT ((1L << 30) - 2L)
|
||||
#endif
|
||||
|
||||
#ifdef NO_UTIMENSAT
|
||||
#ifdef utimensat
|
||||
#undef utimensat
|
||||
#endif
|
||||
#define utimensat git_utimensat
|
||||
int git_utimensat(int fd, const char *path, const struct timespec times[2], int flag);
|
||||
#endif
|
||||
|
||||
#ifdef NO_SETENV
|
||||
#define setenv gitsetenv
|
||||
int gitsetenv(const char *, const char *, int);
|
||||
|
|
@ -502,13 +519,16 @@ int git_qsort_s(void *base, size_t nmemb, size_t size,
|
|||
|
||||
#ifdef NO_NSEC
|
||||
#undef USE_NSEC
|
||||
#define ST_ATIME_NSEC(st) 0
|
||||
#define ST_CTIME_NSEC(st) 0
|
||||
#define ST_MTIME_NSEC(st) 0
|
||||
#else
|
||||
#ifdef USE_ST_TIMESPEC
|
||||
#define ST_ATIME_NSEC(st) ((unsigned int)((st).st_atimespec.tv_nsec))
|
||||
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
|
||||
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
|
||||
#else
|
||||
#define ST_ATIME_NSEC(st) ((unsigned int)((st).st_atim.tv_nsec))
|
||||
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
|
||||
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#include "../git-compat-util.h"
|
||||
|
||||
int git_utimensat(int fd, const char *path, const struct timespec times[2], int flag)
|
||||
{
|
||||
struct timeval tv[2];
|
||||
struct timeval *tvp = NULL;
|
||||
|
||||
if (fd != AT_FDCWD) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (times) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (times[i].tv_nsec == UTIME_NOW) {
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
tv[i] = now;
|
||||
} else if (times[i].tv_nsec == UTIME_OMIT) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
tv[i].tv_sec = (i == 0) ? st.st_atime : st.st_mtime;
|
||||
tv[i].tv_usec = (i == 0) ? ST_ATIME_NSEC(st) / 1000 : ST_MTIME_NSEC(st) / 1000;
|
||||
} else {
|
||||
tv[i].tv_sec = times[i].tv_sec;
|
||||
tv[i].tv_usec = times[i].tv_nsec / 1000;
|
||||
}
|
||||
}
|
||||
tvp = tv;
|
||||
}
|
||||
|
||||
return utimes(path, tvp);
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef _UTIME_H_
|
||||
#define _UTIME_H_
|
||||
/*
|
||||
* UTIME.H
|
||||
* This file has no copyright assigned and is placed in the Public Domain.
|
||||
* This file is a part of the mingw-runtime package.
|
||||
*
|
||||
* The mingw-runtime package and its code is distributed in the hope that it
|
||||
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
|
||||
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
|
||||
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You are free to use this package and its code without limitation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Structure used by _utime function.
|
||||
*/
|
||||
struct _utimbuf
|
||||
{
|
||||
time_t actime; /* Access time */
|
||||
time_t modtime; /* Modification time */
|
||||
};
|
||||
|
||||
#ifndef _NO_OLDNAMES
|
||||
/* NOTE: Must be the same as _utimbuf above. */
|
||||
struct utimbuf
|
||||
{
|
||||
time_t actime;
|
||||
time_t modtime;
|
||||
};
|
||||
#endif /* Not _NO_OLDNAMES */
|
||||
|
||||
#endif
|
||||
|
|
@ -1 +0,0 @@
|
|||
#include <sys/utime.h>
|
||||
|
|
@ -1146,6 +1146,12 @@ GIT_CHECK_FUNC(mkdtemp,
|
|||
[NO_MKDTEMP=YesPlease])
|
||||
GIT_CONF_SUBST([NO_MKDTEMP])
|
||||
#
|
||||
# Define NO_UTIMENSAT if you don't have utimensat in the C library.
|
||||
GIT_CHECK_FUNC(utimensat,
|
||||
[NO_UTIMENSAT=],
|
||||
[NO_UTIMENSAT=YesPlease])
|
||||
GIT_CONF_SUBST([NO_UTIMENSAT])
|
||||
#
|
||||
# Define NO_INITGROUPS if you don't have initgroups in the C library.
|
||||
GIT_CHECK_FUNC(initgroups,
|
||||
[NO_INITGROUPS=],
|
||||
|
|
|
|||
|
|
@ -387,9 +387,9 @@ set(function_checks
|
|||
strcasestr memmem strlcpy strtoimax strtoumax strtoull
|
||||
setenv mkdtemp poll pread memmem writev)
|
||||
|
||||
#unsetenv,hstrerror are incompatible with windows build
|
||||
#unsetenv,hstrerror,utimensat are incompatible with windows build (provided by compat/mingw.c)
|
||||
if(NOT WIN32)
|
||||
list(APPEND function_checks unsetenv hstrerror)
|
||||
list(APPEND function_checks unsetenv hstrerror utimensat)
|
||||
endif()
|
||||
|
||||
foreach(f ${function_checks})
|
||||
|
|
@ -435,6 +435,10 @@ if(NOT HAVE_WRITEV)
|
|||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT HAVE_UTIMENSAT)
|
||||
list(APPEND compat_SOURCES compat/utimensat.c)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_UNSETENV)
|
||||
list(APPEND compat_SOURCES compat/unsetenv.c)
|
||||
endif()
|
||||
|
|
|
|||
10
copy.c
10
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, ×) < 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1493,6 +1493,8 @@ else
|
|||
'unsetenv' : ['unsetenv.c'],
|
||||
# provided by compat/mingw.c.
|
||||
'getpagesize' : [],
|
||||
# provided by compat/mingw.c.
|
||||
'utimensat' : ['utimensat.c'],
|
||||
}
|
||||
|
||||
if get_option('b_sanitize').contains('address') or get_option('b_sanitize').contains('leak')
|
||||
|
|
|
|||
|
|
@ -71,15 +71,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[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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -815,14 +815,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,
|
||||
|
|
|
|||
|
|
@ -602,13 +602,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[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, NULL))
|
||||
|
|
@ -617,7 +619,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;
|
||||
|
||||
|
|
|
|||
4
rerere.c
4
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 */
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
*/
|
||||
#include "test-tool.h"
|
||||
#include "git-compat-util.h"
|
||||
#include <utime.h>
|
||||
|
||||
static const char usage_str[] =
|
||||
"(-v|--verbose|-g|--get) (+|=|=+|=-|-)<seconds> <file>...";
|
||||
|
|
@ -105,7 +104,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 +123,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.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,5 @@
|
|||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <regex.h>
|
||||
#include <utime.h>
|
||||
#include <syslog.h>
|
||||
#include <End.h>
|
||||
|
|
|
|||
Loading…
Reference in New Issue