File indexing completed on 2024-04-21 05:48:31
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 "posixWalker.h" 0005 #include <QDebug> 0006 0007 #ifdef Q_OS_LINUX 0008 #include <sys/param.h> 0009 #endif 0010 0011 static void outputError(const QByteArray &path) 0012 { 0013 /// show error message that stat or opendir may give 0014 0015 #define out(s) \ 0016 qWarning() << s ": " << path; \ 0017 break 0018 0019 switch (errno) { 0020 case EACCES: 0021 out("Inadequate access permissions"); 0022 case EMFILE: 0023 out("Too many file descriptors in use by Filelight"); 0024 case ENFILE: 0025 out("Too many files are currently open in the system"); 0026 case ENOENT: 0027 out("A component of the path does not exist, or the path is an empty string"); 0028 case ENOMEM: 0029 out("Insufficient memory to complete the operation"); 0030 case ENOTDIR: 0031 out("A component of the path is not a folder"); 0032 case EBADF: 0033 out("Bad file descriptor"); 0034 case EFAULT: 0035 out("Bad address"); 0036 case ELOOP: // NOTE shouldn't ever happen 0037 out("Too many symbolic links encountered while traversing the path"); 0038 case ENAMETOOLONG: 0039 out("File name too long"); 0040 } 0041 0042 #undef out 0043 } 0044 0045 POSIXWalker::POSIXWalker(const QByteArray &path) 0046 : m_path(path) 0047 { 0048 if (path.isEmpty()) { 0049 return; 0050 } 0051 0052 m_dir = opendir(path.constData()); 0053 if (!m_dir) { 0054 outputError(QByteArray(path)); 0055 return; 0056 } 0057 0058 m_dirfd = dirfd(m_dir); 0059 // load first entry to achieve iterator behavior. If there are no entries then this results 0060 // in a default constructed m_entry and thus ==end(); otherwise it is the first m_entry ==begin(). 0061 next(); 0062 } 0063 0064 POSIXWalker::~POSIXWalker() 0065 { 0066 close(); 0067 } 0068 0069 void POSIXWalker::close() 0070 { 0071 if (m_dir) { 0072 closedir(m_dir); 0073 m_dir = nullptr; 0074 m_dirfd = -1; 0075 } 0076 } 0077 0078 void POSIXWalker::next() 0079 { 0080 while (true) { 0081 m_entry = {}; // reset 0082 0083 if (!m_dir) { 0084 return; 0085 } 0086 0087 dirent *ent = readdir(m_dir); 0088 if (!ent) { // end of dir 0089 close(); 0090 return; 0091 } 0092 0093 if (qstrcmp(ent->d_name, ".") == 0 || qstrcmp(ent->d_name, "..") == 0) { 0094 continue; 0095 } 0096 0097 m_entry.name = QByteArray(ent->d_name); 0098 if (fstatat(m_dirfd, ent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) == -1) { 0099 outputError(m_entry.name); 0100 return; 0101 } 0102 0103 m_entry.isSkipable = 0104 S_ISLNK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode) || S_ISSOCK(statbuf.st_mode); 0105 0106 auto links = statbuf.st_nlink; 0107 // Only count as hard link if it's not already being skipped 0108 if (links > 1 && !m_entry.isSkipable) { 0109 ino_t inode = statbuf.st_ino; 0110 // If we already counted this inode, skip it 0111 if (m_countedHardlinks.find(inode) != m_countedHardlinks.end()) { 0112 m_entry.isDuplicate = true; 0113 } else { 0114 // Only add to counted hard links if we are going to count it 0115 m_countedHardlinks.insert(inode); 0116 } 0117 } 0118 m_entry.isDir = S_ISDIR(statbuf.st_mode); 0119 m_entry.isFile = S_ISREG(statbuf.st_mode); 0120 0121 #ifdef Q_OS_LINUX 0122 m_entry.size = statbuf.st_blocks * DEV_BSIZE; 0123 #else 0124 m_entry.size = statbuf.st_blocks * S_BLKSIZE; 0125 #endif 0126 break; 0127 } 0128 }