File indexing completed on 2024-04-21 04:57:58

0001 /* This file is part of FSView.
0002     SPDX-FileCopyrightText: 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only
0005 */
0006 
0007 #include "scan.h"
0008 
0009 #include <QDir>
0010 #include <QStringList>
0011 #include <QSet>
0012 #include <qplatformdefs.h>
0013 
0014 #include <kauthorized.h>
0015 #include <kurlauthorized.h>
0016 
0017 #include "inode.h"
0018 #include "fsviewdebug.h"
0019 
0020 // ScanManager
0021 
0022 ScanManager::ScanManager()
0023 {
0024     _topDir = nullptr;
0025     _listener = nullptr;
0026 }
0027 
0028 ScanManager::ScanManager(const QString &path)
0029 {
0030     _topDir = nullptr;
0031     _listener = nullptr;
0032     setTop(path);
0033 }
0034 
0035 ScanManager::~ScanManager()
0036 {
0037     stopScan();
0038     delete _topDir;
0039 }
0040 
0041 void ScanManager::setListener(ScanListener *l)
0042 {
0043     _listener = l;
0044 }
0045 
0046 ScanDir *ScanManager::setTop(const QString &path, int data)
0047 {
0048     stopScan();
0049     if (_topDir) {
0050         delete _topDir;
0051         _topDir = nullptr;
0052     }
0053     if (!path.isEmpty()) {
0054         _topDir = new ScanDir(path, this, nullptr, data);
0055     }
0056     return _topDir;
0057 }
0058 
0059 bool ScanManager::scanRunning()
0060 {
0061     if (!_topDir) {
0062         return false;
0063     }
0064 
0065     return _topDir->scanRunning();
0066 }
0067 
0068 void ScanManager::startScan(ScanDir *from)
0069 {
0070     if (!_topDir) {
0071         return;
0072     }
0073     if (!from) {
0074         from = _topDir;
0075     }
0076 
0077     if (scanRunning()) {
0078         stopScan();
0079     }
0080 
0081     from->clear();
0082     if (from->parent()) {
0083         from->parent()->setupChildRescan();
0084     }
0085 
0086     _list.append(new ScanItem(from->path(), from));
0087 }
0088 
0089 void ScanManager::stopScan()
0090 {
0091     if (!_topDir) {
0092         return;
0093     }
0094 
0095     if (0) qCDebug(FSVIEWLOG) << "ScanManager::stopScan, scanLength "
0096                               << _list.count();
0097 
0098     while (!_list.isEmpty()) {
0099         ScanItem *si = _list.takeFirst();
0100         si->dir->finish();
0101         delete si;
0102     }
0103 }
0104 
0105 int ScanManager::scan(int data)
0106 {
0107     if (_list.isEmpty()) {
0108         return false;
0109     }
0110     ScanItem *si = _list.takeFirst();
0111 
0112     int newCount = si->dir->scan(si, _list, data);
0113     delete si;
0114 
0115     return newCount;
0116 }
0117 
0118 // ScanFile
0119 
0120 ScanFile::ScanFile()
0121 {
0122     _size = 0;
0123     _listener = nullptr;
0124 }
0125 
0126 ScanFile::ScanFile(const QString &n, KIO::fileoffset_t s)
0127 {
0128     _name = n;
0129     _size = s;
0130     _listener = nullptr;
0131 }
0132 
0133 ScanFile::~ScanFile()
0134 {
0135     if (_listener) {
0136         _listener->destroyed(this);
0137     }
0138 }
0139 
0140 // ScanDir
0141 
0142 ScanDir::ScanDir()
0143 {
0144     _dirty = true;
0145     _dirsFinished = -1; /* scan not started */
0146 
0147     _parent = nullptr;
0148     _manager = nullptr;
0149     _listener = nullptr;
0150     _data = 0;
0151 }
0152 
0153 ScanDir::ScanDir(const QString &n, ScanManager *m,
0154                  ScanDir *p, int data)
0155     : _name(n)
0156 {
0157     _dirty = true;
0158     _dirsFinished = -1; /* scan not started */
0159 
0160     _parent = p;
0161     _manager = m;
0162     _listener = nullptr;
0163     _data = data;
0164 }
0165 
0166 ScanDir::~ScanDir()
0167 {
0168     if (_listener) {
0169         _listener->destroyed(this);
0170     }
0171 }
0172 
0173 void ScanDir::setListener(ScanListener *l)
0174 {
0175     _listener = l;
0176 }
0177 
0178 QString ScanDir::path()
0179 {
0180     if (_parent) {
0181         QString p = _parent->path();
0182         if (!p.endsWith(QLatin1Char('/'))) {
0183             p += QLatin1Char('/');
0184         }
0185         return p + _name;
0186     }
0187 
0188     return _name;
0189 }
0190 
0191 void ScanDir::clear()
0192 {
0193     _dirty = true;
0194     _dirsFinished = -1; /* scan not started */
0195 
0196     _files.clear();
0197     _dirs.clear();
0198 }
0199 
0200 void ScanDir::update()
0201 {
0202     if (!_dirty) {
0203         return;
0204     }
0205     _dirty = false;
0206 
0207     _fileCount = 0;
0208     _dirCount = 0;
0209     _size = 0;
0210 
0211     if (_dirsFinished == -1) {
0212         return;
0213     }
0214 
0215     if (_files.count() > 0) {
0216         _fileCount += _files.count();
0217         _size = _fileSize;
0218     }
0219     if (_dirs.count() > 0) {
0220         _dirCount += _dirs.count();
0221         ScanDirVector::iterator it;
0222         for (it = _dirs.begin(); it != _dirs.end(); ++it) {
0223             (*it).update();
0224             _fileCount += (*it)._fileCount;
0225             _dirCount  += (*it)._dirCount;
0226             _size      += (*it)._size;
0227         }
0228     }
0229 }
0230 
0231 bool ScanDir::isForbiddenDir(QString &d)
0232 {
0233     static QSet<QString> *s = nullptr;
0234 
0235     if (!s) {
0236         s = new QSet<QString>;
0237         // directories without real files on Linux
0238         // TODO: should be OS specific
0239         s->insert(QStringLiteral("/proc"));
0240         s->insert(QStringLiteral("/dev"));
0241         s->insert(QStringLiteral("/sys"));
0242     }
0243     return (s->contains(d));
0244 }
0245 
0246 int ScanDir::scan(ScanItem *si, ScanItemList &list, int data)
0247 {
0248     clear();
0249     _dirsFinished = 0;
0250     _fileSize = 0;
0251     _dirty = true;
0252 
0253     if (isForbiddenDir(si->absPath)) {
0254         if (_parent) {
0255             _parent->subScanFinished();
0256         }
0257         return 0;
0258     }
0259 
0260     QUrl u = QUrl::fromLocalFile(si->absPath);
0261     if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), QUrl(), u)) {
0262         if (_parent) {
0263             _parent->subScanFinished();
0264         }
0265 
0266         return 0;
0267     }
0268 
0269     QDir d(si->absPath);
0270     const QStringList fileList = d.entryList(QDir::Files |
0271                                  QDir::Hidden | QDir::NoSymLinks);
0272 
0273     if (fileList.count() > 0) {
0274         QT_STATBUF buff;
0275 
0276         _files.reserve(fileList.count());
0277 
0278         QStringList::ConstIterator it;
0279         for (it = fileList.constBegin(); it != fileList.constEnd(); ++it) {
0280             QString tmp(si->absPath + QLatin1Char('/') + (*it));
0281             if (QT_LSTAT(tmp.toStdString().c_str(), &buff) != 0) {
0282                 continue;
0283             }
0284             _files.append(ScanFile(*it, buff.st_size));
0285             _fileSize += buff.st_size;
0286         }
0287     }
0288 
0289     const QStringList dirList = d.entryList(QDir::Dirs |
0290                                             QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
0291 
0292     if (dirList.count() > 0) {
0293         _dirs.reserve(dirList.count());
0294 
0295         QStringList::ConstIterator it;
0296         for (it = dirList.constBegin(); it != dirList.constEnd(); ++it) {
0297             _dirs.append(ScanDir(*it, _manager, this, data));
0298             QString newpath = si->absPath;
0299             if (!newpath.endsWith(QChar('/'))) {
0300                 newpath.append("/");
0301             }
0302             newpath.append(*it);
0303             list.append(new ScanItem(newpath, &(_dirs.last())));
0304         }
0305         _dirCount += _dirs.count();
0306     }
0307 
0308     callScanStarted();
0309     callSizeChanged();
0310 
0311     if (_dirs.count() == 0) {
0312         callScanFinished();
0313 
0314         if (_parent) {
0315             _parent->subScanFinished();
0316         }
0317     }
0318 
0319     return _dirs.count();
0320 }
0321 
0322 void ScanDir::subScanFinished()
0323 {
0324     _dirsFinished++;
0325     callSizeChanged();
0326 
0327     if (0) qCDebug(FSVIEWLOG) << "ScanDir::subScanFinished [" << path()
0328                               << "]: " << _dirsFinished << "/" << _dirs.count();
0329 
0330     if (_dirsFinished < _dirs.count()) {
0331         return;
0332     }
0333 
0334     /* all subdirs read */
0335     callScanFinished();
0336 
0337     if (_parent) {
0338         _parent->subScanFinished();
0339     }
0340 }
0341 
0342 void ScanDir::finish()
0343 {
0344     if (scanRunning()) {
0345         _dirsFinished = _dirs.count();
0346         callScanFinished();
0347     }
0348 
0349     if (_parent) {
0350         _parent->finish();
0351     }
0352 }
0353 
0354 void ScanDir::setupChildRescan()
0355 {
0356     if (_dirs.count() == 0) {
0357         return;
0358     }
0359 
0360     _dirsFinished = 0;
0361     ScanDirVector::iterator it;
0362     for (it = _dirs.begin(); it != _dirs.end(); ++it)
0363         if ((*it).scanFinished()) {
0364             _dirsFinished++;
0365         }
0366 
0367     if (_parent &&
0368             (_dirsFinished < _dirs.count())) {
0369         _parent->setupChildRescan();
0370     }
0371 
0372     callScanStarted();
0373 }
0374 
0375 void ScanDir::callScanStarted()
0376 {
0377     if (0) qCDebug(FSVIEWLOG) << "ScanDir:Started [" << path()
0378                               << "]: size " << size() << ", files " << fileCount();
0379 
0380     ScanListener *mListener = _manager ? _manager->listener() : nullptr;
0381 
0382     if (_listener) {
0383         _listener->scanStarted(this);
0384     }
0385     if (mListener) {
0386         mListener->scanStarted(this);
0387     }
0388 }
0389 
0390 void ScanDir::callSizeChanged()
0391 {
0392     if (0) qCDebug(FSVIEWLOG) << ". [" << path()
0393                               << "]: size " << size() << ", files " << fileCount();
0394 
0395     _dirty = true;
0396 
0397     if (_parent) {
0398         _parent->callSizeChanged();
0399     }
0400 
0401     ScanListener *mListener = _manager ? _manager->listener() : nullptr;
0402 
0403     if (_listener) {
0404         _listener->sizeChanged(this);
0405     }
0406     if (mListener) {
0407         mListener->sizeChanged(this);
0408     }
0409 }
0410 
0411 void ScanDir::callScanFinished()
0412 {
0413     if (0) qCDebug(FSVIEWLOG) << "ScanDir:Finished [" << path()
0414                               << "]: size " << size() << ", files " << fileCount();
0415 
0416     ScanListener *mListener = _manager ? _manager->listener() : nullptr;
0417 
0418     if (_listener) {
0419         _listener->scanFinished(this);
0420     }
0421     if (mListener) {
0422         mListener->scanFinished(this);
0423     }
0424 }
0425