File indexing completed on 2024-04-28 09:43:50
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 }