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 }