Issue Description
With a disk quota on an account (maxDiskQuota), FileNode/set never checks it. It keeps creating files after Quota/get already shows used above hardLimit, and it also replaces a file’s blob with a bigger one past the limit. In the same state a WebDAV PUT into the same account is refused with 412, and ContactCard/set returns overQuota. So only the JMAP file path lacks the check, and any user with file storage can go past the limit the admin set, without an upper bound.
Expected Behavior
A FileNode/set create, or an update that changes blobId, fails with overQuota once the quota would be exceeded, the same way WebDAV PUT does.
Actual Behavior
With hardLimit 100000 and three creates of 60000 octets each:
create 1: used before 196 -> created, used after 60388
create 2: used before 60388 -> created, used after 120580
create 3: used before 120580 -> created, used after 180772
WebDAV PUT of 60000 octets in the same state -> 412 <D:quota-not-exceeded/>
The third create starts when the account is already over the limit. Also in the over-limit state:
FileNode/set create, 1000 octets -> created
FileNode/set update blobId, 1000 -> 150000 -> updated (used 270774, hardLimit 100000)
WebDAV overwrite, 60000 -> 70000 -> 412
ContactCard/set create -> overQuota "Account quota exceeded."
Reproduction Steps
- As admin, give a user a disk quota of 100000.
- As that user, call Quota/get. It shows hardLimit 100000 with FileNode in types.
- Upload a 60000-octet blob and create a file from it with FileNode/set.
- Repeat step 3 two more times with new blobs and names. Every create succeeds.
- PUT a 60000-octet file over WebDAV into the same account. It gets 412.
1. ["x:Account/set", {"accountId": "<admin account>", "update": {"<user account>": {"quotas/maxDiskQuota": 100000}}}, "0"]
2. ["Quota/get", {"accountId": "<acc>", "ids": null}, "0"]
<- {"resourceType": "octets", "used": 0, "hardLimit": 100000, "scope": "account",
"types": ["Email", "SieveScript", "FileNode", "CalendarEvent", "ContactCard"]}
3. POST /jmap/upload/<acc>/ (60000 octets, application/octet-stream)
["FileNode/set", {"accountId": "<acc>", "create": {"f": {"name": "big0.bin", "parentId": null, "blobId": "<blobId>"}}}, "0"],
["Quota/get", {"accountId": "<acc>", "ids": null}, "q"]
<- "created": {"f": {"id": "vf"}}
5. PUT /dav/file/<user>/via-dav.bin (60000 octets)
<- HTTP 412 <D:error xmlns:D="DAV:"><D:quota-not-exceeded/></D:error>
Stalwart Version
v0.16.x
Installation Method
Built from source
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
Also on the now deleted 1.0 branch
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on