objops

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package objops implements the transport-agnostic object operations (Put/Get/Head/Delete) shared by the HTTP S3 handler and the native proto handler. It is the single place where:

  • token-level authorization is applied (via auth.Authorize)
  • bucket-policy deny overlays are evaluated (via auth.EvaluatePolicyDeny)
  • metadata is written / read / deleted against meta.DB
  • bytes are streamed to/from the physical store

Both transports must route through this package. Do not duplicate these rules in the handlers.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrBucketNotFound: the named bucket does not exist.
	ErrBucketNotFound = errors.New("objops: bucket not found")
	// ErrObjectNotFound: the named object does not exist in the bucket.
	ErrObjectNotFound = errors.New("objops: object not found")
	// ErrPolicyDenied: bucket-policy evaluator returned a deny match. This is
	// distinct from a token-level authorization failure (see ErrAccessDenied).
	ErrPolicyDenied = errors.New("objops: access denied by bucket policy")
	// ErrAccessDenied: token-level authorization failed.
	ErrAccessDenied = errors.New("objops: access denied")
	// ErrBadPolicy: the bucket policy JSON could not be unmarshalled. Treated
	// as deny (fail-closed) — never as allow.
	ErrBadPolicy = errors.New("objops: malformed bucket policy")
)

Exported errors so handlers can map them to transport-specific status codes.

Functions

This section is empty.

Types

type Identity

type Identity struct {
	TokenID   string
	AccountID string
	SourceIP  string
	// Action is the meta.Action* constant for the operation (e.g.
	// meta.ActionObjectGet). This is what the bucket policy evaluator matches
	// against — the actions field of policy statements uses the same strings.
	Action string
}

Identity carries the authenticated caller context needed for policy evaluation. TokenID may be empty for anonymous reads on public-read buckets (the handler will not build an Identity in that case unless asked to).

type PutOptions

type PutOptions struct {
	// UserMetadata holds x-amz-meta-* headers (HTTP) or a decoded metadata map
	// (native proto). Keys should already be lower-cased and sanitized.
	UserMetadata map[string]string
}

PutOptions carry per-request metadata that doesn't fit in the required args.

type PutResult

type PutResult struct {
	Object *meta.Object
}

PutResult is returned by PutObject so both transports can produce the same response shape (ETag / checksum / size).

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the transport-agnostic object-operations service.

func New

func New(db *meta.DB, st *store.Store, log *slog.Logger) *Service

New constructs a Service. db, st, and log must all be non-nil.

func (*Service) DeleteObject

func (s *Service) DeleteObject(
	_ context.Context,
	token *meta.Token,
	bucketName, key string,
	identity Identity,
) error

DeleteObject marks the object deleted in metadata and GCs its physical file. Returns nil if the object did not exist (S3 semantics: DELETE is idempotent).

func (*Service) GetObject

func (s *Service) GetObject(
	_ context.Context,
	token *meta.Token,
	bucketName, key string,
	w io.Writer,
	identity Identity,
) (*meta.Object, error)

GetObject resolves metadata + opens the stored file and streams it to w via io.Copy. On Linux, when w is a *net.TCPConn or an io.ReaderFrom that wraps one (e.g. api.statusWriter), the kernel's sendfile(2) takes over and the bytes never touch userspace. For that reason we do NOT re-hash on read here — the scrubber owns integrity verification (maintenance/scrub.go).

The object is returned so callers can set transport-specific response headers (ETag, Content-Length, Content-Type, Last-Modified, x-amz-meta-*).

If w is nil, GetObject returns the Object without streaming bytes — this is HEAD semantics. Callers should prefer HeadObject for clarity.

func (*Service) HeadObject

func (s *Service) HeadObject(
	_ context.Context,
	token *meta.Token,
	bucketName, key string,
	identity Identity,
) (*meta.Object, error)

HeadObject returns object metadata only — no body stream.

func (*Service) OpenObjectFile

func (s *Service) OpenObjectFile(obj *meta.Object) (*os.File, error)

OpenObjectFile opens the physical file at obj.LocationRef for direct transport use. The HTTP handler needs Seek for Range support; the proto handler needs a reader for its frame writer. Caller must Close the file.

This is the one place outside GetObject where the store is opened by location_ref — it skips the auth check (the caller already has an authorized *meta.Object in hand). Do NOT export this to callers that received the Object from an untrusted source.

func (*Service) PutObject

func (s *Service) PutObject(
	_ context.Context,
	token *meta.Token,
	bucketName, key, contentType string,
	body io.Reader,
	opts PutOptions,
	identity Identity,
) (*meta.Object, error)

PutObject writes body to the store, commits metadata, and returns the new object. On overwrite the previous version's physical file is GC'd. On metadata commit failure the freshly-written file is cleaned up.

The caller is responsible for setting Content-Length / Content-Type headers on HTTP responses — PutObject only fills *meta.Object and returns it.

Jump to

Keyboard shortcuts

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