-
Notifications
You must be signed in to change notification settings - Fork 19
/
store_namespacer.go
139 lines (113 loc) · 3.79 KB
/
store_namespacer.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
package groot
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"sort"
"code.cloudfoundry.org/grootfs/store"
errorspkg "github.com/pkg/errors"
)
const NamespaceFilename = "namespace.json"
type StoreNamespacer struct {
storePath string
}
type mappings struct {
UIDMappings []string `json:"uid-mappings"`
GIDMappings []string `json:"gid-mappings"`
}
func NewStoreNamespacer(storePath string) *StoreNamespacer {
return &StoreNamespacer{
storePath: storePath,
}
}
func (n *StoreNamespacer) ApplyMappings(uidMappings, gidMappings []IDMappingSpec) error {
namespaceFilePath := n.namespaceFilePath()
_, err := os.Stat(namespaceFilePath)
if err != nil {
if os.IsNotExist(err) {
return n.write(uidMappings, gidMappings)
}
}
return n.validateNamespace(namespaceFilePath, uidMappings, gidMappings)
}
func (n *StoreNamespacer) Read() (IDMappings, error) {
mappingsFromFile := mappings{}
jsonBytes, err := ioutil.ReadFile(n.namespaceFilePath())
if err != nil {
return IDMappings{}, errorspkg.Wrap(err, "reading namespace file")
}
if err := json.Unmarshal(jsonBytes, &mappingsFromFile); err != nil {
return IDMappings{}, errorspkg.Wrap(err, "invalid namespace file")
}
uidMappings, err := n.parseIDMappings(mappingsFromFile.UIDMappings)
if err != nil {
return IDMappings{}, errorspkg.Wrap(err, "invalid uid mappings format")
}
gidMappings, err := n.parseIDMappings(mappingsFromFile.GIDMappings)
if err != nil {
return IDMappings{}, errorspkg.Wrap(err, "invalid gid mappings format")
}
return IDMappings{
UIDMappings: uidMappings,
GIDMappings: gidMappings,
}, nil
}
func (n *StoreNamespacer) write(uidMappings, gidMappings []IDMappingSpec) error {
namespaceStore, err := os.Create(n.namespaceFilePath())
if err != nil {
return errorspkg.Wrap(err, "creating namespace file")
}
defer namespaceStore.Close()
namespace := mappings{
UIDMappings: n.normalizeMappings(uidMappings),
GIDMappings: n.normalizeMappings(gidMappings),
}
if err := os.Chmod(namespaceStore.Name(), 0755); err != nil {
return errorspkg.Wrap(err, "failed to chmod namespace file")
}
return json.NewEncoder(namespaceStore).Encode(namespace)
}
func (n *StoreNamespacer) validateNamespace(namespaceFilePath string, uidMappings, gidMappings []IDMappingSpec) error {
namespaceStore, err := os.Open(namespaceFilePath)
if err != nil {
return errorspkg.Wrap(err, "opening namespace file")
}
defer namespaceStore.Close()
var namespace mappings
if err := json.NewDecoder(namespaceStore).Decode(&namespace); err != nil {
return errorspkg.Wrapf(err, "reading namespace file %s", namespaceStore.Name())
}
if !reflect.DeepEqual(namespace.UIDMappings, n.normalizeMappings(uidMappings)) {
return errorspkg.New("provided UID mappings do not match those already configured in the store")
}
if !reflect.DeepEqual(namespace.GIDMappings, n.normalizeMappings(gidMappings)) {
return errorspkg.New("provided GID mappings do not match those already configured in the store")
}
return nil
}
func (n *StoreNamespacer) namespaceFilePath() string {
return filepath.Join(n.storePath, store.MetaDirName, NamespaceFilename)
}
func (n *StoreNamespacer) normalizeMappings(mappings []IDMappingSpec) []string {
stringMappings := []string{}
for _, mapping := range mappings {
stringMappings = append(stringMappings, fmt.Sprintf("%d:%d:%d", mapping.NamespaceID, mapping.HostID, mapping.Size))
}
sort.Strings(stringMappings)
return stringMappings
}
func (n *StoreNamespacer) parseIDMappings(args []string) ([]IDMappingSpec, error) {
mappings := []IDMappingSpec{}
for _, v := range args {
var mapping IDMappingSpec
_, err := fmt.Sscanf(v, "%d:%d:%d", &mapping.NamespaceID, &mapping.HostID, &mapping.Size)
if err != nil {
return nil, err
}
mappings = append(mappings, mapping)
}
return mappings, nil
}