How Zakirullin Files Enforces Storage Quota Limits Per User
Zakirullin Files limits user data by validating file write operations against a configurable per-user quota, while allowing administrators to exempt specific user IDs from storage restrictions.
The zakirullin/files.md repository provides a markdown file server with built-in storage management capabilities. The quota system operates through a three-layer architecture: configuration loading, user-specific exemptions, and runtime write validation.
Configuring the Default Quota Limit
The storage quota configuration resides in server/config/config.go, where the application defines default limits and environment variable overrides.
// server/config/config.go
StorageQuotaKB int64 `default:"1024" envconfig:"STORAGE_QUOTA_KB"` // 1 MiB
UnlimitedQuotaIDs string `envconfig:"UNLIMITED_QUOTA_IDS"`
The StorageQuotaKB field defaults to 1024 (1 MiB) but can be customized at startup using the STORAGE_QUOTA_KB environment variable. The configuration loads via config.LoadBotConfig() during server initialization.
To set a custom quota before starting the server:
export STORAGE_QUOTA_KB=5120 # 5 MiB per user
go run ./cmd/server
Exempting Users from Quota Restrictions
Administrators can designate specific users as unlimited by populating the UnlimitedQuotaIDs environment variable with comma-separated user identifiers.
The exemption logic lives in server/fs/quota.go:
// server/fs/quota.go
func isUnlimitedQuota(userID int64, unlimitedIDs string) bool { … }
When initializing a user's filesystem in server/fs/fs.go, the newUserFS function checks this status and sets quotaKB to 0 for unlimited users, effectively disabling storage checks:
// server/fs/fs.go (newUserFS)
quotaKB := config.ServerCfg.StorageQuotaKB
if isUnlimitedQuota(userID, config.ServerCfg.UnlimitedQuotaIDs) {
quotaKB = 0
}
To exempt specific users:
export UNLIMITED_QUOTA_IDS="1234,5678"
go run ./cmd/server
Runtime Quota Validation During Write Operations
The enforcement mechanism activates during every file write through the Write method in server/fs/fs.go. The system calculates storage consumption lazily using storageUsed and caches results in storageQuotaCache.
Before committing data, the code invokes checkQuota from server/fs/quota.go:
// server/fs/quota.go
func checkQuota(rootPath string, backend afero.Fs, quotaKB int64, contentSize int64) error {
if quotaKB <= 0 { return nil } // unlimited
if storageUsed(rootPath, backend)+contentSize > quotaKB*1024 {
return ErrQuotaExceeded
}
return nil
}
The Write method calculates the size delta (newSize - oldSize) and validates against the quota:
// server/fs/fs.go (Write)
newSize := int64(len(content))
if err := checkQuota(fs.rootPath, fs.backend, fs.quotaKB, newSize-oldSize); err != nil {
return err
}
If validation passes, recordQuotaUsage updates the cached total. If the quota would be exceeded, the operation aborts immediately with ErrQuotaExceeded.
Handling Quota Exceeded Errors
When applications attempt to write data that breaches the configured limit, the filesystem returns a specific error that can be handled programmatically:
package main
import (
"fmt"
"strings"
"github.com/zakirullin/files.md/server/fs"
)
func main() {
userFS, err := fs.NewUserFS(1234)
if err != nil {
panic(err)
}
content := strings.Repeat("x", 2*1024*1024) // 2 MiB
if err := userFS.Write(fs.DirJournal, "BigNote.md", content); err != nil {
fmt.Printf("Write failed: %v\n", err) // ErrQuotaExceeded if quota is 1 MiB
} else {
fmt.Println("Write succeeded")
}
}
Summary
- Configuration: Default 1 MiB quotas are defined in
server/config/config.goand overridable viaSTORAGE_QUOTA_KB. - Exemptions: Specific user IDs listed in
UNLIMITED_QUOTA_IDSbypass all restrictions through theisUnlimitedQuotahelper. - Enforcement: The
checkQuotafunction inserver/fs/quota.govalidates every write operation against cached storage totals, aborting withErrQuotaExceededwhen limits are breached. - Delta Calculation: Quota checks account for file modifications by comparing new content size against existing file size.
Frequently Asked Questions
How do I increase the storage quota for all users?
Set the STORAGE_QUOTA_KB environment variable to the desired kilobyte limit before starting the server. For example, export STORAGE_QUOTA_KB=10240 sets a 10 MiB quota per user. The configuration loads at startup via config.LoadBotConfig().
Can I give specific users unlimited storage while keeping limits for others?
Yes. Add the user IDs to the UNLIMITED_QUOTA_IDS environment variable as comma-separated values. The newUserFS function in server/fs/fs.go sets quotaKB = 0 for these users, causing checkQuota to skip validation entirely.
What happens when a user exceeds their storage quota?
The Write method returns ErrQuotaExceeded immediately before writing data to disk. The operation aborts without modifying the filesystem, and the application receives the error to handle appropriately.
How does the system calculate current storage usage?
The storageUsed function calculates consumption by walking the user's root directory, with results cached in storageQuotaCache to minimize filesystem traversal during frequent write operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →