Skip to content

fix(discovery): check quota before upload (during discovery) - #9777

Open
solracsf wants to merge 2 commits into
masterfrom
checkQuotaBeforeUpload
Open

fix(discovery): check quota before upload (during discovery)#9777
solracsf wants to merge 2 commits into
masterfrom
checkQuotaBeforeUpload

Conversation

@solracsf

@solracsf solracsf commented Apr 2, 2026

Copy link
Copy Markdown
Member

Fixes #4580 (more an enhancement of actual behavior)

Related to nextcloud/server#51977
Related to nextcloud/server#59398

Quota is now checked during discovery, before any data is transmitted. If a file exceeds available quota it receives a DetailError immediately, with no retry scheduled. A _quotaBytesReserved counter prevents multiple files from being approved when their combined size exceeds quota.

The propagator's pre-upload guard is also improved: it now walks the folder hierarchy to find the nearest quota entry, and the propagator is seeded with PROPFIND quota data at the start of each cycle. Combined with nextcloud/server#59398 and nextcloud/groupfolders#4539 this makes this behavior almost full proof.

4 new tests cover: single file blocked, unknown quota allowed, chunked upload blocked, and two files exceeding combined quota.

@solracsf
solracsf force-pushed the checkQuotaBeforeUpload branch from 114c6ee to 1f866cb Compare April 2, 2026 13:56
@solracsf solracsf changed the title fix(discovery): check quota before upload fix(discovery): check quota before upload (during discovery) Apr 2, 2026
@solracsf
solracsf force-pushed the checkQuotaBeforeUpload branch 3 times, most recently from 091d2a9 to 399b158 Compare April 3, 2026 05:42
@solracsf
solracsf marked this pull request as ready for review April 3, 2026 06:08
@solracsf

solracsf commented Apr 3, 2026

Copy link
Copy Markdown
Member Author

solar-scanner missing is out of scope of this PR
(#9786 should fix it)

@mgallien

mgallien commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

solar-scanner missing is out of scope of this PR (#9786 should fix it)

sorry for the mess
my bad

@mgallien
mgallien force-pushed the checkQuotaBeforeUpload branch from 399b158 to e425898 Compare April 3, 2026 08:35
@solracsf
solracsf force-pushed the checkQuotaBeforeUpload branch from e425898 to dcb2b54 Compare April 3, 2026 09:24
@github-actions

github-actions Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Artifact containing the AppImage: nextcloud-appimage-pr-9777.zip

Digest: sha256:827461c2f2400b34459144223dd6997c57b2811085bc67b6f13ab742cc53fe5f

To test this change/fix you can download the above artifact file, unzip it, and run it.

Please make sure to quit your existing Nextcloud app and backup your data.

@sonarqubecloud

sonarqubecloud Bot commented Apr 3, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
D Maintainability Rating on New Code (required ≥ A)
E Reliability Rating on New Code (required ≥ A)
104 New Code Smells (required ≤ 0)
1 New Bugs (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@solracsf

solracsf commented Apr 3, 2026

Copy link
Copy Markdown
Member Author

Every remaining SonarCloud issues are pre-existing code that SonarQube is classifying as "new" because our modifications shifted line numbers of otherwise untouched code into its diff window.

The Quality Gate cannot be cleared by fixing code in this PR.

solracsf added 2 commits July 27, 2026 14:56
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
@camilasan
camilasan force-pushed the checkQuotaBeforeUpload branch from c72a6c3 to ef71e47 Compare July 27, 2026 12:56
@camilasan
camilasan requested a review from claucambra as a code owner July 27, 2026 12:56
void SyncEngine::slotInsufficientRemoteStorage()
{
auto msg = tr("There is insufficient space available on the server for some uploads.");
auto msg = tr("Upload paused: one or more files exceed your remaining Nextcloud storage quota. "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto msg = tr("Upload paused: one or more files exceed your remaining Nextcloud storage quota. "
const auto msg = tr("Current files upload size exceeds your remaining storage quota."

{
auto msg = tr("There is insufficient space available on the server for some uploads.");
auto msg = tr("Upload paused: one or more files exceed your remaining Nextcloud storage quota. "
"Free up server space or contact your administrator to increase your quota.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Free up server space or contact your administrator to increase your quota.");
"Free up server space or contact your administrator to increase your user quota.");

emit propagator()->insufficientRemoteStorage();
done(SyncFileItem::DetailError, tr("Upload of %1 exceeds the quota for the folder").arg(Utility::octetsToString(_fileToUpload._size)));
done(SyncFileItem::DetailError,
tr("Upload of %1 exceeds %2 of remaining storage quota for this folder")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tr("Upload of %1 exceeds %2 of remaining storage quota for this folder")
tr("Upload of %1 exceeds %2 of remaining storage quota for this folder.")

QCOMPARE(recordFolderA._folderQuota.bytesAvailable, 500);

// add fileNameA to folderA - size < quota in db
// ── Case 1: upload succeeds when size < fresh PROPFIND quota ──

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ── Case 1: upload succeeds when size < fresh PROPFIND quota ──
// Case 1: upload succeeds when size < fresh PROPFIND quota

// add smallFile to folderA - size < quota in db
const QString smallFile("A/smallFile.data");
fakeFolder.localModifier().insert(smallFile, 100);
// ── Case 2: fresh PROPFIND value (0) wins over stale DB (500) ──

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ── Case 2: fresh PROPFIND value (0) wins over stale DB (500) ──
// Case 2: fresh PROPFIND value (0) wins over stale DB (500)

QCOMPARE(syncSpy.findItem(fileBlocked)->_status, SyncFileItem::Status::DetailError);

// create remoteFileA - size > bytes available
// ── Case 3: downloads are not affected by upload quota ──

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ── Case 3: downloads are not affected by upload quota ──
// Case 3: downloads are not affected by upload quota

fakeFolder.localModifier().insert(fileNameB, 0);

// set different quota for folderA - remote change does not change etag yet
// ── Case 4: after quota increase, previously blocked file uploads ──

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ── Case 4: after quota increase, previously blocked file uploads ──
// Case 4: after quota increase, previously blocked file uploads

QCOMPARE(syncSpy.findItem(fileNameD)->_status, SyncFileItem::Status::DetailError);

// create local fileNameE - size < quota in db
// ── Case 6: file within quota succeeds even while another is blocked ──

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ── Case 6: file within quota succeeds even while another is blocked ──
// Case 6: file within quota succeeds even while another is blocked

@camilasan

Copy link
Copy Markdown
Member

This really needs to be tested with nextcloud/server#59398 merged.

@camilasan camilasan self-assigned this Jul 27, 2026
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
D Maintainability Rating on New Code (required ≥ A)
105 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Check for server quota before trying to upload files

3 participants