Skip to content
Merged
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
66 changes: 65 additions & 1 deletion src/libsync/discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace
constexpr const char *editorNamesForDelayedUpload[] = {"PowerPDF"};
constexpr const char *fileExtensionsToCheckIfOpenForSigning[] = {".pdf"};
constexpr auto delayIntervalForSyncRetryForOpenedForSigningFilesSeconds = 60;
constexpr auto delayIntervalForSyncRetryForFilesExceedQuotaSeconds = 60;
}

namespace OCC {
Expand Down Expand Up @@ -766,6 +767,9 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it
item->_isLivePhoto = serverEntry.isLivePhoto;
item->_livePhotoFile = serverEntry.livePhotoFile;

item->_folderQuota.bytesUsed = serverEntry.folderQuota.bytesUsed;
item->_folderQuota.bytesAvailable = serverEntry.folderQuota.bytesAvailable;

// Check for missing server data
{
QStringList missingData;
Expand Down Expand Up @@ -1072,6 +1076,40 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it
processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer);
}

int64_t ProcessDirectoryJob::folderQuotaAvailable(const SyncFileItemPtr &item)
{
const auto unlimitedFreeSpace = -3;
if (const auto isNewItem = item->_instruction == CSYNC_INSTRUCTION_NEW;
_queryServer != QueryMode::InBlackList && _queryServer != QueryMode::ParentDontExist
&& !item->isDirectory()
&& item->_direction == SyncFileItem::Up
&& item->_size > 0
&& (item->_instruction == CSYNC_INSTRUCTION_SYNC || isNewItem)) {
Comment thread
camilasan marked this conversation as resolved.

const auto unknownFreeSpace = -2;
const auto bytesAvailable = _folderQuota.bytesAvailable;

if (!_dirItem) {
return bytesAvailable;
}

const auto isDirItemRenamed = _dirItem && _dirItem->_instruction == CSYNC_INSTRUCTION_RENAME;

// quota is unknown at this point
if (isDirItemRenamed && isNewItem) {
return unknownFreeSpace;
}

if (isDirItemRenamed) {
return bytesAvailable;
}

return _dirItem->_folderQuota.bytesAvailable;
}

return unlimitedFreeSpace;
}

void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
const SyncFileItemPtr &item, PathTuple path, const LocalInfo &localEntry,
const RemoteInfo &serverEntry, const SyncJournalFileRecord &dbEntry, QueryMode recurseQueryServer)
Expand Down Expand Up @@ -1126,6 +1164,25 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
item->_status = SyncFileItem::Status::NormalError;
}

if (const auto folderQuota = folderQuotaAvailable(item);item->_size > folderQuota && folderQuota > -1) {
qCDebug(lcDisco) << "Folder" << item->_file
<< "- quota used: " << serverEntry.folderQuota.bytesUsed
<< "- quota available: " << serverEntry.folderQuota.bytesAvailable;
item->_instruction = CSYNC_INSTRUCTION_ERROR;
if (_currentFolder._server.isEmpty()) {
item->_errorString = tr("Upload of %1 exceeds %2 of space left in personal files.").arg(Utility::octetsToString(item->_size),
Utility::octetsToString(folderQuota));
} else {
item->_errorString = tr("Upload of %1 exceeds %2 of space left in folder %3.").arg(Utility::octetsToString(item->_size),
Utility::octetsToString(folderQuota),
_currentFolder._server);
}

item->_status = SyncFileItem::Status::NormalError;
_discoveryData->_anotherSyncNeeded = true;
Comment thread
camilasan marked this conversation as resolved.
_discoveryData->_filesNeedingScheduledSync.insert(path._original, delayIntervalForSyncRetryForFilesExceedQuotaSeconds);
}

if (item->_type != CSyncEnums::ItemTypeVirtualFile) {
const auto foundEditorsKeepingFileBusy = queryEditorsKeepingFileBusy(item, path);
if (!foundEditorsKeepingFileBusy.isEmpty()) {
Expand Down Expand Up @@ -2153,6 +2210,7 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery()
}

connect(serverJob, &DiscoverySingleDirectoryJob::etag, this, &ProcessDirectoryJob::etag);
connect(serverJob, &DiscoverySingleDirectoryJob::setfolderQuota, this, &ProcessDirectoryJob::setFolderQuota);
_discoveryData->_currentlyActiveJobs++;
_pendingAsyncJobs++;
connect(serverJob, &DiscoverySingleDirectoryJob::finished, this, [this, serverJob](const auto &results) {
Expand All @@ -2179,6 +2237,7 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery()
_serverQueryDone = true;
if (!serverJob->_dataFingerprint.isEmpty() && _discoveryData->_dataFingerprint.isEmpty())
_discoveryData->_dataFingerprint = serverJob->_dataFingerprint;

if (_localQueryDone)
this->process();
} else {
Expand Down Expand Up @@ -2209,6 +2268,12 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery()
return serverJob;
}

void ProcessDirectoryJob::setFolderQuota(const FolderQuota &folderQuota)
{
_folderQuota.bytesUsed = folderQuota.bytesUsed;
_folderQuota.bytesAvailable = folderQuota.bytesAvailable;
}

void ProcessDirectoryJob::startAsyncLocalQuery()
{
QString localPath = _discoveryData->_localDir + _currentFolder._local;
Expand Down Expand Up @@ -2337,5 +2402,4 @@ bool ProcessDirectoryJob::maybeRenameForWindowsCompatibility(const QString &abso
}
return result;
}

}
7 changes: 7 additions & 0 deletions src/libsync/discovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,16 @@ class ProcessDirectoryJob : public QObject
PinState _pinState = PinState::Unspecified; // The directory's pin-state, see computePinState()
bool _isInsideEncryptedTree = false; // this directory is encrypted or is within the tree of directories with root directory encrypted

FolderQuota _folderQuota;

int64_t folderQuotaAvailable(const SyncFileItemPtr &item);

signals:
void finished();
// The root etag of this directory was fetched
void etag(const QByteArray &, const QDateTime &time);

private slots:
void setFolderQuota(const FolderQuota &folderQuota);
};
}
21 changes: 21 additions & 0 deletions src/libsync/discoveryphase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ void DiscoverySingleDirectoryJob::start()
<< "getlastmodified"
<< "getcontentlength"
<< "getetag"
<< "quota-available-bytes"
<< "quota-used-bytes"
<< "http://owncloud.org/ns:size"
<< "http://owncloud.org/ns:id"
<< "http://owncloud.org/ns:fileid"
Expand Down Expand Up @@ -581,6 +583,14 @@ static void propertyMapToRemoteInfo(const QMap<QString, QString> &map, RemotePer
if (result.isDirectory && map.contains("size")) {
result.sizeOfFolder = map.value("size").toInt();
}

if (result.isDirectory && map.contains("quota-used-bytes")) {
result.folderQuota.bytesUsed = map.value("quota-used-bytes").toLongLong();
}

if (result.isDirectory && map.contains("quota-available-bytes")) {
result.folderQuota.bytesAvailable = map.value("quota-available-bytes").toLongLong();
}
}

void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(const QString &file, const QMap<QString, QString> &map)
Expand Down Expand Up @@ -615,11 +625,22 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(const QString &fi
if (map.contains("size")) {
_size = map.value("size").toInt();
}

// all folders will contain both
if (map.contains("quota-used-bytes") && map.contains("quota-available-bytes")) {
emit setfolderQuota(FolderQuota{map.value("quota-used-bytes").toLongLong(), map.value("quota-available-bytes").toLongLong()});
}
} else {
RemoteInfo result;
int slash = file.lastIndexOf('/');
result.name = file.mid(slash + 1);
result.size = -1;
if (map.contains("quota-used-bytes")) {
result.folderQuota.bytesUsed = map.value("quota-used-bytes").toInt();
}
if (map.contains("quota-available-bytes")) {
result.folderQuota.bytesAvailable = map.value("quota-available-bytes").toInt();
}
propertyMapToRemoteInfo(map,
_account->serverHasMountRootProperty() ? RemotePermissions::MountedPermissionAlgorithm::UseMountRootProperty : RemotePermissions::MountedPermissionAlgorithm::WildGuessMountedSubProperty,
result);
Expand Down
18 changes: 18 additions & 0 deletions src/libsync/discoveryphase.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ class ProcessDirectoryJob;

enum class ErrorCategory;

/**
* 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 = 0;
int64_t bytesAvailable = 0;
};

/**
* Represent all the meta-data about a file in the server
*/
Expand Down Expand Up @@ -83,6 +97,8 @@ struct RemoteInfo

bool isLivePhoto = false;
QString livePhotoFile;

FolderQuota folderQuota;
};

struct LocalInfo
Expand Down Expand Up @@ -164,6 +180,7 @@ class DiscoverySingleDirectoryJob : public QObject
void firstDirectoryPermissions(OCC::RemotePermissions);
void etag(const QByteArray &, const QDateTime &time);
void finished(const OCC::HttpResult<QVector<OCC::RemoteInfo>> &result);
void setfolderQuota(const FolderQuota &folderQuota);

private slots:
void directoryListingIteratedSlot(const QString &, const QMap<QString, QString> &);
Expand Down Expand Up @@ -208,6 +225,7 @@ private slots:

public:
QByteArray _dataFingerprint;
FolderQuota _folderQuota;
};

class DiscoveryPhase : public QObject
Expand Down
3 changes: 2 additions & 1 deletion src/libsync/propagateupload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,9 @@ void PropagateUploadFileCommon::slotJobDestroyed(QObject *job)
// This function is used whenever there is an error occurring and jobs might be in progress
void PropagateUploadFileCommon::abortWithError(SyncFileItem::Status status, const QString &error)
{
if (_aborting)
if (_aborting) {
return;
}
abort(AbortType::Synchronous);
done(status, error);
}
Expand Down
3 changes: 2 additions & 1 deletion src/libsync/syncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1442,8 +1442,9 @@ void SyncEngine::slotInsufficientLocalStorage()
void SyncEngine::slotInsufficientRemoteStorage()
{
auto msg = tr("There is insufficient space available on the server for some uploads.");
if (_uniqueErrors.contains(msg))
if (_uniqueErrors.contains(msg)) {
return;
}

_uniqueErrors.insert(msg);
emit syncError(msg, ErrorCategory::InsufficientRemoteStorage);
Expand Down
7 changes: 7 additions & 0 deletions src/libsync/syncfileitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ class OWNCLOUDSYNC_EXPORT SyncFileItem
/// if true, requests the file to be permanently deleted instead of moved to the trashbin
/// only relevant for when `_instruction` is set to `CSYNC_INSTRUCTION_REMOVE`
bool _wantsPermanentDeletion = false;

struct FolderQuota {
int64_t bytesUsed = 0;
int64_t bytesAvailable = 0;
};

FolderQuota _folderQuota;
};

inline bool operator<(const SyncFileItemPtr &item1, const SyncFileItemPtr &item2)
Expand Down
2 changes: 2 additions & 0 deletions test/syncenginetestutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
xml.writeTextElement(davUri, QStringLiteral("getlastmodified"), stringDate);
xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.size));
xml.writeTextElement(davUri, QStringLiteral("getetag"), QStringLiteral("\"%1\"").arg(QString::fromLatin1(fileInfo.etag)));
xml.writeTextElement(ocUri, QStringLiteral("quota-available-bytes"), std::to_string(fileInfo.quota.bytesAvailable));
xml.writeTextElement(ocUri, QStringLiteral("quota-used-bytes"), std::to_string(fileInfo.quota.bytesUsed));
xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("SRDNVCKW") : QStringLiteral("RDNVCKW"));
xml.writeTextElement(ocUri, QStringLiteral("share-permissions"), QString::number(static_cast<int>(OCC::SharePermissions(OCC::SharePermissionRead |
OCC::SharePermissionUpdate |
Expand Down
6 changes: 6 additions & 0 deletions test/syncenginetestutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ class FileInfo : public FileModifier
bool isEncrypted = false;
bool isLivePhoto = false;

struct FileInfoQuota {
int64_t bytesUsed = 0;
int64_t bytesAvailable = 5000000000;
};
FileInfoQuota quota;

// Sorted by name to be able to compare trees
QMap<QString, FileInfo> children;
QString parentPath;
Expand Down