all: Add receive only folder type (#5027)
Adds a receive only folder type that does not send changes, and where the user can optionally revert local changes. Also changes some of the icons to make the three folder types distinguishable.
This commit is contained in:
135
lib/db/meta.go
135
lib/db/meta.go
@@ -7,25 +7,30 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/bits"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
// like protocol.LocalDeviceID but with 0xf8 in all positions
|
||||
var globalDeviceID = protocol.DeviceID{0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8}
|
||||
|
||||
// metadataTracker keeps metadata on a per device, per local flag basis.
|
||||
type metadataTracker struct {
|
||||
mut sync.RWMutex
|
||||
counts CountsSet
|
||||
indexes map[protocol.DeviceID]int // device ID -> index in counts
|
||||
indexes map[metaKey]int // device ID + local flags -> index in counts
|
||||
}
|
||||
|
||||
type metaKey struct {
|
||||
dev protocol.DeviceID
|
||||
flags uint32
|
||||
}
|
||||
|
||||
func newMetadataTracker() *metadataTracker {
|
||||
return &metadataTracker{
|
||||
mut: sync.NewRWMutex(),
|
||||
indexes: make(map[protocol.DeviceID]int),
|
||||
indexes: make(map[metaKey]int),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +43,7 @@ func (m *metadataTracker) Unmarshal(bs []byte) error {
|
||||
|
||||
// Initialize the index map
|
||||
for i, c := range m.counts.Counts {
|
||||
m.indexes[protocol.DeviceIDFromBytes(c.DeviceID)] = i
|
||||
m.indexes[metaKey{protocol.DeviceIDFromBytes(c.DeviceID), c.LocalFlags}] = i
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -72,14 +77,15 @@ func (m *metadataTracker) fromDB(db *Instance, folder []byte) error {
|
||||
|
||||
// countsPtr returns a pointer to the corresponding Counts struct, if
|
||||
// necessary allocating one in the process
|
||||
func (m *metadataTracker) countsPtr(dev protocol.DeviceID) *Counts {
|
||||
func (m *metadataTracker) countsPtr(dev protocol.DeviceID, flags uint32) *Counts {
|
||||
// must be called with the mutex held
|
||||
|
||||
idx, ok := m.indexes[dev]
|
||||
key := metaKey{dev, flags}
|
||||
idx, ok := m.indexes[key]
|
||||
if !ok {
|
||||
idx = len(m.counts.Counts)
|
||||
m.counts.Counts = append(m.counts.Counts, Counts{DeviceID: dev[:]})
|
||||
m.indexes[dev] = idx
|
||||
m.counts.Counts = append(m.counts.Counts, Counts{DeviceID: dev[:], LocalFlags: flags})
|
||||
m.indexes[key] = idx
|
||||
}
|
||||
return &m.counts.Counts[idx]
|
||||
}
|
||||
@@ -87,12 +93,23 @@ func (m *metadataTracker) countsPtr(dev protocol.DeviceID) *Counts {
|
||||
// addFile adds a file to the counts, adjusting the sequence number as
|
||||
// appropriate
|
||||
func (m *metadataTracker) addFile(dev protocol.DeviceID, f FileIntf) {
|
||||
if f.IsInvalid() {
|
||||
return
|
||||
m.mut.Lock()
|
||||
|
||||
if flags := f.FileLocalFlags(); flags == 0 {
|
||||
// Account regular files in the zero-flags bucket.
|
||||
m.addFileLocked(dev, 0, f)
|
||||
} else {
|
||||
// Account in flag specific buckets.
|
||||
eachFlagBit(flags, func(flag uint32) {
|
||||
m.addFileLocked(dev, flag, f)
|
||||
})
|
||||
}
|
||||
|
||||
m.mut.Lock()
|
||||
cp := m.countsPtr(dev)
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
func (m *metadataTracker) addFileLocked(dev protocol.DeviceID, flags uint32, f FileIntf) {
|
||||
cp := m.countsPtr(dev, flags)
|
||||
|
||||
switch {
|
||||
case f.IsDeleted():
|
||||
@@ -109,18 +126,27 @@ func (m *metadataTracker) addFile(dev protocol.DeviceID, f FileIntf) {
|
||||
if seq := f.SequenceNo(); seq > cp.Sequence {
|
||||
cp.Sequence = seq
|
||||
}
|
||||
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
// removeFile removes a file from the counts
|
||||
func (m *metadataTracker) removeFile(dev protocol.DeviceID, f FileIntf) {
|
||||
if f.IsInvalid() {
|
||||
return
|
||||
m.mut.Lock()
|
||||
|
||||
if flags := f.FileLocalFlags(); flags == 0 {
|
||||
// Remove regular files from the zero-flags bucket
|
||||
m.removeFileLocked(dev, 0, f)
|
||||
} else {
|
||||
// Remove from flag specific buckets.
|
||||
eachFlagBit(flags, func(flag uint32) {
|
||||
m.removeFileLocked(dev, flag, f)
|
||||
})
|
||||
}
|
||||
|
||||
m.mut.Lock()
|
||||
cp := m.countsPtr(dev)
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
func (m *metadataTracker) removeFileLocked(dev protocol.DeviceID, flags uint32, f FileIntf) {
|
||||
cp := m.countsPtr(dev, f.FileLocalFlags())
|
||||
|
||||
switch {
|
||||
case f.IsDeleted():
|
||||
@@ -153,14 +179,19 @@ func (m *metadataTracker) removeFile(dev protocol.DeviceID, f FileIntf) {
|
||||
cp.Symlinks = 0
|
||||
m.counts.Created = 0
|
||||
}
|
||||
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
// resetAll resets all metadata for the given device
|
||||
func (m *metadataTracker) resetAll(dev protocol.DeviceID) {
|
||||
m.mut.Lock()
|
||||
*m.countsPtr(dev) = Counts{DeviceID: dev[:]}
|
||||
for i, c := range m.counts.Counts {
|
||||
if bytes.Equal(c.DeviceID, dev[:]) {
|
||||
m.counts.Counts[i] = Counts{
|
||||
DeviceID: c.DeviceID,
|
||||
LocalFlags: c.LocalFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
@@ -169,23 +200,30 @@ func (m *metadataTracker) resetAll(dev protocol.DeviceID) {
|
||||
func (m *metadataTracker) resetCounts(dev protocol.DeviceID) {
|
||||
m.mut.Lock()
|
||||
|
||||
c := m.countsPtr(dev)
|
||||
c.Bytes = 0
|
||||
c.Deleted = 0
|
||||
c.Directories = 0
|
||||
c.Files = 0
|
||||
c.Symlinks = 0
|
||||
// c.Sequence deliberately untouched
|
||||
for i, c := range m.counts.Counts {
|
||||
if bytes.Equal(c.DeviceID, dev[:]) {
|
||||
m.counts.Counts[i] = Counts{
|
||||
DeviceID: c.DeviceID,
|
||||
Sequence: c.Sequence,
|
||||
LocalFlags: c.LocalFlags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
// Counts returns the counts for the given device ID
|
||||
func (m *metadataTracker) Counts(dev protocol.DeviceID) Counts {
|
||||
// Counts returns the counts for the given device ID and flag. `flag` should
|
||||
// be zero or have exactly one bit set.
|
||||
func (m *metadataTracker) Counts(dev protocol.DeviceID, flag uint32) Counts {
|
||||
if bits.OnesCount32(flag) > 1 {
|
||||
panic("incorrect usage: set at most one bit in flag")
|
||||
}
|
||||
|
||||
m.mut.RLock()
|
||||
defer m.mut.RUnlock()
|
||||
|
||||
idx, ok := m.indexes[dev]
|
||||
idx, ok := m.indexes[metaKey{dev, flag}]
|
||||
if !ok {
|
||||
return Counts{}
|
||||
}
|
||||
@@ -198,7 +236,7 @@ func (m *metadataTracker) nextSeq(dev protocol.DeviceID) int64 {
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
c := m.countsPtr(dev)
|
||||
c := m.countsPtr(dev, 0)
|
||||
c.Sequence++
|
||||
return c.Sequence
|
||||
}
|
||||
@@ -206,21 +244,26 @@ func (m *metadataTracker) nextSeq(dev protocol.DeviceID) int64 {
|
||||
// devices returns the list of devices tracked, excluding the local device
|
||||
// (which we don't know the ID of)
|
||||
func (m *metadataTracker) devices() []protocol.DeviceID {
|
||||
devs := make([]protocol.DeviceID, 0, len(m.counts.Counts))
|
||||
devs := make(map[protocol.DeviceID]struct{}, len(m.counts.Counts))
|
||||
|
||||
m.mut.RLock()
|
||||
for _, dev := range m.counts.Counts {
|
||||
if dev.Sequence > 0 {
|
||||
id := protocol.DeviceIDFromBytes(dev.DeviceID)
|
||||
if id == globalDeviceID || id == protocol.LocalDeviceID {
|
||||
if id == protocol.GlobalDeviceID || id == protocol.LocalDeviceID {
|
||||
continue
|
||||
}
|
||||
devs = append(devs, id)
|
||||
devs[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
m.mut.RUnlock()
|
||||
|
||||
return devs
|
||||
devList := make([]protocol.DeviceID, 0, len(devs))
|
||||
for dev := range devs {
|
||||
devList = append(devList, dev)
|
||||
}
|
||||
|
||||
return devList
|
||||
}
|
||||
|
||||
func (m *metadataTracker) Created() time.Time {
|
||||
@@ -234,3 +277,19 @@ func (m *metadataTracker) SetCreated() {
|
||||
m.counts.Created = time.Now().UnixNano()
|
||||
m.mut.Unlock()
|
||||
}
|
||||
|
||||
// eachFlagBit calls the function once for every bit that is set in flags
|
||||
func eachFlagBit(flags uint32, fn func(flag uint32)) {
|
||||
// Test each bit from the right, as long as there are bits left in the
|
||||
// flag set. Clear any bits found and stop testing as soon as there are
|
||||
// no more bits set.
|
||||
|
||||
currentBit := uint32(1 << 0)
|
||||
for flags != 0 {
|
||||
if flags¤tBit != 0 {
|
||||
fn(currentBit)
|
||||
flags &^= currentBit
|
||||
}
|
||||
currentBit <<= 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user