internal/persistence/persistence.go (view raw)
1package persistence
2
3import (
4 "errors"
5 "fmt"
6 "os"
7)
8
9type Config struct {
10 BlobRoot string
11 MasterkeyFile string
12}
13type Persistence struct {
14 blobRoot string
15 masterkey []byte
16}
17
18func New(config Config) (*Persistence, error) {
19 if config.MasterkeyFile == "" {
20 return nil, errors.New("no masterkey file specified")
21 }
22
23 blobRoot := "./data/blobs"
24 if config.BlobRoot != "" {
25 blobRoot = config.BlobRoot
26 }
27
28 if err := ensureBlobsDir(blobRoot); err != nil {
29 return nil, err
30 }
31
32 // the master key used to derive encryption keys
33 masterkey, err := os.ReadFile(config.MasterkeyFile)
34 if err != nil {
35 return nil, fmt.Errorf("cannot read postern masterkey: %w", err)
36 }
37
38 return &Persistence{
39 blobRoot: blobRoot,
40 masterkey: masterkey,
41 }, nil
42}
43
44func (p *Persistence) BlobRoot() string {
45 return p.blobRoot
46}
47
48func ensureBlobsDir(path string) error {
49 if err := os.MkdirAll(path, 0700); err != nil {
50 return fmt.Errorf("creating blobs directory %s: %w", path, err)
51 }
52 return nil
53}
54
55// CheckDirWritable ensures that the persistence layer can write to the given directory
56func CheckDirWritable(dir string) error {
57 if dir == "" {
58 return errors.New("no path given")
59 }
60 info, err := os.Stat(dir)
61 if err != nil {
62 if os.IsNotExist(err) {
63 return fmt.Errorf("directory does not exist: %s", dir)
64 }
65 return fmt.Errorf("failed to stat directory: %w", err)
66 }
67
68 // Ensure it's a directory
69 if !info.IsDir() {
70 return fmt.Errorf("path is not a directory: %s", dir)
71 }
72
73 // Test writability by creating a temporary file
74 writeTest, err := os.CreateTemp(dir, ".write_test")
75 if err != nil {
76 return fmt.Errorf("directory is not writable: %w", err)
77 }
78 _ = writeTest.Close()
79
80 // Clean up the test file
81 err = os.Remove(writeTest.Name())
82 if err != nil {
83 return fmt.Errorf("directory is writable but not deletable: %w", err)
84 }
85
86 return nil
87}