From: Neeraj Singh <neerajsi@xxxxxxxxxxxxx> Make close_loose_object do all of the steps for syncing and correctly naming a new loose object so that it can be reimplemented in the upcoming bulk-fsync mode. Use futimens, which is available in POSIX.1-2008 to update the file timestamps. This should be slightly faster than utime, since we have a file descriptor already available. This change allows us to update the time before closing, renaming, and potentially fsyincing the file being refreshed. This code is currently only invoked by git-pack-objects via force_object_loose. Implement a futimens shim for the Windows port of Git. Signed-off-by: Neeraj Singh <neerajsi@xxxxxxxxxxxxx> --- compat/mingw.c | 53 ++++++++++++++++++++++++++++++++++---------------- compat/mingw.h | 2 ++ object-file.c | 17 ++++++++-------- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 9e0cd1e097f..ce14b21c182 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -734,11 +734,14 @@ int mingw_chmod(const char *filename, int mode) * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC. * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch. */ + +#define UNIX_EPOCH_FILETIME 116444736000000000LL + static inline long long filetime_to_hnsec(const FILETIME *ft) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; /* Windows to Unix Epoch conversion */ - return winTime - 116444736000000000LL; + return winTime - UNIX_EPOCH_FILETIME; } static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts) @@ -748,6 +751,13 @@ static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts) ts->tv_nsec = (hnsec % 10000000) * 100; } +static inline void timespec_to_filetime(const struct timespec *t, FILETIME *ft) +{ + long long winTime = t->tv_sec * 10000000LL + t->tv_nsec / 100 + UNIX_EPOCH_FILETIME; + ft->dwLowDateTime = winTime; + ft->dwHighDateTime = winTime >> 32; +} + /** * Verifies that safe_create_leading_directories() would succeed. */ @@ -949,19 +959,33 @@ int mingw_fstat(int fd, struct stat *buf) } } -static inline void time_t_to_filetime(time_t t, FILETIME *ft) +int mingw_futimens(int fd, const struct timespec times[2]) { - long long winTime = t * 10000000LL + 116444736000000000LL; - ft->dwLowDateTime = winTime; - ft->dwHighDateTime = winTime >> 32; + FILETIME mft, aft; + + if (times) { + timespec_to_filetime(×[0], &aft); + timespec_to_filetime(×[1], &mft); + } else { + GetSystemTimeAsFileTime(&mft); + aft = mft; + } + + if (!SetFileTime((HANDLE)_get_osfhandle(fd), NULL, &aft, &mft)) { + errno = EINVAL; + return -1; + } + + return 0; } -int mingw_utime (const char *file_name, const struct utimbuf *times) +int mingw_utime(const char *file_name, const struct utimbuf *times) { - FILETIME mft, aft; int fh, rc; DWORD attrs; wchar_t wfilename[MAX_PATH]; + struct timespec ts[2]; + if (xutftowcs_path(wfilename, file_name) < 0) return -1; @@ -979,17 +1003,12 @@ 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); - } else { - GetSystemTimeAsFileTime(&mft); - aft = mft; + memset(ts, 0, sizeof(ts)); + ts[0].tv_sec = times->actime; + ts[1].tv_sec = times->modtime; } - if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) { - errno = EINVAL; - rc = -1; - } else - rc = 0; + + rc = mingw_futimens(fh, times ? ts : NULL); close(fh); revert_attrs: diff --git a/compat/mingw.h b/compat/mingw.h index c9a52ad64a6..87944dfec72 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -398,6 +398,8 @@ int mingw_fstat(int fd, struct stat *buf); int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime +int mingw_futimens(int fd, const struct timespec times[2]); +#define futimens mingw_futimens size_t mingw_strftime(char *s, size_t max, const char *format, const struct tm *tm); #define strftime mingw_strftime diff --git a/object-file.c b/object-file.c index a8be8994814..5421811273e 100644 --- a/object-file.c +++ b/object-file.c @@ -1860,12 +1860,13 @@ int hash_object_file(const struct git_hash_algo *algo, const void *buf, } /* Finalize a file on disk, and close it. */ -static void close_loose_object(int fd) +static int close_loose_object(int fd, const char *tmpfile, const char *filename) { if (fsync_object_files) fsync_or_die(fd, "loose object file"); if (close(fd) != 0) die_errno(_("error when closing loose object file")); + return finalize_object_file(tmpfile, filename); } /* Size of directory component, including the ending '/' */ @@ -1973,17 +1974,15 @@ static int write_loose_object(const struct object_id *oid, char *hdr, die(_("confused by unstable object source data for %s"), oid_to_hex(oid)); - close_loose_object(fd); - if (mtime) { - struct utimbuf utb; - utb.actime = mtime; - utb.modtime = mtime; - if (utime(tmp_file.buf, &utb) < 0) - warning_errno(_("failed utime() on %s"), tmp_file.buf); + struct timespec ts[2] = {0}; + ts[0].tv_sec = mtime; + ts[1].tv_sec = mtime; + if (futimens(fd, ts) < 0) + warning_errno(_("failed futimes() on %s"), tmp_file.buf); } - return finalize_object_file(tmp_file.buf, filename.buf); + return close_loose_object(fd, tmp_file.buf, filename.buf); } static int freshen_loose_object(const struct object_id *oid) -- gitgitgadget