mirror of
https://github.com/Luzifer/preserve.git
synced 2024-11-08 22:30:05 +00:00
105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
|
// Package local implements a storage.Storage backend for local file storage
|
||
|
package local
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"path"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/Luzifer/preserve/pkg/storage"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
)
|
||
|
|
||
|
const storageLocalDirPermission = 0o700
|
||
|
|
||
|
// Storage implements the storage.Storage interface for local file storage
|
||
|
type Storage struct {
|
||
|
basePath string
|
||
|
}
|
||
|
|
||
|
// New returns a new local file storage
|
||
|
func New(basePath string) Storage { return Storage{basePath} }
|
||
|
|
||
|
// GetFile implements the storage.Storage GetFile method
|
||
|
func (s Storage) GetFile(_ context.Context, cachePath string) (io.ReadSeekCloser, error) {
|
||
|
cachePath = path.Join(s.basePath, cachePath)
|
||
|
rsc, err := os.Open(cachePath) //#nosec:G304 // Safe source of variable
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("opening cache file: %w", err)
|
||
|
}
|
||
|
|
||
|
return rsc, nil
|
||
|
}
|
||
|
|
||
|
// LoadMeta implements the storage.Storage LoadMeta method
|
||
|
func (s Storage) LoadMeta(_ context.Context, cachePath string) (*storage.Meta, error) {
|
||
|
cachePath = path.Join(s.basePath, cachePath)
|
||
|
|
||
|
metaPath := strings.Join([]string{cachePath, "meta"}, ".")
|
||
|
if _, err := os.Stat(metaPath); err != nil {
|
||
|
return nil, fmt.Errorf("getting cache file stat: %w", err)
|
||
|
}
|
||
|
|
||
|
f, err := os.Open(metaPath) //#nosec:G304 // Safe source of variable
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "open metadata file")
|
||
|
}
|
||
|
defer func() {
|
||
|
if err := f.Close(); err != nil {
|
||
|
logrus.WithError(err).Error("closing metadata file (leaked fd)")
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
out := new(storage.Meta)
|
||
|
return out, errors.Wrap(
|
||
|
json.NewDecoder(f).Decode(out),
|
||
|
"decode metadata file",
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// StoreFile implements the storage.Storage StoreFile method
|
||
|
func (s Storage) StoreFile(_ context.Context, cachePath string, metadata *storage.Meta, data io.Reader) (err error) {
|
||
|
cachePath = path.Join(s.basePath, cachePath)
|
||
|
|
||
|
if err = os.MkdirAll(path.Dir(cachePath), storageLocalDirPermission); err != nil {
|
||
|
return errors.Wrap(err, "create cache dir")
|
||
|
}
|
||
|
|
||
|
f, err := os.Create(cachePath) //#nosec:G304 // Safe source of variable
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "create cache file")
|
||
|
}
|
||
|
defer func() {
|
||
|
if err := f.Close(); err != nil {
|
||
|
logrus.WithError(err).Error("closing cache file (leaked fd)")
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
if _, err := io.Copy(f, data); err != nil {
|
||
|
return errors.Wrap(err, "write cache file")
|
||
|
}
|
||
|
|
||
|
f, err = os.Create(strings.Join([]string{cachePath, "meta"}, "."))
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "create cache meta file")
|
||
|
}
|
||
|
defer func() {
|
||
|
if err := f.Close(); err != nil {
|
||
|
logrus.WithError(err).Error("closing metadata file (leaked fd)")
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
metadata.LastCached = time.Now()
|
||
|
|
||
|
return errors.Wrap(
|
||
|
json.NewEncoder(f).Encode(metadata),
|
||
|
"write cache meta file",
|
||
|
)
|
||
|
}
|