1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
// SPDX-FileCopyrightText: 2020 Simon Persson <simon.persson@mykolab.com>
//
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "fsexecutor.h"
#include "backupplan.h"
#include <QAction>
#include <QDir>
#include <QFileInfo>
#include <QTextStream>
#include <QTimer>
#include <KDirWatch>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
namespace
{
// very light check if a directory exists that works on automounts where QDir::exists fails
bool checkDirExists(const QDir &dir)
{
struct stat s;
return stat(dir.absolutePath().toLocal8Bit().data(), &s) == 0 && S_ISDIR(s.st_mode);
}
}
FSExecutor::FSExecutor(BackupPlan *pPlan, KupDaemon *pKupDaemon)
: PlanExecutor(pPlan, pKupDaemon)
{
mDestinationPath = QDir::cleanPath(mPlan->mFilesystemDestinationPath.toLocalFile());
mDirWatch = new KDirWatch(this);
connect(mDirWatch, SIGNAL(deleted(QString)), SLOT(checkStatus()));
mMountWatcher.start();
}
FSExecutor::~FSExecutor()
{
mMountWatcher.terminate();
mMountWatcher.wait();
}
void FSExecutor::checkStatus()
{
static bool lComingBackLater = false;
if (!mWatchedParentDir.isEmpty() && !lComingBackLater) {
// came here because something happened to a parent folder,
// come back in a few seconds, give a new mount some time before checking
// status of destination folder
QTimer::singleShot(5000, this, SLOT(checkStatus()));
lComingBackLater = true;
return;
}
lComingBackLater = false;
QDir lDir(mDestinationPath);
if (!lDir.exists()) {
// Destination doesn't exist, find nearest existing parent folder and
// watch that for dirty or deleted
if (mDirWatch->contains(mDestinationPath)) {
mDirWatch->removeDir(mDestinationPath);
}
QString lExisting = mDestinationPath;
do {
lExisting += QStringLiteral("/..");
lDir = QDir(QDir::cleanPath(lExisting));
} while (!checkDirExists(lDir));
lExisting = lDir.canonicalPath();
if (lExisting != mWatchedParentDir) { // new parent to watch
if (!mWatchedParentDir.isEmpty()) { // were already watching a parent
mDirWatch->removeDir(mWatchedParentDir);
} else { // start watching a parent
connect(mDirWatch, SIGNAL(dirty(QString)), SLOT(checkStatus()));
connect(&mMountWatcher, SIGNAL(mountsChanged()), SLOT(checkMountPoints()), Qt::QueuedConnection);
}
mWatchedParentDir = lExisting;
mDirWatch->addDir(mWatchedParentDir);
}
if (mState != NOT_AVAILABLE) {
enterNotAvailableState();
}
} else {
// Destination exists... only watch for delete
if (!mWatchedParentDir.isEmpty()) {
disconnect(mDirWatch, SIGNAL(dirty(QString)), this, SLOT(checkStatus()));
disconnect(&mMountWatcher, SIGNAL(mountsChanged()), this, SLOT(checkMountPoints()));
mDirWatch->removeDir(mWatchedParentDir);
mWatchedParentDir.clear();
}
mDirWatch->addDir(mDestinationPath);
QFileInfo lInfo(mDestinationPath);
if (lInfo.isWritable() && mState == NOT_AVAILABLE) {
enterAvailableState();
} else if (!lInfo.isWritable() && mState != NOT_AVAILABLE) {
enterNotAvailableState();
}
}
}
void FSExecutor::checkMountPoints()
{
QFile lMountsFile(QStringLiteral("/proc/mounts"));
if (!lMountsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
return;
}
// don't use atEnd() to detect when finished reading file, size of
// this special file is 0 but still returns data when read.
forever {
QByteArray lLine = lMountsFile.readLine();
if (lLine.isEmpty()) {
break;
}
QTextStream lTextStream(lLine);
QString lDevice, lMountPoint;
lTextStream >> lDevice >> lMountPoint;
if (lMountPoint == mWatchedParentDir) {
checkStatus();
}
}
}
void MountWatcher::run()
{
int lMountsFd = open("/proc/mounts", O_RDONLY);
fd_set lFdSet;
forever {
FD_ZERO(&lFdSet);
FD_SET(lMountsFd, &lFdSet);
if (select(lMountsFd + 1, nullptr, nullptr, &lFdSet, nullptr) > 0) {
emit mountsChanged();
}
}
}
|