QFileSystemEngine/Unix: rework the getting of nsec-precision file times

It was working on Linux because _GNU_SOURCE gets us POSIX.1-2008
compatibility, but not on macOS or the BSDs. There, we were still stuck
to full second precision.

This commit uses the template trick introduced by the futimes code
(which itself was inspired by commit 2fb42eb4af3444b11e7b1210323637937ef
in QtNetwork). Also note how it adds support for birth time, if the
system's stat struct has that information.

Tested to work on MacOS and FreeBSD. The manual filetest produces:
 Name:   .
 Path:   . (/usr/home/tjmaciei/src/qt/qt5)
 Size:   1536    Type: Directory
 Attrs:   readable writable executable hidden nativepath
 Mode:   drwxr-xr-x
 Owner:  tjmaciei (1001) Group:  tjmaciei (1001)
 Access: 2017-07-13T20:03:47.916
 Birth:  2017-07-13T20:03:47.916
 Change: 2017-07-13T20:04:41.648
 Modified: 2017-07-13T20:04:41.648

Linux will require support for statx(2).

Change-Id: I8d96dea9955d4c749b99fffd14cd97d7a8c6d45d
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2017-07-02 11:46:43 -07:00
parent 04f4d87bda
commit 5a9b1425e1
3 changed files with 145 additions and 25 deletions

View File

@ -157,10 +157,9 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e
}
#endif
#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes))
namespace {
namespace GetFileTimes {
#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes))
template <typename T>
static inline typename QtPrivate::QEnableIf<(&T::st_atim, &T::st_mtim, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
{
@ -190,11 +189,86 @@ static inline typename QtPrivate::QEnableIf<(&T::st_atimensec, &T::st_mtimensec,
modification->tv_sec = p->st_mtime;
modification->tv_usec = p->st_mtimensec / 1000;
}
}
}
#endif
qint64 timespecToMSecs(const timespec &spec)
{
return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
}
// fallback set
Q_DECL_UNUSED qint64 atime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_atime) * 1000; }
Q_DECL_UNUSED qint64 birthtime(const QT_STATBUF &, ulong) { return Q_INT64_C(0); }
Q_DECL_UNUSED qint64 ctime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_ctime) * 1000; }
Q_DECL_UNUSED qint64 mtime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_mtime) * 1000; }
// Xtim, POSIX.1-2008
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_atim, true), qint64>::type
atime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_atim); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtim, true), qint64>::type
birthtime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_birthtim); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctim, true), qint64>::type
ctime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_ctim); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtim, true), qint64>::type
mtime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_mtim); }
#ifndef st_mtimespec
// Xtimespec
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimespec, true), qint64>::type
atime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_atimespec); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimespec, true), qint64>::type
birthtime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_birthtimespec); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimespec, true), qint64>::type
ctime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_ctimespec); }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimespec, true), qint64>::type
mtime(const T &statBuffer, int)
{ return timespecToMSecs(statBuffer.st_mtimespec); }
#endif
// Xtimensec
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimensec, true), qint64>::type
atime(const T &statBuffer, int)
{ return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimensec, true), qint64>::type
birthtime(const T &statBuffer, int)
{ return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimensec, true), qint64>::type
ctime(const T &statBuffer, int)
{ return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
template <typename T>
Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimensec, true), qint64>::type
mtime(const T &statBuffer, int)
{ return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
}
}
//static
bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
{
@ -229,13 +303,6 @@ static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &st
}
#endif
#if _POSIX_VERSION >= 200809L
static qint64 timespecToMSecs(const timespec &spec)
{
return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
}
#endif
void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
{
// Permissions
@ -281,16 +348,11 @@ void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
#endif
// Times
birthTime_ = 0;
#if _POSIX_VERSION >= 200809L
modificationTime_ = timespecToMSecs(statBuffer.st_mtim);
metadataChangeTime_ = timespecToMSecs(statBuffer.st_ctim);
accessTime_ = timespecToMSecs(statBuffer.st_atim);
#else
modificationTime_ = qint64(statBuffer.st_mtime) * 1000;
metadataChangeTime_ = qint64(statBuffer.st_ctime) * 1000;
accessTime_ = qint64(statBuffer.st_atime) * 1000;
#endif
accessTime_ = GetFileTimes::atime(statBuffer, 0);
birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
userId_ = statBuffer.st_uid;
groupId_ = statBuffer.st_gid;
}

View File

@ -293,9 +293,12 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
#if defined(Q_OS_UNIX)
inline QDateTime QFileSystemMetaData::birthTime() const
{ return birthTime_ ? QDateTime::fromMSecsSinceEpoch(birthTime_) : QDateTime(); }
inline QDateTime QFileSystemMetaData::metadataChangeTime() const { return QDateTime::fromMSecsSinceEpoch(metadataChangeTime_); }
inline QDateTime QFileSystemMetaData::modificationTime() const { return QDateTime::fromMSecsSinceEpoch(modificationTime_); }
inline QDateTime QFileSystemMetaData::accessTime() const { return QDateTime::fromMSecsSinceEpoch(accessTime_); }
inline QDateTime QFileSystemMetaData::metadataChangeTime() const
{ return metadataChangeTime_ ? QDateTime::fromMSecsSinceEpoch(metadataChangeTime_) : QDateTime(); }
inline QDateTime QFileSystemMetaData::modificationTime() const
{ return modificationTime_ ? QDateTime::fromMSecsSinceEpoch(modificationTime_) : QDateTime(); }
inline QDateTime QFileSystemMetaData::accessTime() const
{ return accessTime_ ? QDateTime::fromMSecsSinceEpoch(accessTime_) : QDateTime(); }
inline uint QFileSystemMetaData::userId() const { return userId_; }
inline uint QFileSystemMetaData::groupId() const { return groupId_; }

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <QDateTime>
#include <QDebug>
#include <QCoreApplication>
#include <QFileInfo>
@ -40,12 +41,25 @@ static const char usage1[] =
"Usage: ";
static const char usage2[] =" [KEYWORD] [ARGUMENTS]\n\n"
"Keywords: ls FILES list file information\n"
" stat FILES print detailed file information\n"
" mv SOURCE TARGET rename files using QFile::rename\n"
" cp SOURCE TARGET copy files using QFile::copy\n"
" rm FILE remove file using QFile::remove\n"
" rmr DIR remove directory recursively\n"
" using QDir::removeRecursively\n";
std::ostream &operator<<(std::ostream &o, const QString &str)
{
return o << qPrintable(str);
}
std::ostream &operator<<(std::ostream &o, const QDateTime &dt)
{
if (dt.isValid())
return o << dt.toString(Qt::ISODateWithMs);
return o << '-';
}
static inline std::string permissions(QFile::Permissions permissions)
{
std::string result(10, '-');
@ -102,6 +116,44 @@ static int ls(int argCount, char **args)
return 0;
}
static int stat(int argCount, char **args)
{
for (int i = 0 ; i < argCount; ++i) {
const QFileInfo fi(QFile::decodeName(args[i]));
std::cout << "Name:\t" << fi.fileName() << std::endl;
std::cout << "Path:\t" << QDir::toNativeSeparators(fi.path())
<< " (" << QDir::toNativeSeparators(fi.absolutePath()) << ')' << std::endl;
std::cout << "Size:\t" << fi.size()
<< "\tType: "
<< (fi.isSymLink() && !fi.exists() ? "Broken symlink" :
!fi.exists() ? "Non-existent" :
fi.isSymLink() ? "Symlink to " : "")
<< (!fi.exists() ? "" :
fi.isFile() ? "Regular file" :
fi.isDir() ? "Directory" : "Special node")
<< std::endl;
if (fi.isSymLink())
std::cout << "Target:\t" << fi.symLinkTarget() << std::endl;
std::cout << "Attrs: "
<< (fi.isReadable() ? " readable" : "")
<< (fi.isWritable() ? " writable" : "")
<< (fi.isExecutable() ? " executable" : "")
<< (fi.isHidden() ? " hidden" : "")
<< (fi.isNativePath() ? " nativepath" : "")
<< (fi.isRoot() ? " root" : "")
<< (fi.isBundle() ? " bundle" : "")
<< std::endl;
std::cout << "Mode:\t" << permissions(fi) << std::endl;
std::cout << "Owner:\t" << fi.owner() << " (" << fi.ownerId()
<< ")\tGroup:\t" << fi.group() << " (" << fi.groupId() << ')' << std::endl;
std::cout << "Access:\t" << fi.lastRead() << std::endl;
std::cout << "Birth:\t" << fi.birthTime() << std::endl;
std::cout << "Change:\t" << fi.metadataChangeTime() << std::endl;
std::cout << "Modified: " << fi.lastModified() << std::endl;
}
return 0;
}
static int mv(const char *sourceFileName, const char *targetFileName)
{
QFile sourceFile(QString::fromLocal8Bit(sourceFileName));
@ -155,6 +207,9 @@ int main(int argc, char *argv[])
if (argc >= 3 && !qstrcmp(argv[1], "ls"))
return ls(argc -2, argv + 2);
if (argc >= 3 && !qstrcmp(argv[1], "stat"))
return stat(argc -2, argv + 2);
if (argc == 4 && !qstrcmp(argv[1], "mv"))
return mv(argv[2], argv[3]);