-
Notifications
You must be signed in to change notification settings - Fork 33
/
ghost.go
236 lines (194 loc) · 5.08 KB
/
ghost.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package nodes
import (
"fmt"
ie "github.com/sahib/brig/catfs/errors"
capnp_model "github.com/sahib/brig/catfs/nodes/capnp"
h "github.com/sahib/brig/util/hashlib"
capnp "zombiezen.com/go/capnproto2"
)
// Ghost is a special kind of Node that marks a moved node.
// If a file was moved, a ghost will be created for the old place.
// If another file is moved to the new place, the ghost will be "resurrected"
// with the new content.
type Ghost struct {
ModNode
ghostPath string
ghostInode uint64
oldType NodeType
}
// MakeGhost takes an existing node and converts it to a ghost.
// In the ghost form no metadata is lost, but the node should
// not show up. `inode` will be the new inode of the ghost.
// It should differ to the previous node.
func MakeGhost(nd ModNode, inode uint64) (*Ghost, error) {
if nd.Type() == NodeTypeGhost {
panic("cannot put a ghost in a ghost")
}
return &Ghost{
ModNode: nd.Copy(nd.Inode()),
oldType: nd.Type(),
ghostInode: inode,
ghostPath: nd.Path(),
}, nil
}
// Type always returns NodeTypeGhost
func (g *Ghost) Type() NodeType {
return NodeTypeGhost
}
// OldNode returns the node the ghost was when it still was alive.
func (g *Ghost) OldNode() ModNode {
return g.ModNode
}
// OldFile returns the file the ghost was when it still was alive.
// Returns ErrBadNode when it wasn't a file.
func (g *Ghost) OldFile() (*File, error) {
file, ok := g.ModNode.(*File)
if !ok {
return nil, ie.ErrBadNode
}
return file, nil
}
// OldDirectory returns the old directory that the node was in lifetime
// If the ghost was not a directory, ErrBadNode is returned.
func (g *Ghost) OldDirectory() (*Directory, error) {
directory, ok := g.ModNode.(*Directory)
if !ok {
return nil, ie.ErrBadNode
}
return directory, nil
}
func (g *Ghost) String() string {
return fmt.Sprintf("<ghost: %s %v>", g.TreeHash(), g.ModNode)
}
// Path returns the path of the node.
func (g *Ghost) Path() string {
return g.ghostPath
}
// TreeHash returns the hash of the node.
func (g *Ghost) TreeHash() h.Hash {
return h.Sum([]byte(fmt.Sprintf("ghost:%s", g.ModNode.TreeHash())))
}
// Inode returns the inode
func (g *Ghost) Inode() uint64 {
return g.ghostInode
}
// SetGhostPath sets the path of the ghost.
func (g *Ghost) SetGhostPath(newPath string) {
g.ghostPath = newPath
}
// ToCapnp serializes the underlying node
func (g *Ghost) ToCapnp() (*capnp.Message, error) {
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
return nil, err
}
capNd, err := capnp_model.NewRootNode(seg)
if err != nil {
return nil, err
}
return msg, g.ToCapnpNode(seg, capNd)
}
// ToCapnpNode converts this node to a serializable capnp proto node.
func (g *Ghost) ToCapnpNode(seg *capnp.Segment, capNd capnp_model.Node) error {
var base *Base
capghost, err := capNd.NewGhost()
if err != nil {
return err
}
capghost.SetGhostInode(g.ghostInode)
if err = capghost.SetGhostPath(g.ghostPath); err != nil {
return err
}
switch g.oldType {
case NodeTypeFile:
file, ok := g.ModNode.(*File)
if !ok {
return ie.ErrBadNode
}
capfile, err := file.setFileAttrs(seg)
if err != nil {
return err
}
base = &file.Base
if err = capghost.SetFile(*capfile); err != nil {
return err
}
case NodeTypeDirectory:
dir, ok := g.ModNode.(*Directory)
if !ok {
return ie.ErrBadNode
}
capdir, err := dir.setDirectoryAttrs(seg)
if err != nil {
return err
}
base = &dir.Base
if err = capghost.SetDirectory(*capdir); err != nil {
return err
}
case NodeTypeGhost:
panic("Recursive ghosts are not possible")
default:
panic(fmt.Sprintf("Unknown node type: %d", g.oldType))
}
if err != nil {
return err
}
if err := base.setBaseAttrsToNode(capNd); err != nil {
return err
}
return capNd.SetGhost(capghost)
}
// FromCapnp reads all attributes from a previously marshaled ghost.
func (g *Ghost) FromCapnp(msg *capnp.Message) error {
capNd, err := capnp_model.ReadRootNode(msg)
if err != nil {
return err
}
return g.FromCapnpNode(capNd)
}
// FromCapnpNode converts a serialized node to a normal node.
func (g *Ghost) FromCapnpNode(capNd capnp_model.Node) error {
if typ := capNd.Which(); typ != capnp_model.Node_Which_ghost {
return fmt.Errorf("BUG: ghost unmarshal with non ghost type: %d", typ)
}
capghost, err := capNd.Ghost()
if err != nil {
return err
}
g.ghostInode = capghost.GhostInode()
g.ghostPath, err = capghost.GhostPath()
if err != nil {
return err
}
var base *Base
switch typ := capghost.Which(); typ {
case capnp_model.Ghost_Which_directory:
capdir, err := capghost.Directory()
if err != nil {
return err
}
dir := &Directory{}
if err := dir.readDirectoryAttr(capdir); err != nil {
return err
}
g.ModNode = dir
g.oldType = NodeTypeDirectory
base = &dir.Base
case capnp_model.Ghost_Which_file:
capfile, err := capghost.File()
if err != nil {
return err
}
file := &File{}
if err := file.readFileAttrs(capfile); err != nil {
return err
}
g.ModNode = file
g.oldType = NodeTypeFile
base = &file.Base
default:
return ie.ErrBadNode
}
return base.parseBaseAttrsFromNode(capNd)
}