Bug 1657663: Improve timestamp precision for nsLocalFileUnix r=froydnj,barret

This change updates the unix implementation of nsLocalFile
Set/GetLastModifiedTime methods to improve the precision of file modification
times from a 1 second resolution to a 1 millisecond resolution.

Differential Revision: https://phabricator.services.mozilla.com/D86238
This commit is contained in:
Keefer Rourke 2020-08-26 15:32:15 +00:00
parent e51e603b3c
commit 53c14a2428
3 changed files with 68 additions and 37 deletions

View File

@ -1128,7 +1128,7 @@ fi
dnl Checks for library functions.
dnl ========================================================
AC_CHECK_FUNCS(stat64 lstat64 truncate64 statvfs64 statvfs statfs64 statfs getpagesize gmtime_r localtime_r arc4random arc4random_buf mallinfo gettid lchown setpriority strerror syscall)
AC_CHECK_FUNCS(stat64 lstat64 truncate64 statvfs64 statvfs statfs64 statfs getpagesize gmtime_r localtime_r arc4random arc4random_buf mallinfo gettid lchown setpriority strerror syscall lutimes)
dnl check for clock_gettime(), the CLOCK_MONOTONIC clock
AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC),

View File

@ -13,9 +13,13 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/Sprintf.h"
#include "mozilla/FilePreferences.h"
#include "prtime.h"
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@ -1073,68 +1077,92 @@ nsLocalFile::Remove(bool aRecursive) {
return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
}
NS_IMETHODIMP
nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime) {
nsresult nsLocalFile::GetLastModifiedTimeImpl(PRTime* aLastModTime,
bool aFollowLinks) {
CHECK_mPath();
if (NS_WARN_IF(!aLastModTime)) {
return NS_ERROR_INVALID_ARG;
}
PRFileInfo64 info;
if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS) {
using StatFn = int (*)(const char*, struct STAT*);
StatFn statFn = aFollowLinks ? &STAT : &LSTAT;
struct STAT fileStats {};
if (statFn(mPath.get(), &fileStats) < 0) {
return NSRESULT_FOR_ERRNO();
}
PRTime modTime = info.modifyTime;
if (modTime == 0) {
*aLastModTime = 0;
} else {
*aLastModTime = modTime / PR_USEC_PER_MSEC;
}
int64_t modSec = 0;
int64_t modNSec = 0;
#if (defined(__APPLE__) && defined(__MACH__))
modSec = fileStats.st_mtimespec.tv_sec;
modNSec = fileStats.st_mtimespec.tv_nsec;
#else
modSec = fileStats.st_mtim.tv_sec;
modNSec = fileStats.st_mtim.tv_nsec;
#endif
*aLastModTime =
PRTime(modSec * PR_MSEC_PER_SEC) + PRTime(modNSec / PR_NSEC_PER_MSEC);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetLastModifiedTime(PRTime aLastModTime) {
nsresult nsLocalFile::SetLastModifiedTimeImpl(PRTime aLastModTime,
bool aFollowLinks) {
CHECK_mPath();
using UtimesFn = int (*)(const char*, const timeval*);
UtimesFn utimesFn = &utimes;
#if HAVE_LUTIMES
if (!aFollowLinks) {
utimesFn = &lutimes;
}
#endif
int result;
if (aLastModTime != 0) {
ENSURE_STAT_CACHE();
struct utimbuf ut;
ut.actime = mCachedStat.st_atime;
timeval access{};
#if (defined(__APPLE__) && defined(__MACH__))
access.tv_sec = mCachedStat.st_atimespec.tv_sec;
access.tv_usec = mCachedStat.st_atimespec.tv_nsec / 1000;
#else
access.tv_sec = mCachedStat.st_atim.tv_sec;
access.tv_usec = mCachedStat.st_atim.tv_nsec / 1000;
#endif
timeval modification{};
modification.tv_sec = aLastModTime / PR_MSEC_PER_SEC;
modification.tv_usec = (aLastModTime % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC;
// convert milliseconds to seconds since the unix epoch
ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
result = utime(mPath.get(), &ut);
timeval times[2];
times[0] = access;
times[1] = modification;
result = utimesFn(mPath.get(), times);
} else {
result = utime(mPath.get(), nullptr);
result = utimesFn(mPath.get(), nullptr);
}
return NSRESULT_FOR_RETURN(result);
}
NS_IMETHODIMP
nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink) {
CHECK_mPath();
if (NS_WARN_IF(!aLastModTimeOfLink)) {
return NS_ERROR_INVALID_ARG;
}
struct STAT sbuf;
if (LSTAT(mPath.get(), &sbuf) == -1) {
return NSRESULT_FOR_ERRNO();
}
*aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
return NS_OK;
nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime) {
return GetLastModifiedTimeImpl(aLastModTime, /* follow links? */ true);
}
NS_IMETHODIMP
nsLocalFile::SetLastModifiedTime(PRTime aLastModTime) {
return SetLastModifiedTimeImpl(aLastModTime, /* follow links ? */ true);
}
NS_IMETHODIMP
nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink) {
return GetLastModifiedTimeImpl(aLastModTimeOfLink, /* follow link? */ false);
}
/*
* utime(2) may or may not dereference symlinks, joy.
*/
NS_IMETHODIMP
nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink) {
return SetLastModifiedTime(aLastModTimeOfLink);
return SetLastModifiedTimeImpl(aLastModTimeOfLink, /* follow links? */ false);
}
/*

View File

@ -122,6 +122,9 @@ class nsLocalFile final
nsresult CreateAndKeepOpen(uint32_t aType, int aFlags, uint32_t aPermissions,
PRFileDesc** aResult);
nsresult SetLastModifiedTimeImpl(PRTime aLastModTime, bool aFollowLinks);
nsresult GetLastModifiedTimeImpl(PRTime* aLastModTime, bool aFollowLinks);
};
#endif /* _nsLocalFileUNIX_H_ */