diff --git a/src/ioslaves/file/file.cpp b/src/ioslaves/file/file.cpp --- a/src/ioslaves/file/file.cpp +++ b/src/ioslaves/file/file.cpp @@ -65,6 +65,13 @@ #include #include +#ifdef Q_OS_LINUX +#include +#include +#include +#include +#endif + #if HAVE_VOLMGT #include #include @@ -838,12 +845,119 @@ return *it; } +#ifdef STATX_BASIC_STATS +// statx syscall is available + bool FileProtocol::createUDSEntry(const QString &filename, const QByteArray &path, UDSEntry &entry, short int details) { assert(entry.count() == 0); // by contract :-) entry.reserve(8); + entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); + + mode_t type; + mode_t access; + bool isBrokenSymLink = false; + long long size = 0LL; +#if HAVE_POSIX_ACL + QByteArray targetPath = path; +#endif + struct statx buff; + + if (statx(AT_FDCWD, path.data(), 0, STATX_BASIC_STATS | STATX_BTIME, &buff) == 0) { + if (details > 2) { + entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, buff.stx_dev_major); + entry.fastInsert(KIO::UDSEntry::UDS_INODE, buff.stx_ino); + } + + if ((buff.stx_mode & QT_STAT_MASK) == QT_STAT_LNK) { + + // Use readlink on Unix because symLinkTarget turns relative targets into absolute (#352927) + const __uint64_t lowerLimit = 1; + const __uint64_t upperLimit = 1024; + size_t bufferSize = qBound(lowerLimit, buff.stx_size, upperLimit); + QByteArray linkTargetBuffer; + linkTargetBuffer.resize(bufferSize); + while (true) { + ssize_t n = readlink(path.constData(), linkTargetBuffer.data(), bufferSize); + if (n < 0 && errno != ERANGE) { + qCWarning(KIO_FILE) << "readlink failed!" << path; + return false; + } else if (n > 0 && static_cast(n) != bufferSize) { + linkTargetBuffer.truncate(n); + break; + } + bufferSize *= 2; + linkTargetBuffer.resize(bufferSize); + } + const QString linkTarget = QFile::decodeName(linkTargetBuffer); + entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkTarget); + // A symlink -> follow it only if details>1 + if (details > 1) { + if (statx(AT_FDCWD, path.constData(), 0, STATX_BASIC_STATS | STATX_BTIME, &buff) == -1) { + isBrokenSymLink = true; + } else { +#if HAVE_POSIX_ACL + // valid symlink, will get the ACLs of the destination + targetPath = linkTargetBuffer; +#endif + } + } + } + } else { + // qCWarning(KIO_FILE) << "statx didn't work on " << path.data(); + return false; + } + + if (isBrokenSymLink) { + // It is a link pointing to nowhere + type = S_IFMT - 1; + access = S_IRWXU | S_IRWXG | S_IRWXO; + size = 0LL; + } else { + type = buff.stx_mode & S_IFMT; // extract file type + access = buff.stx_mode & 07777; // extract permissions + size = buff.stx_size; + } + + entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type); + entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access); + entry.fastInsert(KIO::UDSEntry::UDS_SIZE, size); + +#if HAVE_POSIX_ACL + if (details > 1) { + /* Append an atom indicating whether the file has extended acl information + * and if withACL is specified also one with the acl itself. If it's a directory + * and it has a default ACL, also append that. */ + appendACLAtoms(targetPath, entry, type); + } +#endif + + if (details > 0) { + entry.fastInsert(KIO::UDSEntry::UDS_USER, getUserName(KUserId(buff.stx_uid))); + entry.fastInsert(KIO::UDSEntry::UDS_GROUP, getGroupName(KGroupId(buff.stx_gid))); + + entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.stx_mtime.tv_sec); + entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.stx_atime.tv_sec); + entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, buff.stx_btime.tv_sec); + } + + // Note: buff.st_ctime isn't the creation time ! + // We made that mistake for KDE 2.0, but it's in fact the + // "file status" change time, which we don't care about. + // For FreeBSD and NetBSD, use st_birthtime. For OpenBSD, + // use __st_birthtime. + + return true; +} +#else + +bool FileProtocol::createUDSEntry(const QString &filename, const QByteArray &path, UDSEntry &entry, + short int details) +{ + assert(entry.count() == 0); // by contract :-) + entry.reserve(8); entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); mode_t type; @@ -964,6 +1078,7 @@ return true; } +#endif void FileProtocol::special(const QByteArray &data) { diff --git a/tests/kioslavetest.cpp b/tests/kioslavetest.cpp --- a/tests/kioslavetest.cpp +++ b/tests/kioslavetest.cpp @@ -347,6 +347,8 @@ // It's rather rare to iterate that way, usually you'd use numberValue/stringValue directly. // This is just to print out all that we got + QDateTime timestamp; + const QVector keys = entry.fields(); QVector::const_iterator it = keys.begin(); for (; it != keys.end(); ++it) { @@ -384,6 +386,18 @@ case KIO::UDSEntry::UDS_SIZE: qDebug() << "Size: " << KIO::convertSize(entry.numberValue(*it)); break; + case KIO::UDSEntry::UDS_CREATION_TIME: + timestamp = QDateTime::fromSecsSinceEpoch(entry.numberValue(*it)); + qDebug() << "CreationTime: " << timestamp.toString(Qt::SystemLocaleShortDate); + break; + case KIO::UDSEntry::UDS_MODIFICATION_TIME: + timestamp = QDateTime::fromSecsSinceEpoch(entry.numberValue(*it)); + qDebug() << "ModificationTime: " << timestamp.toString(Qt::SystemLocaleShortDate); + break; + case KIO::UDSEntry::UDS_ACCESS_TIME: + timestamp = QDateTime::fromSecsSinceEpoch(entry.numberValue(*it)); + qDebug() << "AccessTime: " << timestamp.toString(Qt::SystemLocaleShortDate); + break; } } }