-
Notifications
You must be signed in to change notification settings - Fork 0
/
storage.go
146 lines (125 loc) · 3.14 KB
/
storage.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package scratch
import (
"fmt"
"os"
"path"
"time"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
)
const (
StorageDirName = "cache"
KeyFileName = "key.bin"
ContentFileName = "content"
)
type storage string
func Storage(base string) storage {
return storage(path.Join(base, StorageDirName))
}
func (s storage) CheckPwForPath(pw []byte, to string) bool {
k, err := s.LoadKeyForPath(to)
if err != nil && len(pw) > 0 {
return false
}
return bcrypt.CompareHashAndPassword(k, pw) == nil
}
func (s storage) SaveKeyForPath(key []byte, to string) error {
to = path.Join(string(s), to, KeyFileName)
enc, err := bcrypt.GenerateFromPassword(key, bcrypt.DefaultCost)
if err != nil {
return err
}
return writeToPath(to, enc)
}
func (s storage) LoadKeyForPath(from string) ([]byte, error) {
from = path.Join(string(s), from, KeyFileName)
return loadFromPath(from)
}
func (s storage) SavePath(content, to string) error {
to = path.Join(string(s), to, ContentFileName)
return writeToPath(to, []byte(content))
}
func (s storage) DeletePath(what string) error {
content := path.Join(string(s), what, ContentFileName)
if err := os.RemoveAll(content); err != nil {
return err
}
key := path.Join(string(s), what, KeyFileName)
if err := os.RemoveAll(key); err != nil {
return err
}
return nil
}
func (s storage) LoadPath(from string) (string, error) {
from = path.Join(string(s), from, ContentFileName)
content, err := loadFromPath(from)
if err != nil {
return "", err
}
return string(content), nil
}
func (s storage) ModTimePath(from string) (time.Time, error) {
from = path.Join(string(s), from, ContentFileName)
fi, err := os.Stat(from)
if err != nil {
return (time.Time{}).UTC(), err
}
return fi.ModTime().UTC(), nil
}
func mkDirIfNotExists(p string) error {
fi, err := os.Stat(p)
if err != nil && os.IsNotExist(err) {
err = os.MkdirAll(p, os.ModeDir|os.ModePerm|0700)
}
if err != nil {
return err
}
fi, err = os.Stat(p)
if err != nil {
return err
} else if !fi.IsDir() {
return errors.Errorf("path exists, and is not a folder %q", p)
}
return nil
}
func createOrOpenFile(p string) (*os.File, error) {
if err := mkDirIfNotExists(path.Dir(p)); err != nil {
return nil, err
}
return os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
}
func writeToPath(to string, content []byte) error {
f, err := createOrOpenFile(to)
if err != nil {
return err
}
defer f.Close()
content, err = encodeFn(content)
if err != nil {
return fmt.Errorf("could not marshal metadata: %w", err)
}
var wrote int
wrote, err = f.Write(content)
if err != nil {
return fmt.Errorf("could not store encoded object: %w", err)
}
if wrote != len(content) {
return fmt.Errorf("failed writing full object: %w", err)
}
return nil
}
func encodeFn(content []byte) ([]byte, error) {
return content, nil
}
var decodeFn = encodeFn
func loadFromPath(from string) ([]byte, error) {
content, err := os.ReadFile(from)
if err != nil {
return nil, fmt.Errorf("could not read from path %q: %w", from, err)
}
content, err = decodeFn(content)
if err != nil {
return nil, fmt.Errorf("could not marshal metadata: %w", err)
}
return content, nil
}