Skip to content

Commit

Permalink
gui, lib: Fix tracking deleted locally-changed on encrypted (fixes sy…
Browse files Browse the repository at this point in the history
  • Loading branch information
imsodin committed May 28, 2021
1 parent fbaf696 commit 4e4e78e
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 194 deletions.
6 changes: 0 additions & 6 deletions gui/default/index.html
Expand Up @@ -446,12 +446,6 @@ <h4 class="panel-title">
<a href="" ng-click="showLocalChanged(folder.id, folder.type)">{{model[folder.id].receiveOnlyTotalItems | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{model[folder.id].receiveOnlyChangedBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="hasReceiveEncryptedItems(folder)">
<th><span class="fas fa-fw fa-exclamation-circle"></span>&nbsp;<span translate>Locally Changed Items</span></th>
<td class="text-right">
<a href="" ng-click="showLocalChanged(folder.id, folder.type)">{{receiveEncryptedItemsCount(folder) | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{model[folder.id].receiveOnlyChangedBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="folder.type != 'sendreceive'">
<th><span class="fas fa-fw fa-folder"></span>&nbsp;<span translate>Folder Type</span></th>
<td class="text-right">
Expand Down
23 changes: 4 additions & 19 deletions gui/default/syncthing/core/syncthingController.js
Expand Up @@ -908,9 +908,9 @@ angular.module('syncthing.core')
return 'faileditems';
}
if ($scope.hasReceiveOnlyChanged(folderCfg)) {
return 'localadditions';
}
if ($scope.hasReceiveEncryptedItems(folderCfg)) {
if (folderCfg.type === "receiveonly") {
return 'localadditions';
}
return 'localunencrypted';
}
if (folderCfg.devices.length <= 1) {
Expand Down Expand Up @@ -2526,28 +2526,13 @@ angular.module('syncthing.core')
};

$scope.hasReceiveOnlyChanged = function (folderCfg) {
if (!folderCfg || folderCfg.type !== "receiveonly") {
if (!folderCfg || folderCfg.type !== ["receiveonly", "receiveencrypted"].indexOf(folderCfg.type) === -1) {
return false;
}
var counts = $scope.model[folderCfg.id];
return counts && counts.receiveOnlyTotalItems > 0;
};

$scope.hasReceiveEncryptedItems = function (folderCfg) {
if (!folderCfg || folderCfg.type !== "receiveencrypted") {
return false;
}
return $scope.receiveEncryptedItemsCount(folderCfg) > 0;
};

$scope.receiveEncryptedItemsCount = function (folderCfg) {
var counts = $scope.model[folderCfg.id];
if (!counts) {
return 0;
}
return counts.receiveOnlyTotalItems - counts.receiveOnlyChangedDeletes;
}

$scope.revertOverride = function () {
$http.post(
urlbase + "/db/" + $scope.revertOverrideParams.operation +"?folder="
Expand Down
114 changes: 88 additions & 26 deletions lib/db/lowlevel.go
Expand Up @@ -222,35 +222,10 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
blocksHashSame := ok && bytes.Equal(ef.BlocksHash, f.BlocksHash)

if ok {
if len(ef.Blocks) != 0 && !ef.IsInvalid() && ef.Size > 0 {
for _, block := range ef.Blocks {
keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
if err != nil {
return err
}
if err := t.Delete(keyBuf); err != nil {
return err
}
}
if !blocksHashSame {
keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, ef.BlocksHash, name)
if err != nil {
return err
}
if err = t.Delete(keyBuf); err != nil {
return err
}
}
}

keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo())
keyBuf, err = db.removeLocalBlockAndSequenceInfo(keyBuf, folder, name, ef, !blocksHashSame, &t)
if err != nil {
return err
}
if err := t.Delete(keyBuf); err != nil {
return err
}
l.Debugf("removing sequence; folder=%q sequence=%v %v", folder, ef.SequenceNo(), ef.FileName())
}

f.Sequence = meta.nextLocalSeq()
Expand Down Expand Up @@ -313,6 +288,93 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
return t.Commit()
}

func (db *Lowlevel) removeLocalFiles(folder []byte, nameStrs []string, meta *metadataTracker) error {
db.gcMut.RLock()
defer db.gcMut.RUnlock()

t, err := db.newReadWriteTransaction(meta.CommitHook(folder))
if err != nil {
return err
}
defer t.close()

var dk, gk, buf []byte
for _, nameStr := range nameStrs {
name := []byte(nameStr)
dk, err = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name)
if err != nil {
return err
}

ef, ok, err := t.getFileByKey(dk)
if err != nil {
return err
}
if !ok {
l.Debugf("remove (local); folder=%q %v: file doesn't exist", folder, nameStr)
continue
}

buf, err = db.removeLocalBlockAndSequenceInfo(buf, folder, name, ef, true, &t)
if err != nil {
return err
}

meta.removeFile(protocol.LocalDeviceID, ef)

gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
if err != nil {
return err
}
buf, err = t.removeFromGlobal(gk, buf, folder, protocol.LocalDeviceID[:], name, meta)
if err != nil {
return err
}

err = t.Delete(dk)
if err != nil {
return err
}

if err := t.Checkpoint(); err != nil {
return err
}
}

return t.Commit()
}

func (db *Lowlevel) removeLocalBlockAndSequenceInfo(keyBuf, folder, name []byte, ef protocol.FileInfo, removeFromBlockListMap bool, t *readWriteTransaction) ([]byte, error) {
var err error
if len(ef.Blocks) != 0 && !ef.IsInvalid() && ef.Size > 0 {
for _, block := range ef.Blocks {
keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
if err != nil {
return keyBuf, err
}
if err := t.Delete(keyBuf); err != nil {
return keyBuf, err
}
}
if removeFromBlockListMap {
keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, ef.BlocksHash, name)
if err != nil {
return keyBuf, err
}
if err = t.Delete(keyBuf); err != nil {
return keyBuf, err
}
}
}

keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo())
if err != nil {
return keyBuf, err
}
l.Debugf("removing sequence; folder=%q sequence=%v %v", folder, ef.SequenceNo(), ef.FileName())
return keyBuf, t.Delete(keyBuf)
}

func (db *Lowlevel) dropFolder(folder []byte) error {
db.gcMut.RLock()
defer db.gcMut.RUnlock()
Expand Down
16 changes: 16 additions & 0 deletions lib/db/set.go
Expand Up @@ -144,6 +144,22 @@ func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) {
}
}

func (s *FileSet) RemoveLocalItems(items []string) {
opStr := fmt.Sprintf("%s RemoveLocalItems([%d])", s.folder, len(items))
l.Debugf(opStr)

s.updateMutex.Lock()
defer s.updateMutex.Unlock()

for i := range items {
items[i] = osutil.NormalizedFilename(items[i])
}

if err := s.db.removeLocalFiles([]byte(s.folder), items, s.meta); err != nil && !backend.IsClosed(err) {
fatalError(err, opStr, s.db)
}
}

type Snapshot struct {
folder string
t readOnlyTransaction
Expand Down
1 change: 1 addition & 0 deletions lib/model/fakeconns_test.go
Expand Up @@ -156,6 +156,7 @@ func (f *fakeConnection) sendIndexUpdate() {

func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConnection {
fc := newFakeConnection(dev, m)
fc.folder = folderID
m.AddConnection(fc, protocol.Hello{})

m.ClusterConfig(dev, protocol.ClusterConfig{
Expand Down

0 comments on commit 4e4e78e

Please sign in to comment.