This repository has no description
1package storage
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "os"
8 "path/filepath"
9)
10
11type Disk struct {
12 root string
13}
14
15func NewDisk(root string) (*Disk, error) {
16 if root == "" {
17 return nil, fmt.Errorf("storage: disk backend requires a directory")
18 }
19 if err := os.MkdirAll(root, 0o755); err != nil {
20 return nil, fmt.Errorf("storage: create disk root: %w", err)
21 }
22 return &Disk{root: filepath.Clean(root)}, nil
23}
24
25func (d *Disk) path(key string) (string, error) {
26 if err := ValidateKey(key); err != nil {
27 return "", err
28 }
29 return filepath.Join(d.root, filepath.FromSlash(key)), nil
30}
31
32func (d *Disk) Get(_ context.Context, key string) (io.ReadCloser, error) {
33 p, err := d.path(key)
34 if err != nil {
35 return nil, err
36 }
37 f, err := os.Open(p)
38 if err != nil {
39 if os.IsNotExist(err) {
40 return nil, ErrNotExist
41 }
42 return nil, fmt.Errorf("storage: get %q: %w", key, err)
43 }
44 return f, nil
45}
46
47func (d *Disk) Put(_ context.Context, key string, r io.Reader) error {
48 p, err := d.path(key)
49 if err != nil {
50 return err
51 }
52 if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
53 return fmt.Errorf("storage: put %q: %w", key, err)
54 }
55 // dont let readers see a partial object
56 tmp, err := os.CreateTemp(filepath.Dir(p), ".tmp-*")
57 if err != nil {
58 return fmt.Errorf("storage: put %q: %w", key, err)
59 }
60 tmpName := tmp.Name()
61 defer os.Remove(tmpName)
62 if _, err := io.Copy(tmp, r); err != nil {
63 _ = tmp.Close()
64 return fmt.Errorf("storage: put %q: %w", key, err)
65 }
66 if err := tmp.Close(); err != nil {
67 return fmt.Errorf("storage: put %q: %w", key, err)
68 }
69 if err := os.Rename(tmpName, p); err != nil {
70 return fmt.Errorf("storage: put %q: %w", key, err)
71 }
72 return nil
73}
74
75func (d *Disk) Delete(_ context.Context, key string) error {
76 p, err := d.path(key)
77 if err != nil {
78 return err
79 }
80 if err := os.Remove(p); err != nil {
81 if os.IsNotExist(err) {
82 return nil
83 }
84 return fmt.Errorf("storage: delete %q: %w", key, err)
85 }
86 return nil
87}