Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6793ba9
feat(vfs/folders): enable on demand fetch of folder content
mgallien Jun 6, 2025
f11ecfe
feat(vfs/folders): enable on demand fetch of folder content
mgallien Jun 6, 2025
911c662
fix: once a folder has been populated, disable it
mgallien Jul 3, 2025
69c7685
fix: code style fix
mgallien Jul 3, 2025
a8e9c62
fix(vfs/folders): track state of folders using a new blacklist type
mgallien Jul 8, 2025
efa7556
fix: makes sure we do fetch empty folders again
mgallien Jul 9, 2025
80af06a
fix(vfs/folders-on-demand): do not black list virtual folders
mgallien Jul 9, 2025
bd2c8ed
fix(vfs/folders-on-demand): use proper item type for virtual folders
mgallien Jul 9, 2025
3713856
fix(vfs/windows): handle virtual directories as virtual directories
mgallien Sep 23, 2025
335c54b
fix(vfs/windows): update folder type when populated
mgallien Sep 23, 2025
0cbd996
fix(vfs/windows): virtual folders are folders
mgallien Sep 24, 2025
1d4405f
fix(vfs/windows): properly update DB record when folder is not virtual
mgallien Sep 24, 2025
1559783
chore(vfs/windows): more logs for fetch placeholders callback
mgallien Sep 25, 2025
0fc01df
fix(vfs/windows): fix creation of items inside virtual folders
mgallien Sep 25, 2025
522a734
fix(vfs/xattr): fix compilation of vfs xattr experimental plug-in
mgallien Sep 26, 2025
d67f255
fix(db): assert if we insert a DB record with empty path
mgallien Sep 26, 2025
e195150
fix(tests): ensure we do not access items in empty QList
mgallien Sep 26, 2025
0fb59f2
fix(propagator): do not create invalid records when propagating
mgallien Sep 26, 2025
11deae9
fix(vfs/discovery): do not recurse inside a virtual folder
mgallien Sep 26, 2025
f78ad0e
chore(webdav): makes webdav proprty parsing code into reusable method
mgallien Sep 28, 2025
b050fe7
chore(db): do not create db records with invalid mtime
mgallien Sep 28, 2025
d3cfc9a
fix(vfs/windows): fix issues with folders on-demand DB records
mgallien Sep 28, 2025
ada0429
fix(vfs): fix regressions with folders on-demand tests
mgallien Sep 30, 2025
74d551e
chore(ci/linux): update the CI images for linux CI compilation and tests
mgallien Sep 30, 2025
6945c18
chore(vfs/windows): log items pattern when fetching placeholders
mgallien Sep 30, 2025
f5f4a00
chore(vfs/windows): expect failed tests on windows VFS
mgallien Sep 30, 2025
2d94e3b
chore(vfs/windows): common method for WebDAV proprties list
mgallien Oct 1, 2025
e1a85a5
chore: fix common issue with one wrong variable name
mgallien Oct 1, 2025
ef184cf
chore: simplify code
mgallien Oct 1, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/linux-clang-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
build:
name: Linux Clang compilation and tests
runs-on: ubuntu-latest
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-6.8.1-2
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
Expand All @@ -17,7 +17,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G Ninja -DCMAKE_PREFIX_PATH=/opt/qt -DCMAKE_C_COMPILER=clang-14 -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_BUILD_TYPE=Debug -DQT_MAJOR_VERSION=6 -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64
cmake .. -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DQT_MAJOR_VERSION=6 -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DCMAKE_CXX_FLAGS=-Werror
ninja
- name: Run tests
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/linux-gcc-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
build:
name: Linux GCC compilation and tests
runs-on: ubuntu-latest
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-6.8.1-2
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
Expand All @@ -17,7 +17,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G Ninja -DCMAKE_PREFIX_PATH=/opt/qt -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_BUILD_TYPE=Debug -DQT_MAJOR_VERSION=6 -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64
cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Debug -DQT_MAJOR_VERSION=6 -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DCMAKE_CXX_FLAGS=-Werror
ninja
- name: Run tests
run: |
Expand Down
3 changes: 3 additions & 0 deletions src/common/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ set(common_SOURCES
${CMAKE_CURRENT_LIST_DIR}/pinstate.cpp
${CMAKE_CURRENT_LIST_DIR}/plugin.cpp
${CMAKE_CURRENT_LIST_DIR}/syncfilestatus.cpp
${CMAKE_CURRENT_LIST_DIR}/syncitemenums.cpp
${CMAKE_CURRENT_LIST_DIR}/remoteinfo.h
${CMAKE_CURRENT_LIST_DIR}/folderquota.h
)

if(WIN32)
Expand Down
36 changes: 36 additions & 0 deletions src/common/folderquota.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef FOLDERQUOTA_H
#define FOLDERQUOTA_H

#include <cstdint>

Check failure on line 10 in src/common/folderquota.h

View workflow job for this annotation

GitHub Actions / build

src/common/folderquota.h:10:10 [clang-diagnostic-error]

'cstdint' file not found

namespace OCC {

Check warning on line 12 in src/common/folderquota.h

View workflow job for this annotation

GitHub Actions / build

src/common/folderquota.h:12:11 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'OCC' is non-const and globally accessible, consider making it const

/**
* Represent the quota for each folder retrieved from the server
* bytesUsed: space used in bytes
* bytesAvailale: free space available in bytes or
* -1: Uncomputed free space - new folder (externally created) not yet scanned by the server
* -2: Unknown free space
* -3: Unlimited free space.
*/
struct FolderQuota
{
int64_t bytesUsed = -1;
int64_t bytesAvailable = -1;
enum ServerEntry {
Invalid = 0,
Valid
};
static constexpr char availableBytesC[] = "quota-available-bytes";
static constexpr char usedBytesC[] = "quota-used-bytes";
};

}

#endif // FOLDERQUOTA_H
62 changes: 62 additions & 0 deletions src/common/remoteinfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef REMOTEINFO_H
#define REMOTEINFO_H

#include "folderquota.h"
#include "common/remotepermissions.h"
#include "common/syncitemenums.h"

#include <QString>
#include <QByteArray>

namespace OCC {

/**
* Represent all the meta-data about a file in the server
*/
struct RemoteInfo
{
/** FileName of the entry (this does not contains any directory or path, just the plain name */
QString name;
QByteArray etag;
QByteArray fileId;
QByteArray checksumHeader;
OCC::RemotePermissions remotePerm;
time_t modtime = 0;
int64_t size = 0;
int64_t sizeOfFolder = 0;
bool isDirectory = false;
bool _isE2eEncrypted = false;
bool isFileDropDetected = false;
QString e2eMangledName;
bool sharedByMe = false;

[[nodiscard]] bool isValid() const { return !name.isNull(); }
[[nodiscard]] bool isE2eEncrypted() const { return _isE2eEncrypted; }

QString directDownloadUrl;
QString directDownloadCookies;

SyncFileItemEnums::LockStatus locked = SyncFileItemEnums::LockStatus::UnlockedItem;
QString lockOwnerDisplayName;
QString lockOwnerId;
SyncFileItemEnums::LockOwnerType lockOwnerType = SyncFileItemEnums::LockOwnerType::UserLock;
QString lockEditorApp;
qint64 lockTime = 0;
qint64 lockTimeout = 0;
QString lockToken;

bool isLivePhoto = false;
QString livePhotoFile;

FolderQuota folderQuota;
};

}

#endif // REMOTEINFO_H
7 changes: 7 additions & 0 deletions src/common/syncitemenums.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* SPDX-FileCopyrightText: 2020 ownCloud GmbH
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#include "syncitemenums.h"
#include "moc_syncitemenums.cpp"

Check warning on line 7 in src/common/syncitemenums.cpp

View workflow job for this annotation

GitHub Actions / build

src/common/syncitemenums.cpp:7:11 [bugprone-suspicious-include]

suspicious #include of file with '.cpp' extension
39 changes: 39 additions & 0 deletions src/common/syncitemenums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef SYNCITEMENUMS_H
#define SYNCITEMENUMS_H

#include "ocsynclib.h"

Check failure on line 10 in src/common/syncitemenums.h

View workflow job for this annotation

GitHub Actions / build

src/common/syncitemenums.h:10:10 [clang-diagnostic-error]

'ocsynclib.h' file not found

#include <QObject>

namespace OCC {

Check warning on line 14 in src/common/syncitemenums.h

View workflow job for this annotation

GitHub Actions / build

src/common/syncitemenums.h:14:11 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'OCC' is non-const and globally accessible, consider making it const

namespace SyncFileItemEnums {

OCSYNC_EXPORT Q_NAMESPACE

enum class LockStatus {
UnlockedItem = 0,
LockedItem = 1,
};

Q_ENUM_NS(LockStatus)

enum class LockOwnerType : int{
UserLock = 0,
AppLock = 1,
TokenLock = 2,
};

Q_ENUM_NS(LockOwnerType)

}

}

#endif // SYNCITEMENUMS_H
43 changes: 43 additions & 0 deletions src/common/syncjournaldb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#include <QCryptographicHash>

Check failure on line 7 in src/common/syncjournaldb.cpp

View workflow job for this annotation

GitHub Actions / build

src/common/syncjournaldb.cpp:7:10 [clang-diagnostic-error]

'QCryptographicHash' file not found
#include <QFile>
#include <QLoggingCategory>
#include <QStringList>
Expand Down Expand Up @@ -1015,6 +1015,11 @@
SyncJournalFileRecord record = _record;
QMutexLocker locker(&_mutex);

Q_ASSERT(record._modtime > 0);
if (record._modtime <= 0) {
qCCritical(lcDb) << "invalid modification time";
}

if (!_etagStorageFilter.isEmpty()) {
// If we are a directory that should not be read from db next time, don't write the etag
QByteArray prefix = record._path + "/";
Expand Down Expand Up @@ -1044,6 +1049,8 @@
<< "livePhotoFile" << record._livePhotoFile
<< "folderQuota - bytesUsed:" << record._folderQuota.bytesUsed << "bytesAvailable:" << record._folderQuota.bytesAvailable;

Q_ASSERT(!record.path().isEmpty());

const qint64 phash = getPHash(record._path);
if (!checkConnect()) {
qCWarning(lcDb) << "Failed to connect database.";
Expand Down Expand Up @@ -2377,6 +2384,42 @@
commitInternal(QStringLiteral("setSelectiveSyncList"));
}

QStringList SyncJournalDb::addSelectiveSyncLists(SelectiveSyncListType type, const QString &path)
{
bool ok = false;

const auto pathWithTrailingSlash = Utility::trailingSlashPath(path);

const auto blackListList = getSelectiveSyncList(type, &ok);
auto blackListSet = QSet<QString>{blackListList.begin(), blackListList.end()};
blackListSet.insert(pathWithTrailingSlash);
auto blackList = blackListSet.values();
blackList.sort();
setSelectiveSyncList(type, blackList);

qCInfo(lcSql()) << "add" << path << "into" << type << blackList;

return blackList;
}

QStringList SyncJournalDb::removeSelectiveSyncLists(SelectiveSyncListType type, const QString &path)
{
bool ok = false;

const auto pathWithTrailingSlash = Utility::trailingSlashPath(path);

const auto blackListList = getSelectiveSyncList(type, &ok);
auto blackListSet = QSet<QString>{blackListList.begin(), blackListList.end()};
blackListSet.remove(pathWithTrailingSlash);
auto blackList = blackListSet.values();
blackList.sort();
setSelectiveSyncList(type, blackList);

qCInfo(lcSql()) << "remove" << path << "into" << type << blackList;

return blackList;
}

void SyncJournalDb::avoidRenamesOnNextSync(const QByteArray &path)
{
QMutexLocker locker(&_mutex);
Expand Down
4 changes: 4 additions & 0 deletions src/common/syncjournaldb.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#ifndef SYNCJOURNALDB_H
#define SYNCJOURNALDB_H

#include <QObject>

Check failure on line 10 in src/common/syncjournaldb.h

View workflow job for this annotation

GitHub Actions / build

src/common/syncjournaldb.h:10:10 [clang-diagnostic-error]

'QObject' file not found
#include <QDateTime>
#include <QHash>
#include <QMutex>
Expand Down Expand Up @@ -173,6 +173,10 @@
/* Write the selective sync list (remove all other entries of that list */
void setSelectiveSyncList(SelectiveSyncListType type, const QStringList &list);

QStringList addSelectiveSyncLists(SelectiveSyncListType type, const QString &path);

QStringList removeSelectiveSyncLists(SelectiveSyncListType type, const QString &path);

/**
* Make sure that on the next sync fileName and its parents are discovered from the server.
*
Expand Down
2 changes: 1 addition & 1 deletion src/common/syncjournalfilerecord.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
Expand All @@ -7,7 +7,7 @@
#ifndef SYNCJOURNALFILERECORD_H
#define SYNCJOURNALFILERECORD_H

#include <QString>

Check failure on line 10 in src/common/syncjournalfilerecord.h

View workflow job for this annotation

GitHub Actions / build

src/common/syncjournalfilerecord.h:10:10 [clang-diagnostic-error]

'QString' file not found
#include <QDateTime>

#include "csync.h"
Expand Down Expand Up @@ -53,7 +53,7 @@
[[nodiscard]] QByteArray numericFileId() const;
[[nodiscard]] QDateTime modDateTime() const { return Utility::qDateTimeFromTime_t(_modtime); }

[[nodiscard]] bool isDirectory() const { return _type == ItemTypeDirectory; }
[[nodiscard]] bool isDirectory() const { return _type == ItemTypeVirtualDirectory || _type == ItemTypeDirectory; }
[[nodiscard]] bool isFile() const { return _type == ItemTypeFile || _type == ItemTypeVirtualFileDehydration; }
[[nodiscard]] bool isVirtualFile() const { return _type == ItemTypeVirtualFile || _type == ItemTypeVirtualFileDownload; }
[[nodiscard]] QString path() const { return QString::fromUtf8(_path); }
Expand Down
15 changes: 13 additions & 2 deletions src/common/vfs.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2018 ownCloud GmbH
Expand All @@ -5,14 +5,18 @@
*/
#pragma once

#include "ocsynclib.h"

Check failure on line 8 in src/common/vfs.h

View workflow job for this annotation

GitHub Actions / build

src/common/vfs.h:8:10 [clang-diagnostic-error]

'ocsynclib.h' file not found
#include "result.h"
#include "syncfilestatus.h"
#include "pinstate.h"

#include "common/remoteinfo.h"

#include <QObject>
#include <QScopedPointer>
#include <QSharedPointer>
#include <QString>
#include <QMap>

#include <deque>
#include <memory>
Expand All @@ -28,6 +32,13 @@
class SyncFileItem;
using SyncFileItemPtr = QSharedPointer<SyncFileItem>;

struct OCSYNC_EXPORT PlaceholderCreateInfo {
QString name;
std::wstring stdWStringName;
QString fullPath;
RemoteInfo parsedProperties;
};

/** Collection of parameters for initializing a Vfs instance. */
struct OCSYNC_EXPORT VfsSetupParams
{
Expand Down Expand Up @@ -183,7 +194,7 @@
*/
[[nodiscard]] virtual OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> updateMetadata(const SyncFileItem &syncItem, const QString &filePath, const QString &replacesFile) = 0;

[[nodiscard]] virtual Result<Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) = 0;
[[nodiscard]] virtual Result<Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderMarkInSync(const QString &filePath, const SyncFileItem &item) = 0;

[[nodiscard]] virtual bool isPlaceHolderInSync(const QString &filePath) const = 0;

Expand Down Expand Up @@ -326,7 +337,7 @@
[[nodiscard]] bool isHydrating() const override { return false; }

OCC::Result<OCC::Vfs::ConvertToPlaceholderResult, QString> updateMetadata(const SyncFileItem &, const QString &, const QString &) override { return {OCC::Vfs::ConvertToPlaceholderResult::Ok}; }
Result<Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) override {Q_UNUSED(filePath) Q_UNUSED(fileId) return {QString{}};}
Result<Vfs::ConvertToPlaceholderResult, QString> updatePlaceholderMarkInSync(const QString &filePath, const SyncFileItem &item) override {Q_UNUSED(filePath) Q_UNUSED(item) return {QString{}};}
[[nodiscard]] bool isPlaceHolderInSync(const QString &filePath) const override { Q_UNUSED(filePath) return true; }

Result<void, QString> createPlaceholder(const SyncFileItem &) override { return {}; }
Expand Down
2 changes: 2 additions & 0 deletions src/csync/csync.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#ifndef _CSYNC_H
#define _CSYNC_H

#include <QByteArray>

Check failure on line 23 in src/csync/csync.h

View workflow job for this annotation

GitHub Actions / build

src/csync/csync.h:23:10 [clang-diagnostic-error]

'QByteArray' file not found
#include <QVariant>
#include <QLoggingCategory>

Expand Down Expand Up @@ -186,6 +186,8 @@
* file dehydration without changing the pin state.
*/
ItemTypeVirtualFileDehydration = 6,

ItemTypeVirtualDirectory = 7,
};
Q_ENUM_NS(ItemType)
}
Expand Down
6 changes: 4 additions & 2 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "common/syncjournaldb.h"

Check failure on line 7 in src/gui/folder.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/folder.cpp:7:10 [clang-diagnostic-error]

'common/syncjournaldb.h' file not found
#include "config.h"

#include "account.h"
Expand Down Expand Up @@ -696,7 +696,8 @@
SyncJournalFileRecord rec;
const auto isFileRecordValid = journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid();
if (isFileRecordValid) {
[[maybe_unused]] const auto result = _vfs->updatePlaceholderMarkInSync(path() + rec.path(), rec._fileId);
const auto itemPointer = SyncFileItem::fromSyncJournalFileRecord(rec);
[[maybe_unused]] const auto result = _vfs->updatePlaceholderMarkInSync(path() + rec.path(), *itemPointer);
}
const auto canUnlockFile = isFileRecordValid
&& rec._lockstate._locked
Expand Down Expand Up @@ -740,7 +741,8 @@
const auto fileRecordPath = fileFromLocalPath(file);
SyncJournalFileRecord rec;
if (journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid()) {
[[maybe_unused]] const auto result = _vfs->updatePlaceholderMarkInSync(path() + rec.path(), rec._fileId);
const auto itemPointer = SyncFileItem::fromSyncJournalFileRecord(rec);
[[maybe_unused]] const auto result = _vfs->updatePlaceholderMarkInSync(path() + rec.path(), *itemPointer);
}
}
}
Expand Down
Loading
Loading