From e3014f72d705339d921963bd463f848b4e5aa061 Mon Sep 17 00:00:00 2001 From: Daniel Scharrer Date: Sun, 20 Jan 2013 22:38:15 +0100 Subject: [PATCH] Add support for nanosecond file times under Linux Also, for other POSIX.1-2008+ systems. --- CMakeLists.txt | 6 +++- src/configure.hpp.in | 2 ++ src/util/time.cpp | 79 ++++++++++++++++++++++++++++++++++---------- src/util/time.hpp | 6 ++-- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbd6040..ae2bba7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,11 @@ if(NOT INNOEXTRACT_HAVE_GMTIME_R) check_symbol_exists(_gmtime_s "time.h" INNOEXTRACT_HAVE_GMTIME_S) endif() if(NOT WIN32) - check_symbol_exists(utimes "sys/time.h" INNOEXTRACT_HAVE_UTIMES) + check_symbol_exists(utimensat "sys/stat.h" INNOEXTRACT_HAVE_UTIMENSAT) + check_symbol_exists(AT_FDCWD "fcntl.h" INNOEXTRACT_HAVE_AT_FDCWD) + if(NOT (INNOEXTRACT_HAVE_UTIMENSAT AND INNOEXTRACT_HAVE_AT_FDCWD)) + check_symbol_exists(utimes "sys/time.h" INNOEXTRACT_HAVE_UTIMES) + endif() endif() check_symbol_exists(bswap_16 "byteswap.h" INNOEXTRACT_HAVE_BSWAP_16) diff --git a/src/configure.hpp.in b/src/configure.hpp.in index 1ced508..a3a2170 100644 --- a/src/configure.hpp.in +++ b/src/configure.hpp.in @@ -8,6 +8,8 @@ #cmakedefine01 INNOEXTRACT_HAVE_MKGMTIME #cmakedefine01 INNOEXTRACT_HAVE_GMTIME_R #cmakedefine01 INNOEXTRACT_HAVE_GMTIME_S +#cmakedefine01 INNOEXTRACT_HAVE_UTIMENSAT +#cmakedefine01 INNOEXTRACT_HAVE_AT_FDCWD #cmakedefine01 INNOEXTRACT_HAVE_UTIMES #cmakedefine01 INNOEXTRACT_HAVE_LZMA #cmakedefine01 INNOEXTRACT_IS_BIG_ENDIAN diff --git a/src/util/time.cpp b/src/util/time.cpp index 6553be3..3e192ea 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -33,7 +33,10 @@ #include #endif -#if INNOEXTRACT_HAVE_UTIMES +#if INNOEXTRACT_HAVE_UTIMENSAT && INNOEXTRACT_HAVE_AT_FDCWD +#include +#include +#elif INNOEXTRACT_HAVE_UTIMES #include #elif !defined(_WIN32) #include @@ -46,15 +49,19 @@ static void set_timezone(const char * value) { const char * variable = "TZ"; #if defined(WIN32) + SetEnvironmentVariable(variable, value); _tzset(); + #else + if(value) { setenv(variable, value, 1); } else { unsetenv(variable); } tzset(); + #endif } @@ -65,14 +72,20 @@ std::time_t parse_time(std::tm tm) { #if INNOEXTRACT_HAVE_TIMEGM + // GNU / BSD extension + return timegm(&tm); #elif INNOEXTRACT_HAVE_MKGMTIME + // Windows + return _mkgmtime(&tm); #else + // Standard, but not thread-safe - should be OK for our use though + char * tz = getenv("TZ"); set_timezone("UTC"); @@ -93,16 +106,21 @@ std::tm format_time(time_t t) { #if INNOEXTRACT_HAVE_GMTIME_R + // POSIX.1 + gmtime_r(&t, &ret); #elif INNOEXTRACT_HAVE_GMTIME_S + // Windows (MSVC) + _gmtime_s(&ret, &t); #else - // Hope that this is threadsafe... - std::tm * tmp = gmtime(&t); + // Standard C++, but may not be thread-safe + + std::tm * tmp = std::gmtime(&t); if(tmp) { ret = *tmp; } else { @@ -116,7 +134,7 @@ std::tm format_time(time_t t) { return ret; } -time_t to_local_time(time_t t) { +std::time_t to_local_time(std::time_t t) { // Format time as UTC ... std::tm time = format_time(t); @@ -126,18 +144,52 @@ time_t to_local_time(time_t t) { return std::mktime(&time); } +void set_local_timezone(std::string timezone) { + + /* + * The TZ variable interprets the offset as the change from local time + * to UTC while everyone else does the opposite. + * We flip the direction so that timezone strings such as GMT+1 work as expected. + */ + for(size_t i = 0; i < timezone.length(); i++) { + if(timezone[i] == '+') { + timezone[i] = '-'; + } else if(timezone[i] == '-') { + timezone[i] = '+'; + } + } + + set_timezone(timezone.c_str()); +} + #if defined(_WIN32) + static HANDLE open_file(LPCSTR name) { return CreateFileA(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); } + static HANDLE open_file(LPCWSTR name) { return CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); } + #endif bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t nsec) { -#if INNOEXTRACT_HAVE_UTIMES +#if INNOEXTRACT_HAVE_UTIMENSAT && INNOEXTRACT_HAVE_AT_FDCWD + + // nanosecond precision, for Linux and POSIX.1-2008+ systems + + struct timespec times[2]; + times[0].tv_sec = t; + times[0].tv_nsec = int32_t(nsec); + times[1] = times[0]; + + return (utimensat(AT_FDCWD, path.string().c_str(), times, 0) == 0); + +#elif INNOEXTRACT_HAVE_UTIMES + + // microsecond precision, for older POSIX systems (4.3BSD, POSIX.1-2001) struct timeval times[2]; @@ -149,6 +201,8 @@ bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t #elif defined(_WIN32) + // 100-nanosecond precision, for Windows + // Prevent unused function warnings (void)(HANDLE(*)(LPCSTR))open_file; (void)(HANDLE(*)(LPCWSTR))open_file; @@ -173,6 +227,8 @@ bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t #else + // fallback with second precision or worse + try { (void)nsec; // sub-second precision not supported by boost boost::filesystem::last_write_time(path, t); @@ -185,17 +241,4 @@ bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t } -void set_local_timezone(std::string timezone) { - - for(size_t i = 0; i < timezone.length(); i++) { - if(timezone[i] == '+') { - timezone[i] = '-'; - } else if(timezone[i] == '-') { - timezone[i] = '+'; - } - } - - set_timezone(timezone.c_str()); -} - } // namespace util diff --git a/src/util/time.hpp b/src/util/time.hpp index ff57757..924f18c 100644 --- a/src/util/time.hpp +++ b/src/util/time.hpp @@ -47,12 +47,12 @@ std::tm format_time(std::time_t t); */ std::time_t to_local_time(std::time_t t); -//! Set a file's creation/modification time -bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t nsec); - //! Set the local timezone used by to_local_time void set_local_timezone(std::string timezone); +//! Set a file's creation/modification time +bool set_file_time(const boost::filesystem::path & path, std::time_t t, uint32_t nsec); + } // namespace util #endif // INNOEXTRACT_UTIL_TIME_HPP