Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions src/csync/csync_exclude.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#include "config_csync.h"

Check failure on line 10 in src/csync/csync_exclude.cpp

View workflow job for this annotation

GitHub Actions / build

src/csync/csync_exclude.cpp:10:10 [clang-diagnostic-error]

'config_csync.h' file not found
#include <qglobal.h>

#ifndef _GNU_SOURCE
Expand Down Expand Up @@ -470,8 +470,9 @@
continue;
}

if (!m.hasMatch())
return CSYNC_NOT_EXCLUDED;
if (!m.hasMatch()) {
continue;
}
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
Expand All @@ -494,12 +495,13 @@
continue;
}

if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
if (!m.hasMatch()) {
continue;
}
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
}
return CSYNC_NOT_EXCLUDED;
Expand Down Expand Up @@ -533,12 +535,13 @@
continue;
}

if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
if (!m.hasMatch()) {
continue;
}
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
}

Expand Down
75 changes: 72 additions & 3 deletions test/testexcludedfiles.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud GmbH
Expand All @@ -8,15 +8,19 @@
* any purpose.
*/

#include <QtTest>

Check failure on line 11 in test/testexcludedfiles.cpp

View workflow job for this annotation

GitHub Actions / build

test/testexcludedfiles.cpp:11:10 [clang-diagnostic-error]

'QtTest' file not found
#include <QTemporaryDir>

#include "common/utility.h"
#include "csync.h"
#include "csync_exclude.h"
#include "logger.h"

using namespace OCC;

#define EXCLUDE_LIST_FILE SOURCEDIR "/../../sync-exclude.lst"
using namespace Qt::StringLiterals;

constexpr auto EXCLUDE_LIST_FILE = SOURCEDIR "/../../sync-exclude.lst";

// The tests were converted from the old CMocka framework, that's why there is a global
static QScopedPointer<ExcludedFiles> excludedFiles;
Expand Down Expand Up @@ -397,8 +401,8 @@
QCOMPARE(check_file_traversal("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal("/excludepath/withsubdir2"), CSYNC_NOT_EXCLUDED);

// because leading dirs aren't checked!
QCOMPARE(check_dir_traversal("/excludepath/withsubdir/foo"), CSYNC_NOT_EXCLUDED);
// Parent directories are considered too
QCOMPARE(check_dir_traversal("/excludepath/withsubdir/foo"), CSYNC_FILE_EXCLUDE_LIST);

/* Check ending of pattern */
QCOMPARE(check_file_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
Expand Down Expand Up @@ -785,6 +789,71 @@
QCOMPARE(excludedFiles->reloadExcludeFiles(), true);
QCOMPARE(excludedFiles->_allExcludes.size(), 1);
}

void testFolderExcludeListInheritsGlobalExcludes()
{
QTemporaryDir tempDir;
const auto localPath = Utility::trailingSlashPath(tempDir.path());
excludedFiles.reset(new ExcludedFiles(localPath));

// create .sync-exclude.lst files inside `a/b` and `a/b/c` subdirectories
QDir localDir(localPath);
QVERIFY(localDir.mkpath("a/b/c/d"));
const auto writeExcludeList = [&localDir](const QString& path, const QStringList& contents) -> bool {
QFile f(localDir.filePath("%1/.sync-exclude.lst"_L1.arg(path)));
if (!f.open(QIODevice::WriteOnly)) {
return false;
}
f.write(contents.join("\n").toLocal8Bit());
f.close();
return true;
};
QVERIFY(writeExcludeList("a/b", {"B_ignore*"}));
QVERIFY(writeExcludeList("a/b/c", {"C_ignore*"}));

// add default/global exclude list from the client
excludedFiles->addExcludeFilePath(EXCLUDE_LIST_FILE);
QVERIFY(excludedFiles->reloadExcludeFiles());

QCOMPARE(excludedFiles->traversalPatternMatch("~$_patternExcludedByDefault", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);

// according to `ExcludedFiles::traversalPatternMatch`, directories are
// guaranteed to be visited before their files, so match those first.
//
// The function has the side effect of reading additional excludes from
// a ".sync-exclude.lst" file in the passed directory
QCOMPARE(excludedFiles->_allExcludes.size(), 1);
QCOMPARE(excludedFiles->traversalPatternMatch("a", ItemTypeDirectory), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->_allExcludes.size(), 1);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b", ItemTypeDirectory), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->_allExcludes.size(), 2);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c", ItemTypeDirectory), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->_allExcludes.size(), 3);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/d", ItemTypeDirectory), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->_allExcludes.size(), 3);

// validate custom ignores from the directory-specific .sync-exclude.lst
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/B_ignoredFile", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/C_ignoredFile", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);

// excludes from subfolders are not propagated to their parent(s)
QCOMPARE(excludedFiles->traversalPatternMatch("B_ignoredFile", ItemTypeFile), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->traversalPatternMatch("a/B_ignoredFile", ItemTypeFile), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->traversalPatternMatch("C_ignoredFile", ItemTypeFile), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->traversalPatternMatch("a/C_ignoredFile", ItemTypeFile), CSYNC_NOT_EXCLUDED);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/C_ignoredFile", ItemTypeFile), CSYNC_NOT_EXCLUDED);

// global exclude list should still match in subdirs
QCOMPARE(excludedFiles->traversalPatternMatch("a/~$_patternExcludedByDefault", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/~$_patternExcludedByDefault", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/~$_patternExcludedByDefault", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/d/~$_patternExcludedByDefault", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);

// excludes from subfolders inherit the parent excludes
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/B_ignoredFile", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/d/B_ignoredFile", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(excludedFiles->traversalPatternMatch("a/b/c/d/C_ignoredFile", ItemTypeFile), CSYNC_FILE_EXCLUDE_LIST);
}
};

QTEST_APPLESS_MAIN(TestExcludedFiles)
Expand Down
Loading