-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.go
167 lines (134 loc) · 3.62 KB
/
index.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
package archiving
import (
"sync"
"github.com/golang/glog"
"github.com/rokeller/bart/domain"
)
type indexEntry struct {
domain.EntryMetadata
EntryFlags
}
type EntryFlags uint32
const (
EntryFlagsNone EntryFlags = 0x0000_0000
EntryFlagsPresentInBackup EntryFlags = 0x0000_0001
EntryFlagsPresentInLocal EntryFlags = 0x0000_0002
)
type Index struct {
archive *Archive
// entries tracks entries in the index; must only accessed directly by
// handleMessages, handleMessage or during initialization.
entries map[string]indexEntry
messages chan message
dirty bool
closed bool
wgClose *sync.WaitGroup
}
func newIndex(a *Archive) *Index {
index := Index{
archive: a,
entries: make(map[string]indexEntry),
messages: make(chan message, 10),
dirty: false,
closed: false,
wgClose: &sync.WaitGroup{},
}
index.load()
index.wgClose.Add(1)
go index.handleMessages()
return &index
}
func (i *Index) Count() int {
var count int
i.sync(func() {
count = len(i.entries)
})
return count
}
func (i *Index) Dirty() bool {
return i.dirty
}
func (i *Index) Close() error {
close(i.messages)
i.wgClose.Wait()
i.closed = true
if i.Dirty() {
glog.Info("The archive index has changed and needs to be uploaded.")
// Calling writeIndex here is safe because message handler must have
// been stopped at the beginning of the method.
if err := i.writeIndex(); nil != err {
glog.Errorf("The archive index could not be uploaded: %v", err)
return err
}
} else {
glog.Info("The archive index has not changed.")
}
return nil
}
func (i *Index) load() {
if err := i.readIndex(); nil == err {
return
} else if err == IndexNotFound {
// It's not an error if the index does not exist yet.
return
} else if err == IndexDecryptionFailed {
glog.Exit("Index decryption failed. Did you provide the correct password?")
} else {
glog.Exit("Failed to load archive index: %v", err)
}
}
// walkIndex walks through the index. It is the caller's responsibility to make
// sure there is mutually exclusive access to the index, e.g. through the use
// of Index.sync, or by calling before index message handling has started or
// after it has finished.
func (i *Index) walkIndex(fn func(domain.Entry, EntryFlags) error) error {
var err error
err = nil
for key, value := range i.entries {
entry := domain.Entry{
RelPath: key,
EntryMetadata: value.EntryMetadata,
}
if err = fn(entry, value.EntryFlags); nil != err {
break
}
}
return err
}
// walkIndexSnapshot creates a snapshot of the current index and then walks the
// entries of that snapshot, applying the given fn for every entry.
func (i *Index) walkIndexSnapshot(fn func(domain.Entry, EntryFlags) error) error {
// Track a snapshot of the current index' keys.
snapshot := make(map[string]indexEntry)
extractKeys := func() {
for key, value := range i.entries {
snapshot[key] = value
}
}
i.sync(extractKeys)
// Now iterate through the snapshot applying the fn to every item.
var err error
err = nil
for key, value := range snapshot {
entry := domain.Entry{
RelPath: key,
EntryMetadata: value.EntryMetadata,
}
if err = fn(entry, value.EntryFlags); nil != err {
break
}
}
return err
}
func (i *Index) needsBackup(entry domain.Entry) bool {
indexEntry := i.getEntry(entry.RelPath)
found := nil != indexEntry
backupNeeded := !found ||
(indexEntry.EntryFlags&EntryFlagsPresentInBackup) == EntryFlagsNone ||
indexEntry.Timestamp < entry.Timestamp
// Let's mark the file as present in local
if found {
i.setEntry(entry, indexEntry.EntryFlags|EntryFlagsPresentInLocal, false)
}
return backupNeeded
}