]> gitweb.hamatoma.de Git - reqt/commitdiff
rebackgui: implentation of "clean"
authorhama <hama@siduction.net>
Sun, 7 Feb 2016 21:58:58 +0000 (22:58 +0100)
committerhama <hama@siduction.net>
Sun, 7 Feb 2016 21:58:58 +0000 (22:58 +0100)
appl/rebackgui/BackupEngine.cpp
appl/rebackgui/BackupEngine.hpp
appl/rebackgui/mainwindow.cpp
appl/rebackgui/mainwindow.hpp
appl/rebackgui/mainwindow.ui
appl/rebackgui/osconnect.pl

index 27aae20addb04167981fbacefc5e3fc5e256510b..dbab03f611314644b9574408420f10d2138cc2db 100644 (file)
@@ -21,6 +21,7 @@ bool BackupEngine::m_searchReady = false;
 QMutex BackupEngine::m_mutex;
 QChar BackupEngine::m_separator = '\t';
 QString BackupEngine::m_separatorString = "\t";
+
 QStringList ChecksumTask::m_checksumInfo;
 bool ChecksumTask::m_sourceProcessingReady = false;
 
@@ -39,6 +40,8 @@ BackupEngine::BackupEngine(const QString& name,
        m_sourceDirs(sourceDirs),
        m_targetBaseDir(targetDir),
        m_targetDirs(),
+       m_shadowBaseDir(),
+       m_shadowDirs(),
        m_mainWindow(mainWindow),
        m_name(name)
 {
@@ -65,6 +68,33 @@ bool BackupEngine::error(const QString& message){
    return false;
 }
 
+/**
+ * Initializes the shadow directories.
+ *
+ * All not too old superflous files will be moved to the shadow directory.
+ * Note: the length of the shadow directory is equal to
+ * the length of the target dir: Otherwise full path length could be too long.
+ */
+void BackupEngine::initializeShadowDir(){
+       if (m_shadowBaseDir.isEmpty()){
+               int fullLength = m_targetBaseDir.length();
+               QString node = ReFileUtils::nodeOf(m_targetBaseDir);
+               int nodeLength = node.length();
+               if (nodeLength <= 2)
+                       m_shadowBaseDir = ReFileUtils::parentOf(m_targetBaseDir) + "$";
+               else {
+                       int middle = nodeLength / 2;
+                       m_shadowBaseDir = "." + m_targetBaseDir;
+                       m_shadowBaseDir.remove(fullLength - middle, 1);
+               }
+       }
+       m_shadowDirs.clear();
+       for (int ix = 0; ix < m_sourceDirs.size(); ix++){
+               m_shadowDirs.append(m_shadowBaseDir + OS_SEPARATOR_STR
+                                                + ReFileUtils::nodeOf(m_sourceDirs.at(ix)));
+       }
+}
+
 /**
  * @brief Logs a message.
  *
@@ -78,83 +108,32 @@ bool BackupEngine::log(const QString& message){
 }
 
 /**
- * Constructor.
+ * Inserts a remove command into queue for too old files in the shadow.
  *
- * @param compareWithTarget    <code>false</code>: all files will be found<br>
- *                                                     otherwise: only not existing or newer files will
- *                                                     be found
- * @param name                 name of the task
- * @param filePatterns only files which match the patterns will be found
- * @param dirPatterns  only subdirectories which matches the patterns will be entered
- * @param source               the list of base directories to search
- * @param targetDir            the base target directory
- * @param mainWindow   the GUI module, the parent
- */
-SearchTask::SearchTask(bool compareWithTarget, const QString& name,
-               const QString& filePatterns, const QString& dirPatterns,
-               const QStringList& sourceDirs, const QString& targetDir,
-               MainWindow* mainWindow) :
-       BackupEngine(name, sourceDirs, targetDir, mainWindow),
-       m_fileMatcher(filePatterns),
-       m_dirMatcher(dirPatterns),
-       m_compareWithTarget(compareWithTarget)
-{
-}
-
-/**
- * Runs the task.
- */
-void SearchTask::run()
-{
-       qint64 start = QDateTime::currentMSecsSinceEpoch();
-       m_searchReady = false;
-       QString targetDir, sourceDir;
-       for (int ix = 0; ix < m_sourceDirs.size(); ix++){
-               sourceDir = m_sourceDirs.at(ix);
-               if (m_compareWithTarget)
-                       targetDir = m_targetDirs.at(ix) + ReFileUtils::nodeOf(sourceDir);
-               searchOneDirectory(sourceDir, targetDir, ix);
-       }
-       m_searchReady = true;
-       m_mainWindow->externalLog(tr(
-               "Search finished: to process: %1 with %2 matching: %3 total: %4 "
-               "subdirs: %5 runtime: %6")
-               .arg(m_hotFiles)
-               .arg(ReQStringUtils::readableSize(m_hotBytes))
-               .arg(m_matchedFiles).arg(m_totalFiles)
-                 .arg(m_totalDirs)
-                 .arg(ReQStringUtils::readableDuration(
-                                  QDateTime::currentMSecsSinceEpoch() - start)));
-}
-/**
- * Search the files to backup and write it to a list.
+ * Note: this method is recursive.
  *
- * This method is recursive.
- *
- * @param source       the directory to inspect
- * @param target       "": no target directory exists<br>
- *                                     otherwise: only files will be written if the file found in
- *                                     the source directory does not exists in this target or
- *                                     it is newer or has another size
- * @param index                the index of the base directory in m_sourceDirs
+ * @param directory    directory to inspect
+ * @param maxAge       all files older than this time point will be deleted
+ * @param index                the index in the shadow directories
  */
-void SearchTask::searchOneDirectory(const QString& source,
-                                                                        const QString& target, int index){
-       QDirIterator it(source);
+void BackupEngine::removeOlder(const QString& directory, const QDateTime& time,
+                                       int index){
+       QDirIterator it(directory);
        QString info, node;
-       int lengthBase = m_sourceDirs.at(index).length();
+       int lengthBase = m_shadowDirs.at(index).length();
        assert(index < 0x7fff);
        QString prefix;
        m_mutex.lock();
        m_totalDirs++;
        m_mutex.unlock();
-       if (source.length() > lengthBase){
-        prefix = QChar(1 + index)
-                + ReFileUtils::nativePath(source.mid(lengthBase)) + OS_SEPARATOR_STR
+       if (directory.length() > lengthBase){
+               prefix = QChar(1 + index)
+                               + ReFileUtils::nativePath(directory.mid(lengthBase)) + OS_SEPARATOR_STR
                                + m_separator;
        } else {
                prefix = QChar(1 + index) + m_separatorString;
        }
+       bool isEmpty = true;
        while (it.hasNext()){
           if (m_shouldStop){
                  break;
@@ -162,70 +141,29 @@ void SearchTask::searchOneDirectory(const QString& source,
           it.next();
           node = it.fileName();
           if (it.fileInfo().isDir()){
-                  // nothing to do
-          } else if (! m_fileMatcher.matches(node)){
-                  m_mutex.lock();
-                  m_totalFiles++;
-                  m_mutex.unlock();
-          } else {
-                  qint64 diff = 0;
-                  bool doCopy = false;
-                  if (! m_compareWithTarget)
-                          doCopy = true;
-                  else if (target.isEmpty())
-                          doCopy = true;
-                  else {
-                          QFileInfo trg(target + it.fileName());
-                          if (! trg.exists())
-                                  doCopy = true;
-                          else {
-                                  const QFileInfo src = it.fileInfo();
-                                  if (trg.size() != src.size())
-                                          doCopy = true;
-                                  else if ((diff = src.lastModified().toMSecsSinceEpoch()
-                                                  - trg.lastModified().toMSecsSinceEpoch()) > 2000)
-                                          doCopy = true;
-                          }
-                  }
-                  if (doCopy){
-                          info = prefix + it.fileName();
+                  if (node != "." && node != ".."){
+                          removeOlder(it.filePath(), time, index);
+                          isEmpty = false;
                   }
+          } else if (it.fileInfo().lastModified() < time){
+                  isEmpty = false;
+                  info = prefix + QChar(CmdRemove) + it.fileName();
                   m_mutex.lock();
-                  if (doCopy){
-                          m_files.append(info);
-                          m_hotFiles++;
-                          m_hotBytes += it.fileInfo().size();
-                  }
+                  m_files.append(info);
+                  m_hotFiles++;
+                  m_hotBytes += it.fileInfo().size();
                   m_totalFiles++;
-                  m_matchedFiles++;
                   m_mutex.unlock();
           }
        }
-       if (! m_shouldStop){
-               QDirIterator it2(source);
-               QString node;
-               QString subTarget;
-               while (it2.hasNext()){
-                       if (m_shouldStop){
-                          break;
-                       }
-                  it2.next();
-
-                  if (it2.fileInfo().isDir() && (node = it2.fileName()) != "." && node != ".."
-                                  && m_dirMatcher.matches(node)){
-                          if (target.isEmpty())
-                                  subTarget.clear();
-                          else{
-                                  subTarget = target + it2.fileName();
-                                  if (! ReFileUtils::isDirectory(subTarget))
-                                          subTarget.clear();
-                                  else
-                                          subTarget += OS_SEPARATOR_STR;
-                          }
-                          searchOneDirectory(it2.filePath(), subTarget, index);
-                  }
-               }
+       if (isEmpty){
+               info = prefix + QChar(CmdRemoveDir) + it.fileName();
+               m_mutex.lock();
+               m_files.append(info);
+               m_matchedFiles++;
+               m_mutex.unlock();
        }
+
 }
 
 /**
@@ -317,7 +255,7 @@ void BackupTask::run()
                        node = info.mid(pos + 1);
                        copyFile(index, relPath, node);
                        qint64 now = QDateTime::currentMSecsSinceEpoch();
-            qint64 estimated = qint64(double(now - start) /  max(1LL, m_processedBytes) * m_hotBytes);
+                       qint64 estimated = qint64(double(now - start) /  max(1LL, m_processedBytes) * m_hotBytes);
                        m_mainWindow->externalAppend(ReGuiQueueItem::StatusLine, NULL,
                                        tr("%1 of %2 (%3 of %4) %5 MB/sec Remaining: %6 of %7")
                                        .arg(m_processedFiles).arg(m_hotFiles)
@@ -507,26 +445,28 @@ void ChecksumOfTargetTask::run()
                                        error(QObject::tr("checksum differs: ") + relPath + node
                                                  + " [" + sourceChecksum + "/" + checksum + "]");
                        }
-            qint64 processedBytes, hotBytes;
-            int hotFiles, processedFiles;
-            m_mutex.lock();
-            hotFiles = m_hotFiles;
-            hotBytes = m_hotBytes;
-            processedBytes = m_processedBytes;
-            processedFiles = m_processedFiles;
-            m_mutex.unlock();
-            now = QDateTime::currentMSecsSinceEpoch();
-            qint64 duration = (now - start);
-            qint64 estimated = estimated = qint64(double(now - start) /  max(1LL, m_processedBytes) * m_hotBytes * 2);
-            qint64 rest = estimated - duration;
+                       qint64 processedBytes, hotBytes;
+                       int hotFiles, processedFiles;
+                       m_mutex.lock();
+                       hotFiles = m_hotFiles;
+                       hotBytes = m_hotBytes;
+                       processedBytes = m_processedBytes;
+                       processedFiles = m_processedFiles;
+                       m_mutex.unlock();
+                       now = QDateTime::currentMSecsSinceEpoch();
+                       double duration = (now - start);
+                       double estimated = qint64(double(now - start));
+                       estimated /= max(1LL, m_processedBytes);
+                       estimated *= m_hotBytes * 2;
+                       qint64 rest = qint64(estimated - duration);
                        m_mainWindow->externalAppend(ReGuiQueueItem::StatusLine, NULL,
                                        tr("%1 of %2 (%3 of %4) %5 MB/sec Remaining: %6 of %7")
-                    .arg(m_processedFiles).arg(hotFiles * 2)
-                                         .arg(ReQStringUtils::readableSize(processedBytes))
-                                         .arg(ReQStringUtils::readableSize(hotBytes * 2))
-                                         .arg(processedBytes / 1024.0 / 1024 / duration * 1000.0, 0, 'f', 3)
-                                         .arg(ReQStringUtils::readableDuration(rest))
-                                                                                .arg(ReQStringUtils::readableDuration(estimated)));
+                                       .arg(processedFiles).arg(hotFiles * 2)
+                                                                                .arg(ReQStringUtils::readableSize(processedBytes))
+                                                                                .arg(ReQStringUtils::readableSize(hotBytes * 2))
+                                                                                .arg(processedBytes / 1024.0 / 1024 / duration * 1000.0, 0, 'f', 3)
+                                                                                .arg(ReQStringUtils::readableDuration(rest))
+                                                                                .arg(ReQStringUtils::readableDuration(qint64(estimated))));
 
                }
        }
@@ -537,4 +477,388 @@ void ChecksumOfTargetTask::run()
                                                                           .arg(m_mainWindow->errors()));
 }
 
+/**
+  Constructor.
+
+ * @param name                 name of the task
+ * @param sourceDirs   the list of source directories to inspect
+ * @param targetDir            the base of the target directories
+ * @param maxAgeSec            superflous file which is older will be moved to the
+ *                                             shadow, the files which are younger will be deleted
+ * @param mainWindow   the GUI module
+ */
+
+CleanTask::CleanTask(const QString& name, const QStringList& sourceDirs,
+                                        const QString& targetDir, MainWindow* mainWindow) :
+       BackupEngine(name, sourceDirs, targetDir, mainWindow)
+{
+
+}
+
+/**
+ * Runs the task.
+ */
+void CleanTask::run()
+{
+       QString relPath, node;
+       QString info;
+       qint64 start = QDateTime::currentMSecsSinceEpoch();
+       while (true){
+               m_mutex.lock();
+               if (m_files.size() == 0)
+                       info.clear();
+               else{
+                       info = m_files.first();
+                       m_files.removeFirst();
+               }
+               m_processedFiles++;
+               m_mutex.unlock();
+               if (info.isEmpty()){
+                       if (m_searchReady)
+                               break;
+                       else
+                               QThread::msleep(50);
+               } else {
+                       int index = int(info.at(0).unicode()) - 1;
+                       int pos = info.indexOf(m_separator, 1);
+                       if (pos == 1)
+                               relPath.clear();
+                       else
+                               relPath = info.mid(1, pos - 1);
+                       Command command = (Command) info.at(pos + 1).unicode();
+                       node = info.mid(pos + 2);
+                       switch(command){
+                       case CmdRemove:
+                               m_mainWindow->addToFileList("-" + relPath);
+                               break;
+                       case CmdRemoveDir:
+                               m_mainWindow->addToFileList("/" + relPath);
+                               break;
+                       case CmdMove:
+                       {
+                               QString target = m_targetDirs.at(index) + relPath + node;
+                               QString shadowDir = m_shadowDirs.at(index) + relPath;
+                               QString shadow = shadowDir + node;
+                               if (! ReFileUtils::makeDirWithParents(shadowDir))
+                                       error(QObject::tr("cannot create the shadow directory: %1")
+                                                 .arg(shadowDir));
+                               m_mainWindow->addToFileList(">" + target + " -> " + shadow);
+                               break;
+                       }
+                       default:
+                               error("unknown command: " + QString::number(command));
+                               break;
+                       }
+                       qint64 now = QDateTime::currentMSecsSinceEpoch();
+                       qint64 estimated = qint64(double(now - start) /  max(1, m_processedFiles) * m_hotFiles);
+                       m_mainWindow->externalAppend(ReGuiQueueItem::StatusLine, NULL,
+                                       tr("%1 of %2 (%3 of %4) %5 MB/sec Remaining: %6 of %7")
+                                       .arg(m_processedFiles).arg(m_hotFiles)
+                                                                                .arg(ReQStringUtils::readableSize(m_processedBytes))
+                                                                                .arg(ReQStringUtils::readableSize(m_hotBytes))
+                                                                                .arg(m_processedBytes / 1024.0 / 1024 / (now - start) * 1000, 0, 'f', 3)
+                                                                                .arg(ReQStringUtils::readableDuration(estimated - (now - start)))
+                                                                                .arg(ReQStringUtils::readableDuration(estimated)));
+               }
+       }
+       m_mainWindow->externalTaskFinished(tr("backup complete after %1. Errors: %2")
+                               .arg(ReQStringUtils::readableDuration(
+                                       QDateTime::currentMSecsSinceEpoch() - start))
+                                                                          .arg(m_mainWindow->errors()));
+}
+
+
+/**
+ * Constructor.
+ *
+ * @param compareWithTarget    <code>false</code>: all files will be found<br>
+ *                                                     otherwise: only not existing or newer files will
+ *                                                     be found
+ * @param name                 name of the task
+ * @param filePatterns only files which match the patterns will be found
+ * @param dirPatterns  only subdirectories which matches the patterns will be entered
+ * @param source               the list of base directories to search
+ * @param targetDir            the base target directory
+ * @param mainWindow   the GUI module, the parent
+ */
+SearchTask::SearchTask(bool compareWithTarget, const QString& name,
+               const QString& filePatterns, const QString& dirPatterns,
+               const QStringList& sourceDirs, const QString& targetDir,
+               MainWindow* mainWindow) :
+       BackupEngine(name, sourceDirs, targetDir, mainWindow),
+       m_fileMatcher(filePatterns),
+       m_dirMatcher(dirPatterns),
+       m_compareWithTarget(compareWithTarget)
+{
+}
+
+/**
+ * Runs the task.
+ */
+void SearchTask::run()
+{
+       qint64 start = QDateTime::currentMSecsSinceEpoch();
+       m_searchReady = false;
+       QString targetDir, sourceDir;
+       for (int ix = 0; ix < m_sourceDirs.size(); ix++){
+               sourceDir = m_sourceDirs.at(ix);
+               if (m_compareWithTarget)
+                       targetDir = m_targetDirs.at(ix) + ReFileUtils::nodeOf(sourceDir);
+               searchOneDirectory(sourceDir, targetDir, ix);
+       }
+       m_searchReady = true;
+       m_mainWindow->externalLog(tr(
+               "Search finished: to process: %1 with %2 matching: %3 total: %4 "
+               "subdirs: %5 runtime: %6")
+               .arg(m_hotFiles)
+               .arg(ReQStringUtils::readableSize(m_hotBytes))
+               .arg(m_matchedFiles).arg(m_totalFiles)
+                 .arg(m_totalDirs)
+                 .arg(ReQStringUtils::readableDuration(
+                                  QDateTime::currentMSecsSinceEpoch() - start)));
+}
+/**
+ * Search the files to backup and write it to a list.
+ *
+ * This method is recursive.
+ *
+ * @param source       the directory to inspect
+ * @param target       "": no target directory exists<br>
+ *                                     otherwise: only files will be written if the file found in
+ *                                     the source directory does not exists in this target or
+ *                                     it is newer or has another size
+ * @param index                the index of the base directory in m_sourceDirs
+ */
+void SearchTask::searchOneDirectory(const QString& source,
+                                                                        const QString& target, int index){
+       QDirIterator it(source);
+       QString info, node;
+       int lengthBase = m_sourceDirs.at(index).length();
+       assert(index < 0x7fff);
+       QString prefix;
+       m_mutex.lock();
+       m_totalDirs++;
+       m_mutex.unlock();
+       if (source.length() > lengthBase){
+               prefix = QChar(1 + index)
+                               + ReFileUtils::nativePath(source.mid(lengthBase)) + OS_SEPARATOR_STR
+                               + m_separator;
+       } else {
+               prefix = QChar(1 + index) + m_separatorString;
+       }
+       while (it.hasNext()){
+          if (m_shouldStop){
+                 break;
+          }
+          it.next();
+          node = it.fileName();
+          if (it.fileInfo().isDir()){
+                  // nothing to do
+          } else if (! m_fileMatcher.matches(node)){
+                  m_mutex.lock();
+                  m_totalFiles++;
+                  m_mutex.unlock();
+          } else {
+                  qint64 diff = 0;
+                  bool doCopy = false;
+                  if (! m_compareWithTarget)
+                          doCopy = true;
+                  else if (target.isEmpty())
+                          doCopy = true;
+                  else {
+                          QFileInfo trg(target + it.fileName());
+                          if (! trg.exists())
+                                  doCopy = true;
+                          else {
+                                  const QFileInfo src = it.fileInfo();
+                                  if (trg.size() != src.size())
+                                          doCopy = true;
+                                  else if ((diff = src.lastModified().toMSecsSinceEpoch()
+                                                  - trg.lastModified().toMSecsSinceEpoch()) > 2000)
+                                          doCopy = true;
+                          }
+                  }
+                  if (doCopy){
+                          info = prefix + it.fileName();
+                  }
+                  m_mutex.lock();
+                  if (doCopy){
+                          m_files.append(info);
+                          m_hotFiles++;
+                          m_hotBytes += it.fileInfo().size();
+                  }
+                  m_totalFiles++;
+                  m_matchedFiles++;
+                  m_mutex.unlock();
+          }
+       }
+       if (! m_shouldStop){
+               QDirIterator it2(source);
+               QString node;
+               QString subTarget;
+               while (it2.hasNext()){
+                       if (m_shouldStop){
+                          break;
+                       }
+                  it2.next();
+
+                  if (it2.fileInfo().isDir() && (node = it2.fileName()) != "." && node != ".."
+                                  && m_dirMatcher.matches(node)){
+                          if (target.isEmpty())
+                                  subTarget.clear();
+                          else{
+                                  subTarget = target + it2.fileName();
+                                  if (! ReFileUtils::isDirectory(subTarget))
+                                          subTarget.clear();
+                                  else
+                                          subTarget += OS_SEPARATOR_STR;
+                          }
+                          searchOneDirectory(it2.filePath(), subTarget, index);
+                  }
+               }
+       }
+}
+
+/**
+  Constructor.
+
+ * @param name                 name of the task
+ * @param sourceDirs   the list of source directories to inspect
+ * @param targetDir            the base of the target directories
+ * @param maxAgeSec            superflous file which is older will be moved to the
+ *                                             shadow, the files which are younger will be deleted
+ * @param mainWindow   the GUI module
+ */
+
+SearchTargetTask::SearchTargetTask(const QString& name, const QStringList& sourceDirs,
+                                        const QString& targetDir, qint64 maxAgeSec, MainWindow* mainWindow) :
+       BackupEngine(name, sourceDirs, targetDir, mainWindow),
+       m_maxAge()
+{
+       m_maxAge.setMSecsSinceEpoch(maxAgeSec * 1000);
+       initializeShadowDir();
+}
+
+/**
+ * Search the files to clean.
+ *
+ * This method is recursive.
+ *
+ * @param target       the directory to inspect
+ * @param source       the source directory to compare
+ * @param index                the index of the base directory in m_targetDirs
+ */
+void SearchTargetTask::searchOneDirectory(const QString& target,
+                                                                        const QString& source, int index){
+       QDirIterator it(target);
+       QString info, node;
+       int lengthBase = m_targetDirs.at(index).length();
+       assert(index < 0x7fff);
+       QString prefix;
+       m_mutex.lock();
+       m_totalDirs++;
+       m_mutex.unlock();
+       if (source.length() > lengthBase){
+               prefix = QChar(1 + index)
+                               + ReFileUtils::nativePath(source.mid(lengthBase)) + OS_SEPARATOR_STR
+                               + m_separator;
+       } else {
+               prefix = QChar(1 + index) + m_separatorString;
+       }
+       while (it.hasNext()){
+          if (m_shouldStop){
+                 break;
+          }
+          it.next();
+          node = it.fileName();
+          if (it.fileInfo().isDir()){
+                  // nothing to do
+          } else{
+                  Command command = CmdUndef;
+                  QFileInfo src(source + it.fileName());
+                  if (! src.exists()){
+                          const QFileInfo trg = it.fileInfo();
+                          command = trg.lastModified() < m_maxAge ? CmdRemove : CmdMove;
+                          info = prefix + QChar(command) + it.fileName();
+                  }
+                  m_mutex.lock();
+                  if (command != CmdUndef){
+                          m_files.append(info);
+                          m_hotFiles++;
+                          m_hotBytes += it.fileInfo().size();
+                  }
+                  m_totalFiles++;
+                  m_mutex.unlock();
+          }
+       }
+       if (! m_shouldStop){
+               QDirIterator it2(target);
+               QString node;
+               QString source;
+               while (it2.hasNext()){
+                       if (m_shouldStop){
+                          break;
+                       }
+                  it2.next();
+
+                  if (it2.fileInfo().isDir() && (node = it2.fileName()) != "." && node != ".."){
+                          QString newSource = source + it2.fileName();
+                          QFileInfo src(newSource);
+                          if (src.exists() && src.isDir())
+                                  searchOneDirectory(it2.filePath(), newSource + OS_SEPARATOR_STR, index);
+                          else{
+                                  moveToShadow(it2.filePath(), prefix, index);
+                          }
+                  }
+               }
+       }
+}
+
+/**
+ * Moves a target directory to the shadow directory and delegate the removing files.
+ *
+ * @param target       the target directory
+ * @param relPath      the relative path of the parent of <code>target</code>
+ * @param index                the index of the target in <code>m_targetDirs</code>
+ */
+void SearchTargetTask::moveToShadow(const QString& target, const QString& relPath, int index){
+       QString shadowDir = m_shadowDirs.at(index) + relPath;
+       if (! ReFileUtils::makeDirWithParents(shadowDir))
+               error(QObject::tr("cannot create shadow directory (%1): %2")
+                         .arg(errno).arg(shadowDir));
+       else {
+               QString shadow = shadowDir + ReFileUtils::nodeOf(target);
+               if (rename(I18N::s2b(target).constData(), I18N::s2b(shadow).constData()) != 0){
+                       error(QObject::tr("cannot move to shadow directory (%1): %2 -> %3")
+                                 .arg(errno).arg(target).arg(shadow));
+               } else {
+                       removeOlder(shadowDir, m_maxAge, index);
+               }
+       }
+}
+
+/**
+ * Executes the task "search superflous files/dirs in the target directory".
+ */
+void SearchTargetTask::run()
+{
+       qint64 start = QDateTime::currentMSecsSinceEpoch();
+       m_searchReady = false;
+       QString targetDir, sourceDir;
+       for (int ix = 0; ix < m_sourceDirs.size(); ix++){
+               sourceDir = m_sourceDirs.at(ix);
+               targetDir = m_targetDirs.at(ix);
+               searchOneDirectory(targetDir, sourceDir, ix);
+       }
+       m_searchReady = true;
+       m_mainWindow->externalLog(tr(
+               "Search in target finished: to process: %1 with %2 dirs to delete: %3 total: %4 "
+               "subdirs: %5 runtime: %6")
+               .arg(m_hotFiles)
+               .arg(ReQStringUtils::readableSize(m_hotBytes))
+               .arg(m_matchedFiles).arg(m_totalFiles)
+                 .arg(m_totalDirs)
+                 .arg(ReQStringUtils::readableDuration(
+                                  QDateTime::currentMSecsSinceEpoch() - start)));
+}
 
index 5171ee110b29db3000b70da0150761c2988071c9..2b148e42f21f90f62376b3141d34d114a90b62db 100644 (file)
  */
 class BackupEngine : public QThread
 {
+public:
+       enum Command {
+               CmdUndef,
+               CmdRemove,
+               CmdMove,
+               CmdRemoveDir
+       };
+
 public:
        BackupEngine(const QString& name,
                                 const QStringList& sourceDirs, const QString& targetDir,
@@ -23,6 +31,8 @@ public:
    bool error(const QString& message);
    bool log(const QString& message);
 protected:
+   void initializeShadowDir();
+   void removeOlder(const QString& directory, const QDateTime& time, int index);
        virtual void run() = 0;
 protected:
    // list of source dirs, trailing with separator
@@ -31,6 +41,10 @@ protected:
    QString m_targetBaseDir;
    // list of target dirs, trailing with separator. Same node as m_sourceDirs
    QStringList m_targetDirs;
+   // Superflous files will moved from target to these dirs
+   QString m_shadowBaseDir;
+   // list of shadow dirs.
+   QStringList m_shadowDirs;
    MainWindow* m_mainWindow;
    QString m_name;
 public:
@@ -121,6 +135,24 @@ public:
        virtual void run();
 };
 
+/**
+  Handles the clean action.
+
+ * Compares each of the files of in the target directory with the source directory:
+ * If it exists nothing is done.
+ * If not and the target file is older than a given date it is moved to a
+ * shadow directory beneath the target directory.
+ * If is younger the target file is deleted.
+ */
+class CleanTask : public BackupEngine
+{
+public:
+       CleanTask(const QString& name, const QStringList& sourceDirs, const QString& targetDir,
+                       MainWindow* mainWindow);
+public:
+       virtual void run();
+};
+
 /**
  * Searches the files matching the file patterns and writes them into the file list.
  */
@@ -144,5 +176,23 @@ private:
        bool m_compareWithTarget;
 };
 
+/**
+ * Searches the files matching the file patterns and writes them into the file list.
+ */
+class SearchTargetTask : public BackupEngine
+{
+public:
+       SearchTargetTask(const QString& name,
+                          const QStringList& sourceDirs, const QString& targetDir,
+                          qint64 maxAgeSec, MainWindow* mainWindow);
+public:
+       virtual void run();
+private:
+       void moveToShadow(const QString& target, const QString& relPath, int index);
+       void searchOneDirectory(const QString& target, const QString& source,
+                                                       int index);
+protected:
+       QDateTime m_maxAge;
+};
 
 #endif // BACKUPPROCESSOR_HPP
index 7f421f487aca30aa8cba5548223f34c8dfacdcb9..63970b16c41caabd728fe3cfb4318896fc9e95a9 100644 (file)
@@ -30,8 +30,10 @@ MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
        m_backupTask(NULL),
        m_checksumOfSourceTask(NULL),
        m_checksumOfTargetTask(NULL),
-    m_errors(0),
-    m_maxListSize(1000)
+       m_searchTargetTask(NULL),
+       m_cleanTask(NULL),
+       m_errors(0),
+       m_maxListSize(1000)
 {
        ui->setupUi(this);
        initializeGuiElements();
@@ -67,6 +69,7 @@ MainWindow::MainWindow(const QString& homeDir, QWidget *parent) :
    connect(ui->pushButtonClearFileList, SIGNAL(clicked()), this, SLOT(onClearFileList()));
    connect(ui->pushButtonClearErrorList, SIGNAL(clicked()), this, SLOT(onClearErrorList()));
    connect(ui->pushButtonClear, SIGNAL(clicked()), this, SLOT(onClearLog()));
+   connect(ui->pushButtonClean, SIGNAL(clicked()), this, SLOT(onClean()));
    m_configuration.load("");
    ui->tableWidgetConfiguration->selectRow(0);
    updateTable();
@@ -162,14 +165,14 @@ QString MainWindow::extractTarget(const QString& dir){
 void MainWindow::onGuiTimerUpdate()
 {
        int count = m_guiQueue.count();
-    QListWidget* list;
+       QListWidget* list;
        while(count-- > 0){
                m_mutexGuiQueue.lock();
                ReGuiQueueItem item = m_guiQueue.popFront();
                m_mutexGuiQueue.unlock();
-        if (item.m_type == ReGuiQueueItem::ListEnd
-                && (list = reinterpret_cast<QListWidget*>(item.m_widget))->count() >= m_maxListSize)
-            delete list->takeItem(0);
+               if (item.m_type == ReGuiQueueItem::ListEnd
+                               && (list = reinterpret_cast<QListWidget*>(item.m_widget))->count() >= m_maxListSize)
+                       delete list->takeItem(0);
                if (item.m_type == ReGuiQueueItem::Undef)
                        break;
                if (! item.apply()){
@@ -206,7 +209,7 @@ void MainWindow::onGuiTimerUpdate()
  *          otherwise: error occurred
  */
 bool MainWindow::initializeStart(){
-    bool rc = true;
+       bool rc = true;
        BackupEngine::m_searchReady = false;
        BackupEngine::m_hotBytes = 0;
        BackupEngine::m_hotFiles = 0;
@@ -216,18 +219,18 @@ bool MainWindow::initializeStart(){
        BackupEngine::m_processedFiles = 0;
        BackupEngine::m_processedBytes = 0;
        ChecksumTask::m_sourceProcessingReady = false;
-    if ( (m_maxListSize = comboInt(ui->comboBoxMaxListLength, -999)) == -999){
-        rc = false;
-        m_maxListSize = 100;
-        ui->comboBoxMaxListLength->setEditText(QString::number(m_maxListSize));
-    }
+       if ( (m_maxListSize = comboInt(ui->comboBoxMaxListLength, -999)) == -999){
+               rc = false;
+               m_maxListSize = 100;
+               ui->comboBoxMaxListLength->setEditText(QString::number(m_maxListSize));
+       }
        m_errors = 0;
        startStop(true);
-    if (ui->checkBoxAutoClean->isChecked()){
-        onClearFileList();
-        onClearErrorList();
-    }
-    return rc;
+       if (ui->checkBoxAutoClean->isChecked()){
+               onClearFileList();
+               onClearErrorList();
+       }
+       return rc;
 }
 
 /**
@@ -360,7 +363,36 @@ QStringList dirs = ReGuiUtils::selectDirectories(tr("Select Source Directory"),
 }
 
 /**
- * Remove the lines of hte file list.
+ * Remove the lines of the log list.
+ */
+void MainWindow::onClean()
+{
+       int row = ui->tableWidget->currentRow();
+       if (row < 0){
+               say(LOG_ERROR, tr("no backup item selected"));
+       } else {
+               BackupItem& item = m_configuration.items()[row];
+               QString target = BackupUtils::findTarget(item, &m_logger);
+               if (target.isEmpty()){
+                       say(LOG_ERROR, tr("Target not available"));
+               } else {
+                       ReQStringUtils::ensureLastChar(target, OS_SEPARATOR);
+                       //writeTargetConfiguration(item, target);
+                       initializeStart();
+                       delete m_searchTargetTask;
+                       m_searchTargetTask = new SearchTargetTask(item.m_name,
+                                                                               item.m_sources, target, this);
+                       m_searchTargetTask->start();
+                       delete m_cleanTask;
+                       m_cleanTask = new CleanTask(item.m_name, item.m_sources, target,
+                                                                                 this);
+                       m_backupTask->start();
+               }
+       }
+}
+
+/**
+ * Removes the lines of the file list.
  */
 void MainWindow::onClearErrorList()
 {
@@ -368,7 +400,7 @@ void MainWindow::onClearErrorList()
 }
 
 /**
- * Remove the lines of the log list.
+ * Removes the lines of the log list.
  */
 void MainWindow::onClearLog()
 {
index 291364135b2789a31ea84077a720e4aa7a57616a..664e7ae2ae66de772c6cae2e2aa194f4d0152916 100644 (file)
@@ -35,7 +35,6 @@ public:
    void restoreState();
    void saveState();
 
-public slots:
 private:
    QString extractTarget(const QString& dir);
    bool initializeStart();
@@ -46,6 +45,7 @@ private slots:
    void onAddItem();
    void onAddSource();
    void onChecksums();
+   void onClean();
    void onClearFileList();
    void onClearErrorList();
    void onClearLog();
@@ -73,8 +73,10 @@ private:
        BackupTask* m_backupTask;
        ChecksumOfSourceTask* m_checksumOfSourceTask;
        ChecksumOfTargetTask* m_checksumOfTargetTask;
+       SearchTargetTask* m_searchTargetTask;
+       CleanTask* m_cleanTask;
        int m_errors;
-    int m_maxListSize;
+       int m_maxListSize;
 };
 
 #endif // MAINWINDOW_HPP
index 627650bce5cb7b3b5985b3daa503e999d29eeea5..b85e259286a4507dfa2c53073c4abc9ce45ae9fb 100644 (file)
                 </property>
                </widget>
               </item>
+              <item>
+               <widget class="QPushButton" name="pushButtonClean">
+                <property name="minimumSize">
+                 <size>
+                  <width>125</width>
+                  <height>0</height>
+                 </size>
+                </property>
+                <property name="toolTip">
+                 <string>Detects files/dirs not found in the source directory. Old files will be remove, the other will be moved to the shadow directory</string>
+                </property>
+                <property name="text">
+                 <string>Clean</string>
+                </property>
+               </widget>
+              </item>
              </layout>
             </item>
            </layout>
index 99c57006b7cdab8232b94e0bdaaf59735a8fded9..04f24c78498741f4f086025673d00df1ab271161 100644 (file)
@@ -4,48 +4,28 @@ use strict;
 
 my $mode = shift;
 my %devs;
+my (%g_devOfPoint, %g_pointOfDev, %g_devOfLabel, %g_labelOfDev);
+
+&FindLabels;
+&FindMountPoints;
 if ($mode eq "find-label"){
        my $targetPath = shift;
        if (! -d $targetPath){
                &Usage("not a directory: $targetPath");
        }
-       %devs = &LabelOfDevs;
-       open($CMD, "mount|") || die "cannot execute mount: $!";
-       my ($label, $relPath);
-       while(<$CMD>){
-               # /dev/sdb1 on /media/src type vfat (rw,...)
-               if(/(\S+) on (\S+)/){
-                       my ($dev, $path) = ($1, $2);
-                       if (index($targetPath, $path) == 0){
-                               if ($devs{$dev} ne ""){
-                                       $label = $devs{$dev};
-                                       $relPath = substr($targetPath, length($path) + 1);
-                               }
-                       }
-               }
-       }
-       close $CMD;
-       if ($label eq ""){
-               &Usage("no mounted device found");
-       } else {
-               print "L=\"$label\"\n$relPath\n";
-       }
+       &PrintLabelAndRelPath($targetPath);
 } elsif ($mode eq "search-target"){
        my $label = shift;
        my $relPath = shift;
        &Usage("missing label") unless $label;
        &Usage("missing relative path") unless $relPath;
-
-       %devs = &LabelOfDevs;
-       if (
-       # /dev/sdb1: LABEL="EFI system" UUID="80F7-47BB" TYPE="vfat" ...
-       open(my $CMD, "blkid|") || die "cannot execute blkid: $!";
-       while(<$CMD>){
-               if(/(\S+): LABEL="(.*?)"/){
-                       $devs{$1} = $2;
+       my $dev = %g_devOfLabel{$label};
+       if ($dev ne ""){
+               my $mountPoint = $g_pointOfDev{$dev};
+               if ($mountPoint ne ""){
+                       print "$mountPoint/$relPath\n";
                }
        }
-close $CMD;
 } else {
        &Usage("unknown mode");
 }
@@ -76,31 +56,48 @@ EOS
 
 # Returns an associative array with (dev, label) entries
 # @return      an associative array with (dev, label) entries
-sub LabelOfDev{
+sub FindLabels{
 
-       my %devs;
        # /dev/sdb1: LABEL="EFI system" UUID="80F7-47BB" TYPE="vfat" ...
        open(my $CMD, "blkid|") || die "cannot execute blkid: $!";
        while(<$CMD>){
                if(/(\S+): LABEL="(.*?)"/){
-                       $devs{$1} = $2;
+                       $g_devOfLabel{$2} = $1;
+                       $g_labelOfDev{$1} = $2;
                }
        }
        close $CMD;
        return %devs;
 }
 
-# Search the device of a given label.
-# @param $label        label to search
-# @return:             "": not found
-#                              otherwise: the device with the given label
-sub DeviceOfLabel{
-       my $label = shift;
-       my $dev;
-       for (keys %devs){
-       if ($devs{$_} eq $label){
-               $dev = $_;
-               last;
+# stores the relation of devices and mount points.
+sub FindMountPoints{
+       my $dev = shift;
+       open(my $CMD, "mount|") || die "cannot execute mount: $!";
+       my ($label, $relPath);
+       while(<$CMD>){
+               # /dev/sdb1 on /media/src type vfat (rw,...)
+               if(/(\S+) on (\S+)/){
+                       $g_devOfPoint{$2} = $1;
+                       $g_pointOfDev{$1} = $2;
+               }
+       }
+       close $CMD;
+}
+
+# Find the label and relative path given by a subdirectory of the mountpoint
+# @param subdirOfMountpoint
+sub PrintLabelAndRelPath{
+       my $subdirOfMountpoint = shift;
+       my ($relPath, $label);
+       for my $path (keys %g_pointOfDev){
+               if (index($subdirOfMountpoint, $path) == 0){
+                       my $dev = $g_devOfPoint{$path};
+                       if ($g_labelOfDev{$dev} ne ""){
+                               $label = $g_labelOfDev{$dev};
+                               $relPath = substr($subdirOfMountpoint, length($path) + 1);
+                               print "L=$label\n$relPath\n";
+                       }
+               }
        }
-       return $rc;
 }