File indexing completed on 2024-04-21 05:48:32

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0003 
0004 #include "windowsWalker.h"
0005 #include <QDebug>
0006 #include <QScopeGuard>
0007 #include <QString>
0008 
0009 WindowsWalker::WindowsWalker(const QByteArray &path)
0010     : m_path(path)
0011     , m_pathW(QString::fromUtf8(m_path).toStdWString())
0012 {
0013     if (path.isEmpty()) {
0014         return;
0015     }
0016 
0017     const std::wstring filespec = m_pathW + L"\\*";
0018     m_handle = FindFirstFileW(filespec.c_str(), &m_fileinfo);
0019     if (m_handle == INVALID_HANDLE_VALUE) {
0020         const DWORD errorCode = GetLastError();
0021         qWarning() << "Failed to open" << path << ':' << errorCode << GetLastErrorAsString(errorCode);
0022     } else {
0023         if (m_fileinfo.cFileName == std::wstring(L".") || m_fileinfo.cFileName == std::wstring(L"..")) {
0024             // stream past . and ..
0025             next();
0026         } else {
0027             updateEntry();
0028         }
0029     }
0030 }
0031 
0032 WindowsWalker::~WindowsWalker()
0033 {
0034     close();
0035 }
0036 
0037 void WindowsWalker::close()
0038 {
0039     if (m_handle != INVALID_HANDLE_VALUE) {
0040         FindClose(m_handle);
0041         m_handle = INVALID_HANDLE_VALUE;
0042     }
0043 }
0044 
0045 void WindowsWalker::next()
0046 {
0047     while (true) {
0048         m_entry = {}; // reset
0049 
0050         if (FindNextFileW(m_handle, &m_fileinfo) == 0) { // error
0051             const DWORD errorCode = GetLastError();
0052             if (errorCode == ERROR_NO_MORE_FILES) {
0053                 // qDebug() << "no more files";
0054                 close();
0055                 return;
0056             } else {
0057                 qWarning() << m_path << ':' << errorCode << GetLastErrorAsString(errorCode);
0058             }
0059         } else {
0060             if (m_fileinfo.cFileName == std::wstring(L".") || m_fileinfo.cFileName == std::wstring(L"..")) {
0061                 continue;
0062             }
0063         }
0064 
0065         updateEntry();
0066         return;
0067     }
0068 }
0069 
0070 QString WindowsWalker::GetLastErrorAsString(DWORD error)
0071 {
0072     Q_ASSERT(error != NO_ERROR && error != ERROR_SUCCESS);
0073 
0074     LPWSTR buffer = nullptr;
0075     auto freeBuffer = qScopeGuard([buffer] {
0076         LocalFree(buffer);
0077     });
0078 
0079     size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
0080                                  nullptr,
0081                                  error,
0082                                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
0083                                  (LPWSTR)&buffer,
0084                                  0,
0085                                  nullptr);
0086 
0087     return QString::fromWCharArray(buffer);
0088 }
0089 
0090 void WindowsWalker::updateEntry()
0091 {
0092     m_entry = {}; // reset
0093 
0094     const std::wstring name(m_fileinfo.cFileName);
0095     m_entry.name = QString::fromStdWString(name).toUtf8();
0096 
0097     auto new_path = m_pathW + L"/" + name;
0098 
0099     // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesexw
0100     WIN32_FILE_ATTRIBUTE_DATA fileAttributeData;
0101     BOOL result = GetFileAttributesExW(new_path.c_str(), GetFileExInfoStandard, (LPVOID)&fileAttributeData);
0102     if (result == 0) {
0103         qWarning() << "failed to get attributes for" << QString::fromStdWString(new_path);
0104         return;
0105     }
0106 
0107     const auto attributes = fileAttributeData.dwFileAttributes;
0108     // Reparse points are symlinks or NTFS junctions.
0109     m_entry.isSkipable = attributes & FILE_ATTRIBUTE_REPARSE_POINT || attributes & FILE_ATTRIBUTE_TEMPORARY;
0110     m_entry.isDir = attributes & FILE_ATTRIBUTE_DIRECTORY;
0111     m_entry.isFile = !m_entry.isSkipable && !m_entry.isDir; // fileness is implicit in win32 api
0112     ULARGE_INTEGER ulargeInt;
0113     if (attributes & FILE_ATTRIBUTE_COMPRESSED || attributes & FILE_ATTRIBUTE_SPARSE_FILE) {
0114         ulargeInt.HighPart = 0;
0115         ulargeInt.LowPart = GetCompressedFileSizeW(new_path.c_str(), &ulargeInt.HighPart);
0116         if (GetLastError() != ERROR_SUCCESS && ulargeInt.LowPart == INVALID_FILE_SIZE) {
0117             ulargeInt.HighPart = fileAttributeData.nFileSizeHigh;
0118             ulargeInt.LowPart = fileAttributeData.nFileSizeLow;
0119         }
0120     } else {
0121         ulargeInt.HighPart = fileAttributeData.nFileSizeHigh;
0122         ulargeInt.LowPart = fileAttributeData.nFileSizeLow;
0123     }
0124 
0125     m_entry.size = ulargeInt.QuadPart;
0126 }