Documentation
¶
Overview ¶
Package data implements the persistent per-app storage area exposed via `PUT/DELETE/GET /api/apps/:slug/data[/*path]` and the `shinyhub data` CLI.
Index ¶
- Constants
- Variables
- func AppDataDir(root, slug string) string
- func CleanupUploadTemp(dataDir string, maxAge time.Duration) error
- func Delete(dataDir, rel string) error
- func DirSize(dataDir string) (int64, error)
- func ProjectedSize(used, existingDestSize, incoming int64) int64
- func QuotaCheck(used, existingDestSize, incoming, quotaBytes int64) error
- func SafeJoin(dataDir, rel string) (string, error)
- func SanitizeRelPath(rel string) (string, error)
- type FileInfo
- type QuotaError
Constants ¶
const ( // ReservedPrefix marks platform-owned filenames inside a data dir. The // `.shinyhub-upload-tmp/` directory uses this prefix. ReservedPrefix = ".shinyhub-" // UploadTempDir is the per-app subdir under which atomic-rename tempfiles // are written. UploadTempDir = ".shinyhub-upload-tmp" )
Variables ¶
var ( ErrTooManyFiles = errors.New("too many files") ErrNotAFile = errors.New("not a regular file") ErrFileNotFound = errors.New("file not found") )
Sentinel errors returned by List, Delete, and DirSize.
var ErrInvalidPath = errors.New("invalid path")
ErrInvalidPath is returned for any rel path that fails sanitization.
Functions ¶
func AppDataDir ¶
AppDataDir returns the absolute (or root-relative) path of slug's data dir.
func CleanupUploadTemp ¶
CleanupUploadTemp removes entries inside the UploadTempDir that are older than maxAge. Only immediate children are inspected (no recursion). Errors from individual removals are collected and joined.
func Delete ¶
Delete removes the file at rel inside dataDir. It returns ErrFileNotFound if the path does not exist, ErrNotAFile if the path is a directory or non-regular entry, and ErrInvalidPath (via SanitizeRelPath) for traversal attempts or reserved prefixes.
func DirSize ¶
DirSize returns the total byte size of all regular files under dataDir, excluding the UploadTempDir subtree.
func ProjectedSize ¶
ProjectedSize returns the on-disk total after replacing a file of existingDestSize with incoming bytes (existingDestSize=0 for new files).
func QuotaCheck ¶
QuotaCheck returns nil when the projected size fits inside quotaBytes. quotaBytes <= 0 disables the check.
func SafeJoin ¶ added in v0.2.2
SafeJoin resolves dataDir/rel to an absolute file path while refusing any symlink traversal: if any existing segment along the path (parent or destination) is a symbolic link, ErrInvalidPath is returned. The dataDir itself is trusted (operators may legitimately point app_data_dir at a symlinked volume); only entries created underneath it are checked.
Pass an already-cleaned relative path (output of SanitizeRelPath). Missing trailing segments are allowed — they're what Put will create — but a non-directory in the middle of the path is rejected.
NOTE on TOCTOU: the check is followed by a separate write/unlink syscall, so a determined attacker who can create symlinks inside the data dir concurrently with the request can still race past this guard. Closing that fully needs openat2(RESOLVE_NO_SYMLINKS), which is Linux-specific. This guard catches the realistic case (symlink planted in advance) and matches the security posture of similar Go services.
func SanitizeRelPath ¶
SanitizeRelPath validates the user-supplied rel path inside a data dir. Returns the cleaned, forward-slash relative path on success; ErrInvalidPath otherwise. Caller is responsible for any further `os.Lstat` per-segment symlink checks during traversal.
Types ¶
type FileInfo ¶
type FileInfo struct {
Path string `json:"path"`
Size int64 `json:"size"`
SHA256 string `json:"sha256,omitempty"`
ModifiedAt int64 `json:"modified_at,omitempty"`
}
FileInfo describes a single entry returned by Put or List.
func List ¶
List returns all regular files under dataDir, sorted by path, excluding the UploadTempDir subtree. It returns ErrTooManyFiles if the number of entries would exceed maxEntries. A missing dataDir is treated as empty.
func Put ¶
Put streams body into <dataDir>/<rel> via an atomic rename. It computes SHA-256 in the same pass and returns the resulting FileInfo.
dataDir MUST already point at the per-app data dir (caller resolves via AppDataDir). rel is sanitized through SanitizeRelPath.
The size parameter is accepted for API symmetry with quota-aware callers; quota enforcement happens at the HTTP boundary before Put is called.
type QuotaError ¶
QuotaError is returned by QuotaCheck when the projected size exceeds the configured quota. Handlers map this to HTTP 413.
func (*QuotaError) Error ¶
func (e *QuotaError) Error() string