Skip to content

Storage quota never accounts for existing usage, so it does not limit anything #15

Description

@TheMeinerLP

What happens

A configured storage quota never accounts for what is already stored, so it only ever rejects a single upload larger than the entire limit. An instance configured with a 10 GB quota accepts 10 GB, then another 10 GB, and keeps going.

Why

StorageProvider.usage() reports -1 on both backends.

FileSystemStorageProvider.usage() is getFileSize(Location.empty()), and getFileSize returns -1 for a DIRECTORY:

override fun getFileSize(location: Location): Result<Long, ErrorResponse> =
    location.resolveWithRootDirectory()
        .exists()
        .map {
            when (it.type()) {
                FILE -> it.fileSize()
                DIRECTORY -> -1
            }
        }

Nothing walks the tree, so a repository root has no size to report.

Both quota providers subtract that value:

// FixedQuota
override fun canHold(contentLength: Long): Result<Long, ErrorResponse> =
    usage().map { usage -> maxSize - usage }.filter({ contentLength <= it }, { ... })

// PercentageQuota
usage().map { usage -> (rootDirectory.fileStore().usableSpace * maxPercentage).toLong() - usage }

With usage() == -1 the available space is maxSize + 1, constant, regardless of what the repository already holds.

S3StorageProvider is worse: usage() returns ok(-1) as well, but canHold is overridden to ok(Long.MAX_VALUE), so an S3 backed repository has no quota at all.

Impact

Operators who set a quota believe a limit is enforced when it is not. Nobody gains a permission they did not have, so this is a feature that silently does nothing rather than a way in, but a disk can still fill up against an explicit configuration.

Where it lives

  • reposilite-backend/src/main/kotlin/com/reposilite/storage/filesystem/FileSystemStorageProvider.kt, getFileSize and usage
  • reposilite-backend/src/main/kotlin/com/reposilite/storage/filesystem/FileSystemQuotaProviders.kt, FixedQuota.canHold and PercentageQuota.canHold
  • reposilite-backend/src/main/kotlin/com/reposilite/storage/s3/S3StorageProvider.kt, usage and canHold

What a fix has to consider

A correct usage() is a recursive directory walk on the filesystem and a paginated listing on S3, so it cannot run per request. It needs a cached value, refreshed on a schedule and invalidated on deploy, or the quota check has to move somewhere it can afford the cost. Whichever way it goes, canHold should fail loudly rather than silently pass when the backend cannot report a usage.

Notes

Found while reading this code for an unrelated change. Upstream Reposilite carries the same implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions