data

package
v0.10.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 13 Imported by: 0

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

View Source
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

View Source
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.

View Source
var ErrInvalidPath = errors.New("invalid path")

ErrInvalidPath is returned for any rel path that fails sanitization.

Functions

func AppDataDir

func AppDataDir(root, slug string) string

AppDataDir returns the absolute (or root-relative) path of slug's data dir.

func CleanupUploadTemp

func CleanupUploadTemp(dataDir string, maxAge time.Duration) error

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

func Delete(dataDir, rel string) error

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

func DirSize(dataDir string) (int64, error)

DirSize returns the total byte size of all regular files under dataDir, excluding the UploadTempDir subtree.

func ProjectedSize

func ProjectedSize(used, existingDestSize, incoming int64) int64

ProjectedSize returns the on-disk total after replacing a file of existingDestSize with incoming bytes (existingDestSize=0 for new files).

func QuotaCheck

func QuotaCheck(used, existingDestSize, incoming, quotaBytes int64) error

QuotaCheck returns nil when the projected size fits inside quotaBytes. quotaBytes <= 0 disables the check.

func SafeJoin added in v0.2.2

func SafeJoin(dataDir, rel string) (string, error)

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

func SanitizeRelPath(rel string) (string, error)

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

func List(dataDir string, maxEntries int) ([]FileInfo, error)

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

func Put(dataDir, rel string, body io.Reader, size int64) (FileInfo, error)

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

type QuotaError struct {
	QuotaBytes     int64
	UsedBytes      int64
	WouldBeBytes   int64
	RemainingBytes int64
}

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL