mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-22 10:58:57 +00:00
This commit is contained in:
parent
dec6f80d2b
commit
591e4d8af1
@ -455,12 +455,6 @@
|
||||
<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> <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> <span translate>Folder Type</span></th>
|
||||
<td class="text-right">
|
||||
|
@ -931,9 +931,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) {
|
||||
@ -2609,28 +2609,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="
|
||||
|
@ -223,35 +223,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()
|
||||
@ -314,6 +289,96 @@ 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 nil, err
|
||||
}
|
||||
if err := t.Delete(keyBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if removeFromBlockListMap {
|
||||
keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, ef.BlocksHash, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = t.Delete(keyBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.Delete(keyBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Debugf("removing sequence; folder=%q sequence=%v %v", folder, ef.SequenceNo(), ef.FileName())
|
||||
return keyBuf, nil
|
||||
}
|
||||
|
||||
func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
db.gcMut.RLock()
|
||||
defer db.gcMut.RUnlock()
|
||||
|
@ -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
|
||||
|
@ -162,6 +162,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{
|
||||
|
@ -532,16 +532,20 @@ func (f *folder) scanSubdirs(subDirs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const maxToRemove = 1000
|
||||
|
||||
type scanBatch struct {
|
||||
*db.FileInfoBatch
|
||||
f *folder
|
||||
f *folder
|
||||
updateBatch *db.FileInfoBatch
|
||||
toRemove []string
|
||||
}
|
||||
|
||||
func (f *folder) newScanBatch() *scanBatch {
|
||||
b := &scanBatch{
|
||||
f: f,
|
||||
f: f,
|
||||
toRemove: make([]string, 0, maxToRemove),
|
||||
}
|
||||
b.FileInfoBatch = db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
|
||||
b.updateBatch = db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
|
||||
if err := b.f.getHealthErrorWithoutIgnores(); err != nil {
|
||||
l.Debugf("Stopping scan of folder %s due to: %s", b.f.Description(), err)
|
||||
return err
|
||||
@ -552,9 +556,32 @@ func (f *folder) newScanBatch() *scanBatch {
|
||||
return b
|
||||
}
|
||||
|
||||
// Append adds the fileinfo to the batch for updating, and does a few checks.
|
||||
func (b *scanBatch) Remove(item string) {
|
||||
b.toRemove = append(b.toRemove, item)
|
||||
}
|
||||
|
||||
func (b *scanBatch) flushToRemove() {
|
||||
if len(b.toRemove) > 0 {
|
||||
b.f.fset.RemoveLocalItems(b.toRemove)
|
||||
b.toRemove = b.toRemove[:0]
|
||||
}
|
||||
}
|
||||
|
||||
func (b *scanBatch) Flush() error {
|
||||
b.flushToRemove()
|
||||
return b.updateBatch.Flush()
|
||||
}
|
||||
|
||||
func (b *scanBatch) FlushIfFull() error {
|
||||
if len(b.toRemove) >= maxToRemove {
|
||||
b.flushToRemove()
|
||||
}
|
||||
return b.updateBatch.FlushIfFull()
|
||||
}
|
||||
|
||||
// Update adds the fileinfo to the batch for updating, and does a few checks.
|
||||
// It returns false if the checks result in the file not going to be updated or removed.
|
||||
func (b *scanBatch) Append(fi protocol.FileInfo, snap *db.Snapshot) bool {
|
||||
func (b *scanBatch) Update(fi protocol.FileInfo, snap *db.Snapshot) bool {
|
||||
// Check for a "virtual" parent directory of encrypted files. We don't track
|
||||
// it, but check if anything still exists within and delete it otherwise.
|
||||
if b.f.Type == config.FolderTypeReceiveEncrypted && fi.IsDirectory() && protocol.IsEncryptedParent(fs.PathComponents(fi.Name)) {
|
||||
@ -565,20 +592,21 @@ func (b *scanBatch) Append(fi protocol.FileInfo, snap *db.Snapshot) bool {
|
||||
}
|
||||
// Resolve receive-only items which are identical with the global state or
|
||||
// the global item is our own receive-only item.
|
||||
// Except if they are in a receive-encrypted folder and are locally added.
|
||||
// Those must never be sent in index updates and thus must retain the flag.
|
||||
switch gf, ok := snap.GetGlobal(fi.Name); {
|
||||
case !ok:
|
||||
case gf.IsReceiveOnlyChanged():
|
||||
if b.f.Type == config.FolderTypeReceiveOnly && fi.Deleted {
|
||||
l.Debugf("%v scanning: Marking deleted item as not locally changed", b.f, fi)
|
||||
fi.LocalFlags &^= protocol.FlagLocalReceiveOnly
|
||||
if fi.IsDeleted() {
|
||||
// Our item is deleted and the global item is our own receive only
|
||||
// file. No point in keeping track of that.
|
||||
b.Remove(fi.Name)
|
||||
return true
|
||||
}
|
||||
case gf.IsEquivalentOptional(fi, b.f.modTimeWindow, false, false, protocol.FlagLocalReceiveOnly):
|
||||
l.Debugf("%v scanning: Replacing scanned file info with global as it's equivalent", b.f, fi)
|
||||
// What we have locally is equivalent to the global file.
|
||||
l.Debugf("%v scanning: Merging identical locally changed item with global", b.f, fi)
|
||||
fi = gf
|
||||
}
|
||||
b.FileInfoBatch.Append(fi)
|
||||
b.updateBatch.Append(fi)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -634,7 +662,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (i
|
||||
return changes, err
|
||||
}
|
||||
|
||||
if batch.Append(res.File, snap) {
|
||||
if batch.Update(res.File, snap) {
|
||||
changes++
|
||||
}
|
||||
|
||||
@ -642,7 +670,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (i
|
||||
case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted:
|
||||
default:
|
||||
if nf, ok := f.findRename(snap, res.File, alreadyUsedOrExisting); ok {
|
||||
if batch.Append(nf, snap) {
|
||||
if batch.Update(nf, snap) {
|
||||
changes++
|
||||
}
|
||||
}
|
||||
@ -683,7 +711,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch
|
||||
for _, file := range toIgnore {
|
||||
l.Debugln("marking file as ignored", file)
|
||||
nf := file.ConvertToIgnoredFileInfo()
|
||||
if batch.Append(nf, snap) {
|
||||
if batch.Update(nf, snap) {
|
||||
changes++
|
||||
}
|
||||
if err := batch.FlushIfFull(); err != nil {
|
||||
@ -713,7 +741,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch
|
||||
|
||||
l.Debugln("marking file as ignored", file)
|
||||
nf := file.ConvertToIgnoredFileInfo()
|
||||
if batch.Append(nf, snap) {
|
||||
if batch.Update(nf, snap) {
|
||||
changes++
|
||||
}
|
||||
|
||||
@ -743,24 +771,24 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch
|
||||
nf.Version = protocol.Vector{}
|
||||
}
|
||||
l.Debugln("marking file as deleted", nf)
|
||||
if batch.Append(nf, snap) {
|
||||
if batch.Update(nf, snap) {
|
||||
changes++
|
||||
}
|
||||
case file.IsDeleted() && file.IsReceiveOnlyChanged():
|
||||
switch f.Type {
|
||||
case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted:
|
||||
if gf, _ := snap.GetGlobal(file.Name); gf.IsDeleted() {
|
||||
switch gf, ok := snap.GetGlobal(file.Name); {
|
||||
case !ok:
|
||||
case gf.IsReceiveOnlyChanged():
|
||||
l.Debugln("removing deleted, receive-only item that is globally receive-only from db", file)
|
||||
batch.Remove(file.Name)
|
||||
changes++
|
||||
case gf.IsDeleted():
|
||||
// Our item is deleted and the global item is deleted too. We just
|
||||
// pretend it is a normal deleted file (nobody cares about that).
|
||||
// Except if this is a receive-encrypted folder and it
|
||||
// is a locally added file. Those must never be sent
|
||||
// in index updates and thus must retain the flag.
|
||||
if f.Type == config.FolderTypeReceiveEncrypted && gf.IsReceiveOnlyChanged() {
|
||||
return true
|
||||
}
|
||||
l.Debugf("%v scanning: Marking globally deleted item as not locally changed: %v", f, file.Name)
|
||||
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
|
||||
if batch.Append(file.ConvertDeletedToFileInfo(), snap) {
|
||||
if batch.Update(file.ConvertDeletedToFileInfo(), snap) {
|
||||
changes++
|
||||
}
|
||||
}
|
||||
@ -769,7 +797,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch
|
||||
// deleted and just the folder type/local flags changed.
|
||||
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
|
||||
l.Debugln("removing receive-only flag on deleted item", file)
|
||||
if batch.Append(file.ConvertDeletedToFileInfo(), snap) {
|
||||
if batch.Update(file.ConvertDeletedToFileInfo(), snap) {
|
||||
changes++
|
||||
}
|
||||
}
|
||||
@ -788,7 +816,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch
|
||||
for _, file := range toIgnore {
|
||||
l.Debugln("marking file as ignored", f)
|
||||
nf := file.ConvertToIgnoredFileInfo()
|
||||
if batch.Append(nf, snap) {
|
||||
if batch.Update(nf, snap) {
|
||||
changes++
|
||||
}
|
||||
if iterError = batch.FlushIfFull(); iterError != nil {
|
||||
|
@ -37,9 +37,9 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
|
||||
for _, dir := range []string{".stfolder", "ignDir", "unknownDir"} {
|
||||
must(t, ffs.MkdirAll(dir, 0755))
|
||||
}
|
||||
must(t, writeFile(ffs, "ignDir/ignFile", []byte("hello\n"), 0644))
|
||||
must(t, writeFile(ffs, "unknownDir/unknownFile", []byte("hello\n"), 0644))
|
||||
must(t, writeFile(ffs, ".stignore", []byte("ignDir\n"), 0644))
|
||||
writeFilePerm(t, ffs, "ignDir/ignFile", []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, "unknownDir/unknownFile", []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, ".stignore", []byte("ignDir\n"), 0644)
|
||||
|
||||
knownFiles := setupKnownFiles(t, ffs, []byte("hello\n"))
|
||||
|
||||
@ -151,7 +151,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
// Update the file.
|
||||
|
||||
newData := []byte("totally different data\n")
|
||||
must(t, writeFile(ffs, "knownDir/knownFile", newData, 0644))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", newData, 0644)
|
||||
|
||||
// Rescan.
|
||||
|
||||
@ -241,8 +241,8 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
|
||||
// Create a file and modify another
|
||||
|
||||
const file = "foo"
|
||||
must(t, writeFile(ffs, file, []byte("hello\n"), 0644))
|
||||
must(t, writeFile(ffs, "knownDir/knownFile", []byte("bye\n"), 0644))
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", []byte("bye\n"), 0644)
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
|
||||
@ -254,7 +254,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
|
||||
// Remove the file again and undo the modification
|
||||
|
||||
must(t, ffs.Remove(file))
|
||||
must(t, writeFile(ffs, "knownDir/knownFile", oldData, 0644))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", oldData, 0644)
|
||||
must(t, ffs.Chtimes("knownDir/knownFile", knownFiles[1].ModTime(), knownFiles[1].ModTime()))
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
@ -377,8 +377,8 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
|
||||
|
||||
const file = "foo"
|
||||
knownFile := filepath.Join("knownDir", "knownFile")
|
||||
must(t, writeFile(ffs, file, []byte("hello\n"), 0644))
|
||||
must(t, writeFile(ffs, knownFile, []byte("bye\n"), 0644))
|
||||
writeFilePerm(t, ffs, file, []byte("hello\n"), 0644)
|
||||
writeFilePerm(t, ffs, knownFile, []byte("bye\n"), 0644)
|
||||
|
||||
must(t, m.ScanFolder("ro"))
|
||||
|
||||
@ -434,7 +434,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
|
||||
must(t, ffs.MkdirAll(".stfolder", 0755))
|
||||
data := []byte("hello\n")
|
||||
name := "foo"
|
||||
must(t, writeFile(ffs, name, data, 0644))
|
||||
writeFilePerm(t, ffs, name, data, 0644)
|
||||
|
||||
// Make sure the file is scanned and locally changed
|
||||
must(t, m.ScanFolder("ro"))
|
||||
@ -484,7 +484,7 @@ func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.Fi
|
||||
t.Helper()
|
||||
|
||||
must(t, ffs.MkdirAll("knownDir", 0755))
|
||||
must(t, writeFile(ffs, "knownDir/knownFile", data, 0644))
|
||||
writeFilePerm(t, ffs, "knownDir/knownFile", data, 0644)
|
||||
|
||||
t0 := time.Now().Add(-1 * time.Minute)
|
||||
must(t, ffs.Chtimes("knownDir/knownFile", t0, t0))
|
||||
@ -541,18 +541,3 @@ func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.Cancel
|
||||
|
||||
return m, f, cancel
|
||||
}
|
||||
|
||||
func writeFile(fs fs.Filesystem, filename string, data []byte, perm fs.FileMode) error {
|
||||
fd, err := fs.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fd.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fd.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return fs.Chmod(filename, perm)
|
||||
}
|
||||
|
@ -77,12 +77,10 @@ func setupFile(filename string, blockNumbers []int) protocol.FileInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func createFile(t *testing.T, name string, fs fs.Filesystem) protocol.FileInfo {
|
||||
func createEmptyFileInfo(t *testing.T, name string, fs fs.Filesystem) protocol.FileInfo {
|
||||
t.Helper()
|
||||
|
||||
f, err := fs.Create(name)
|
||||
must(t, err)
|
||||
f.Close()
|
||||
writeFile(t, fs, name, nil)
|
||||
fi, err := fs.Stat(name)
|
||||
must(t, err)
|
||||
file, err := scanner.CreateFileInfo(fi, name, fs)
|
||||
@ -913,7 +911,7 @@ func TestSRConflictReplaceFileByDir(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
// create local file
|
||||
file := createFile(t, name, ffs)
|
||||
file := createEmptyFileInfo(t, name, ffs)
|
||||
file.Version = protocol.Vector{}.Update(myID.Short())
|
||||
f.updateLocalsFromScanning([]protocol.FileInfo{file})
|
||||
|
||||
@ -945,7 +943,7 @@ func TestSRConflictReplaceFileByLink(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
// create local file
|
||||
file := createFile(t, name, ffs)
|
||||
file := createEmptyFileInfo(t, name, ffs)
|
||||
file.Version = protocol.Vector{}.Update(myID.Short())
|
||||
f.updateLocalsFromScanning([]protocol.FileInfo{file})
|
||||
|
||||
@ -983,7 +981,7 @@ func TestDeleteBehindSymlink(t *testing.T) {
|
||||
file := filepath.Join(link, "file")
|
||||
|
||||
must(t, ffs.MkdirAll(link, 0755))
|
||||
fi := createFile(t, file, ffs)
|
||||
fi := createEmptyFileInfo(t, file, ffs)
|
||||
f.updateLocalsFromScanning([]protocol.FileInfo{fi})
|
||||
must(t, osutil.RenameOrCopy(fs.CopyRangeMethodStandard, ffs, destFs, file, "file"))
|
||||
must(t, ffs.RemoveAll(link))
|
||||
@ -1099,7 +1097,7 @@ func TestPullCaseOnlyPerformFinish(t *testing.T) {
|
||||
|
||||
name := "foo"
|
||||
contents := []byte("contents")
|
||||
must(t, writeFile(ffs, name, contents, 0644))
|
||||
writeFile(t, ffs, name, contents)
|
||||
must(t, f.scanSubdirs(nil))
|
||||
|
||||
var cur protocol.FileInfo
|
||||
@ -1122,7 +1120,7 @@ func TestPullCaseOnlyPerformFinish(t *testing.T) {
|
||||
remote.Version = protocol.Vector{}.Update(device1.Short())
|
||||
remote.Name = strings.ToUpper(cur.Name)
|
||||
temp := fs.TempName(remote.Name)
|
||||
must(t, writeFile(ffs, temp, contents, 0644))
|
||||
writeFile(t, ffs, temp, contents)
|
||||
scanChan := make(chan string, 1)
|
||||
dbUpdateChan := make(chan dbUpdateJob, 1)
|
||||
|
||||
|
@ -1053,7 +1053,6 @@ func (m *model) RemoteNeedFolderFiles(folder string, device protocol.DeviceID, p
|
||||
func (m *model) LocalChangedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, error) {
|
||||
m.fmut.RLock()
|
||||
rf, ok := m.folderFiles[folder]
|
||||
cfg := m.folderCfgs[folder]
|
||||
m.fmut.RUnlock()
|
||||
|
||||
if !ok {
|
||||
@ -1071,11 +1070,10 @@ func (m *model) LocalChangedFolderFiles(folder string, page, perpage int) ([]db.
|
||||
}
|
||||
|
||||
p := newPager(page, perpage)
|
||||
recvEnc := cfg.Type == config.FolderTypeReceiveEncrypted
|
||||
files := make([]db.FileInfoTruncated, 0, perpage)
|
||||
|
||||
snap.WithHaveTruncated(protocol.LocalDeviceID, func(f protocol.FileIntf) bool {
|
||||
if !f.IsReceiveOnlyChanged() || (recvEnc && f.IsDeleted()) {
|
||||
if !f.IsReceiveOnlyChanged() {
|
||||
return true
|
||||
}
|
||||
if p.skip() {
|
||||
|
@ -299,7 +299,7 @@ func BenchmarkRequestInSingleFile(b *testing.B) {
|
||||
mustRemove(b, defaultFs.RemoveAll("request"))
|
||||
defer func() { mustRemove(b, defaultFs.RemoveAll("request")) }()
|
||||
must(b, defaultFs.MkdirAll("request/for/a/file/in/a/couple/of/dirs", 0755))
|
||||
writeFile(defaultFs, "request/for/a/file/in/a/couple/of/dirs/128k", buf, 0644)
|
||||
writeFile(b, defaultFs, "request/for/a/file/in/a/couple/of/dirs/128k", buf)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
@ -1489,7 +1489,7 @@ func TestIgnores(t *testing.T) {
|
||||
// Assure a clean start state
|
||||
mustRemove(t, defaultFs.RemoveAll(config.DefaultMarkerName))
|
||||
mustRemove(t, defaultFs.MkdirAll(config.DefaultMarkerName, 0644))
|
||||
writeFile(defaultFs, ".stignore", []byte(".*\nquux\n"), 0644)
|
||||
writeFile(t, defaultFs, ".stignore", []byte(".*\nquux\n"))
|
||||
|
||||
m := setupModel(t, defaultCfgWrapper)
|
||||
defer cleanupModel(m)
|
||||
@ -2030,8 +2030,8 @@ func benchmarkTree(b *testing.B, n1, n2 int) {
|
||||
func TestIssue3028(t *testing.T) {
|
||||
// Create two files that we'll delete, one with a name that is a prefix of the other.
|
||||
|
||||
must(t, writeFile(defaultFs, "testrm", []byte("Hello"), 0644))
|
||||
must(t, writeFile(defaultFs, "testrm2", []byte("Hello"), 0644))
|
||||
writeFile(t, defaultFs, "testrm", []byte("Hello"))
|
||||
writeFile(t, defaultFs, "testrm2", []byte("Hello"))
|
||||
defer func() {
|
||||
mustRemove(t, defaultFs.Remove("testrm"))
|
||||
mustRemove(t, defaultFs.Remove("testrm2"))
|
||||
@ -3403,7 +3403,7 @@ func TestRenameSequenceOrder(t *testing.T) {
|
||||
ffs := fcfg.Filesystem()
|
||||
for i := 0; i < numFiles; i++ {
|
||||
v := fmt.Sprintf("%d", i)
|
||||
must(t, writeFile(ffs, v, []byte(v), 0644))
|
||||
writeFile(t, ffs, v, []byte(v))
|
||||
}
|
||||
|
||||
m.ScanFolders()
|
||||
@ -3426,7 +3426,7 @@ func TestRenameSequenceOrder(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
v := fmt.Sprintf("%d", i)
|
||||
must(t, writeFile(ffs, v, []byte(v+"-new"), 0644))
|
||||
writeFile(t, ffs, v, []byte(v+"-new"))
|
||||
}
|
||||
// Rename
|
||||
must(t, ffs.Rename("3", "17"))
|
||||
@ -3470,7 +3470,7 @@ func TestRenameSameFile(t *testing.T) {
|
||||
defer cleanupModel(m)
|
||||
|
||||
ffs := fcfg.Filesystem()
|
||||
must(t, writeFile(ffs, "file", []byte("file"), 0644))
|
||||
writeFile(t, ffs, "file", []byte("file"))
|
||||
|
||||
m.ScanFolders()
|
||||
|
||||
@ -3522,8 +3522,8 @@ func TestRenameEmptyFile(t *testing.T) {
|
||||
|
||||
ffs := fcfg.Filesystem()
|
||||
|
||||
must(t, writeFile(ffs, "file", []byte("data"), 0644))
|
||||
must(t, writeFile(ffs, "empty", nil, 0644))
|
||||
writeFile(t, ffs, "file", []byte("data"))
|
||||
writeFile(t, ffs, "empty", nil)
|
||||
|
||||
m.ScanFolders()
|
||||
|
||||
@ -3598,11 +3598,11 @@ func TestBlockListMap(t *testing.T) {
|
||||
defer cleanupModel(m)
|
||||
|
||||
ffs := fcfg.Filesystem()
|
||||
must(t, writeFile(ffs, "one", []byte("content"), 0644))
|
||||
must(t, writeFile(ffs, "two", []byte("content"), 0644))
|
||||
must(t, writeFile(ffs, "three", []byte("content"), 0644))
|
||||
must(t, writeFile(ffs, "four", []byte("content"), 0644))
|
||||
must(t, writeFile(ffs, "five", []byte("content"), 0644))
|
||||
writeFile(t, ffs, "one", []byte("content"))
|
||||
writeFile(t, ffs, "two", []byte("content"))
|
||||
writeFile(t, ffs, "three", []byte("content"))
|
||||
writeFile(t, ffs, "four", []byte("content"))
|
||||
writeFile(t, ffs, "five", []byte("content"))
|
||||
|
||||
m.ScanFolders()
|
||||
|
||||
@ -3631,7 +3631,7 @@ func TestBlockListMap(t *testing.T) {
|
||||
|
||||
// Modify
|
||||
must(t, ffs.Remove("two"))
|
||||
must(t, writeFile(ffs, "two", []byte("mew-content"), 0644))
|
||||
writeFile(t, ffs, "two", []byte("mew-content"))
|
||||
|
||||
// Rename
|
||||
must(t, ffs.Rename("three", "new-three"))
|
||||
@ -3667,7 +3667,7 @@ func TestScanRenameCaseOnly(t *testing.T) {
|
||||
|
||||
ffs := fcfg.Filesystem()
|
||||
name := "foo"
|
||||
must(t, writeFile(ffs, name, []byte("contents"), 0644))
|
||||
writeFile(t, ffs, name, []byte("contents"))
|
||||
|
||||
m.ScanFolders()
|
||||
|
||||
@ -3791,7 +3791,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) {
|
||||
|
||||
name := "foo"
|
||||
|
||||
must(t, writeFile(ffs, name, []byte(name), 0644))
|
||||
writeFile(t, ffs, name, []byte(name))
|
||||
m.ScanFolders()
|
||||
|
||||
file, ok := m.testCurrentFolderFile(fcfg.ID, name)
|
||||
@ -4255,6 +4255,46 @@ func TestPendingFolder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletedNotLocallyChangedReceiveOnly(t *testing.T) {
|
||||
deletedNotLocallyChanged(t, config.FolderTypeReceiveOnly)
|
||||
}
|
||||
|
||||
func TestDeletedNotLocallyChangedReceiveEncrypted(t *testing.T) {
|
||||
deletedNotLocallyChanged(t, config.FolderTypeReceiveEncrypted)
|
||||
}
|
||||
|
||||
func deletedNotLocallyChanged(t *testing.T, ft config.FolderType) {
|
||||
w, fcfg, wCancel := tmpDefaultWrapper()
|
||||
tfs := fcfg.Filesystem()
|
||||
fcfg.Type = ft
|
||||
setFolder(t, w, fcfg)
|
||||
defer wCancel()
|
||||
m := setupModel(t, w)
|
||||
defer cleanupModelAndRemoveDir(m, tfs.URI())
|
||||
|
||||
name := "foo"
|
||||
writeFile(t, tfs, name, nil)
|
||||
must(t, m.ScanFolder(fcfg.ID))
|
||||
|
||||
fi, ok, err := m.CurrentFolderFile(fcfg.ID, name)
|
||||
must(t, err)
|
||||
if !ok {
|
||||
t.Fatal("File hasn't been added")
|
||||
}
|
||||
if !fi.IsReceiveOnlyChanged() {
|
||||
t.Fatal("File isn't receive-only-changed")
|
||||
}
|
||||
|
||||
must(t, tfs.Remove(name))
|
||||
must(t, m.ScanFolder(fcfg.ID))
|
||||
|
||||
_, ok, err = m.CurrentFolderFile(fcfg.ID, name)
|
||||
must(t, err)
|
||||
if ok {
|
||||
t.Error("Expected file to be removed from db")
|
||||
}
|
||||
}
|
||||
|
||||
func equalStringsInAnyOrder(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
|
@ -330,9 +330,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
fc.deleteFile(invDel)
|
||||
fc.addFile(ign, 0644, protocol.FileInfoTypeFile, contents)
|
||||
fc.addFile(ignExisting, 0644, protocol.FileInfoTypeFile, contents)
|
||||
if err := writeFile(fss, ignExisting, otherContents, 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writeFile(t, fss, ignExisting, otherContents)
|
||||
|
||||
done := make(chan struct{})
|
||||
fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
|
||||
@ -486,7 +484,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) {
|
||||
|
||||
payload := []byte("hello")
|
||||
|
||||
must(t, writeFile(tfs, "foo", payload, 0777))
|
||||
writeFile(t, tfs, "foo", payload)
|
||||
|
||||
received := make(chan []protocol.FileInfo)
|
||||
fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
|
||||
@ -526,7 +524,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) {
|
||||
payload = []byte("bye")
|
||||
buf = make([]byte, len(payload))
|
||||
|
||||
must(t, writeFile(tfs, "foo", payload, 0777))
|
||||
writeFile(t, tfs, "foo", payload)
|
||||
|
||||
_, err = m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false)
|
||||
if err == nil {
|
||||
@ -1066,9 +1064,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := writeFile(fss, file, contents, 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writeFile(t, fss, file, contents)
|
||||
m.ScanFolders()
|
||||
|
||||
select {
|
||||
@ -1430,6 +1426,14 @@ func TestRequestReceiveEncrypted(t *testing.T) {
|
||||
// Simulate request from device that is untrusted too, i.e. with non-empty, but garbage hash
|
||||
_, err := m.Request(device1, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false)
|
||||
must(t, err)
|
||||
|
||||
changed, err := m.LocalChangedFolderFiles(fcfg.ID, 1, 10)
|
||||
must(t, err)
|
||||
if l := len(changed); l != 1 {
|
||||
t.Errorf("Expected one locally changed file, got %v", l)
|
||||
} else if changed[0].Name != files[0].Name {
|
||||
t.Errorf("Expected %v, got %v", files[0].Name, changed[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestGlobalInvalidToValid(t *testing.T) {
|
||||
|
@ -435,3 +435,18 @@ func addDevice2(t testing.TB, w config.Wrapper, fcfg config.FolderConfiguration)
|
||||
must(t, err)
|
||||
waiter.Wait()
|
||||
}
|
||||
|
||||
func writeFile(t testing.TB, filesystem fs.Filesystem, name string, data []byte) {
|
||||
t.Helper()
|
||||
fd, err := filesystem.Create(name)
|
||||
must(t, err)
|
||||
defer fd.Close()
|
||||
_, err = fd.Write(data)
|
||||
must(t, err)
|
||||
}
|
||||
|
||||
func writeFilePerm(t testing.TB, filesystem fs.Filesystem, name string, data []byte, perm fs.FileMode) {
|
||||
t.Helper()
|
||||
writeFile(t, filesystem, name, data)
|
||||
must(t, filesystem.Chmod(name, perm))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user