-
Notifications
You must be signed in to change notification settings - Fork 28
/
persistentstore.go
206 lines (188 loc) · 6.07 KB
/
persistentstore.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mounttablelib
import (
"encoding/json"
"io"
"os"
"path"
"strings"
"sync"
"v.io/v23/context"
"v.io/x/ref/internal/logger"
)
type store struct {
l sync.Mutex
mt *mountTable
dir string
enc *json.Encoder
f *os.File
}
type storeElement struct {
N string // Name of affected node
V VersionedPermissions
D bool // True if the subtree at N has been deleted
C string // Creator
}
// newPersistentStore will read the permissions log from the directory and apply them to the
// in memory tree. It will then write a new file from the in memory tree and any new permission
// changes will be appened to this file. By writing into a new file, we effectively compress
// the permissions file since any set permissions that have been deleted or overwritten will be
// lost.
//
// The code manages three files in the directory 'dir':
//
// persistent.permslog - the log of permissions. A new log entry is added with each SetPermissions or
// Delete RPC.
// tmp.permslog - a temporary file created whenever we restart. Once we write the current state into it,
// it will be renamed persistent.perms becoming the new log.
// old.permslog - the previous version of persistent.perms. This is left around primarily for debugging
// and as an emergency backup.
func newPersistentStore(ctx *context.T, mt *mountTable, dir string) persistence { //nolint:gocyclo
s := &store{mt: mt, dir: dir}
file := path.Join(dir, "persistent.permslog")
tmp := path.Join(dir, "tmp.permslog")
old := path.Join(dir, "old.permslog")
// If the permissions file doesn't exist, try renaming the temporary one.
f, err := os.Open(file)
if err != nil {
if !os.IsNotExist(err) {
logger.Global().Fatalf("cannot open %s: %s", file, err)
}
if _, err := os.Stat(tmp); err == nil {
if err := os.Rename(tmp, file); err != nil {
logger.Global().Fatalf("cannot rename %s to %s: %s", tmp, file, err)
}
}
if f, err = os.Open(file); err != nil && !os.IsNotExist(err) {
logger.Global().Fatalf("cannot open %s: %s", file, err)
}
} else {
os.Remove(tmp)
}
// Parse the permissions file and apply it to the in memory tree.
if f != nil {
if err := s.parseLogFile(ctx, f); err != nil {
f.Close()
// Log the error but keep going. There's not much else we can do.
logger.Global().Infof("parsing old persistent permissions file %s: %s", file, err)
}
f.Close()
}
// Write the permissions to a temporary file. This compresses
// the file since it writes out only the end state.
f, err = os.OpenFile(tmp, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
if err != nil {
// Log the error but keep going, don't compress, just append to the current file.
logger.Global().Infof("can't rewrite persistent permissions file %s: %s", file, err)
if f, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600); err != nil {
logger.Global().Fatalf("can't append to log %s: %s", file, err)
}
if _, err := f.Seek(0, 2); err != nil {
logger.Global().Fatalf("can't seek to end of %s: %s", file, err)
}
s.enc = json.NewEncoder(f)
return s
}
s.enc = json.NewEncoder(f)
if err := s.depthFirstPersist(mt.root, ""); err != nil {
ctx.Infof("depthFirstPersist of %v: %v", mt.root, err)
}
f.Close()
// Switch names and remove the old file.
if err := os.Remove(old); err != nil {
ctx.Infof("removing %s: %s", old, err)
}
if err := os.Rename(file, old); err != nil {
ctx.Infof("renaming %s to %s: %s", file, old, err)
}
if err := os.Rename(tmp, file); err != nil {
ctx.Fatalf("renaming %s to %s: %s", tmp, file, err)
}
// Reopen the new log file. We could have just kept around the encoder used
// to create it but that assumes that, after the Rename above, the s.f still
// points to the same file. Only true on Unix like file systems.
f, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
ctx.Fatalf("can't open %s: %s", file, err)
}
if _, err := f.Seek(0, 2); err != nil {
ctx.Fatalf("can't seek to end of %s: %s", file, err)
}
s.enc = json.NewEncoder(f)
return s
}
// parseLogFile reads a file and parses the contained VersionedPermissions .
func (s *store) parseLogFile(ctx *context.T, f *os.File) error {
if f == nil {
return nil
}
ctx.VI(2).Infof("parseLogFile(%s)", f.Name())
mt := s.mt
decoder := json.NewDecoder(f)
cc := &callContext{ctx: ctx,
create: true,
ignorePerms: true,
ignoreLimits: true,
}
for {
var e storeElement
if err := decoder.Decode(&e); err != nil {
if err == io.EOF {
break
}
return err
}
elems := strings.Split(e.N, "/")
cc.creator = e.C
n, err := mt.findNode(cc, elems, nil, nil)
if n == nil {
continue
}
if err == nil {
if e.D {
mt.deleteNode(n.parent, elems[len(elems)-1])
ctx.VI(2).Infof("deleted %s", e.N)
} else {
n.vPerms = &e.V
n.explicitPermissions = true
ctx.VI(2).Infof("added versions permissions %v to %s", e.V, e.N)
}
}
n.parent.Unlock()
n.Unlock()
}
return nil
}
// depthFirstPersist performs a recursive depth first traversal logging any explicit permissions.
// Doing this immediately after reading in a log file effectively compresses the log file since
// any duplicate or deleted entries disappear.
func (s *store) depthFirstPersist(n *node, name string) error {
if n.explicitPermissions {
if err := s.persistPerms(name, n.creator, n.vPerms); err != nil {
return err
}
}
for nodeName, c := range n.children {
return s.depthFirstPersist(c, path.Join(name, nodeName))
}
return nil
}
// persistPerms appends a changed permission to the log.
func (s *store) persistPerms(name, creator string, vPerms *VersionedPermissions) error {
s.l.Lock()
defer s.l.Unlock()
e := storeElement{N: name, V: *vPerms, C: creator}
return s.enc.Encode(&e)
}
// persistDelete appends a single deletion to the log.
func (s *store) persistDelete(name string) error {
s.l.Lock()
defer s.l.Unlock()
e := storeElement{N: name, D: true}
return s.enc.Encode(&e)
}
func (s *store) close() {
s.f.Close()
}