Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb4fdaf4c0 | ||
|
|
0d7a77ba85 | ||
|
|
924b96856f | ||
|
|
f7929229c8 | ||
|
|
6b25eb2e79 | ||
|
|
bc08a951f1 | ||
|
|
a87c5515bd | ||
|
|
ebcd22b02b | ||
|
|
4b02b7e6f1 | ||
|
|
fdd823d2cb | ||
|
|
8ef504f745 | ||
|
|
960e850a78 | ||
|
|
ea701a4e9e | ||
|
|
6c573a5762 | ||
|
|
3ac858b150 | ||
|
|
f4372710bf | ||
|
|
f39477bbd5 | ||
|
|
6e5514419d | ||
|
|
f014b7b919 | ||
|
|
81484699db | ||
|
|
6d93d9c488 | ||
|
|
0930bccf88 | ||
|
|
e321bd3941 | ||
|
|
4b02937862 | ||
|
|
21e6849f2d | ||
|
|
39c2d1bc1a |
@@ -0,0 +1,12 @@
|
||||
version = 1
|
||||
|
||||
exclude_patterns = ["*.pb.go"]
|
||||
test_patterns = ["*_test.go"]
|
||||
|
||||
[[analyzers]]
|
||||
name = "go"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
import_paths = ["github.com/syncthing/syncthing"]
|
||||
build_tags = ["noassets"]
|
||||
+7
-10
@@ -7,7 +7,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -15,6 +14,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -74,7 +75,7 @@ type fileInfo struct {
|
||||
name string
|
||||
mode os.FileMode
|
||||
mod int64
|
||||
hash [16]byte
|
||||
hash [sha256.Size]byte
|
||||
}
|
||||
|
||||
func (f fileInfo) String() string {
|
||||
@@ -106,11 +107,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := md5.New()
|
||||
h.Write([]byte(tgt))
|
||||
hash := h.Sum(nil)
|
||||
|
||||
copy(f.hash[:], hash)
|
||||
f.hash = sha256.Sum256([]byte(tgt))
|
||||
} else if info.IsDir() {
|
||||
f = fileInfo{
|
||||
name: rn,
|
||||
@@ -123,7 +120,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
mode: info.Mode(),
|
||||
mod: info.ModTime().Unix(),
|
||||
}
|
||||
sum, err := md5file(path)
|
||||
sum, err := sha256file(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -150,14 +147,14 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
return errc
|
||||
}
|
||||
|
||||
func md5file(fname string) (hash [16]byte, err error) {
|
||||
func sha256file(fname string) (hash [sha256.Size]byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
h := sha256.New()
|
||||
io.Copy(h, f)
|
||||
hb := h.Sum(nil)
|
||||
copy(hash[:], hb)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2021 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//+build noassets
|
||||
|
||||
package auto
|
||||
|
||||
import "github.com/syncthing/syncthing/lib/assets"
|
||||
|
||||
func Assets() map[string]assets.Asset {
|
||||
return nil
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func main() {
|
||||
flag.IntVar(&natRenewal, "nat-renewal", 30, "NAT renewal frequency in minutes")
|
||||
flag.IntVar(&natTimeout, "nat-timeout", 10, "NAT discovery timeout in seconds")
|
||||
flag.BoolVar(&pprofEnabled, "pprof", false, "Enable the built in profiling on the status server")
|
||||
flag.IntVar(&networkBufferSize, "network-buffer", 2048, "Network buffer size (two of these per proxied connection)")
|
||||
flag.IntVar(&networkBufferSize, "network-buffer", 65536, "Network buffer size (two of these per proxied connection)")
|
||||
showVersion := flag.Bool("version", false, "Show version")
|
||||
flag.Parse()
|
||||
|
||||
@@ -233,6 +233,7 @@ func main() {
|
||||
uri, err := url.Parse(fmt.Sprintf("relay://%s/?id=%s&pingInterval=%s&networkTimeout=%s&sessionLimitBps=%d&globalLimitBps=%d&statusAddr=%s&providedBy=%s", mapping.Address(), id, pingInterval, networkTimeout, sessionLimitBps, globalLimitBps, statusAddr, providedBy))
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to construct URI", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("URI:", uri.String())
|
||||
|
||||
@@ -37,7 +37,7 @@ type result struct {
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
prefix := strings.ToUpper(strings.Replace(flag.Arg(0), "-", "", -1))
|
||||
prefix := strings.ToUpper(strings.ReplaceAll(flag.Arg(0), "-", ""))
|
||||
if len(prefix) > 7 {
|
||||
prefix = prefix[:7] + "-" + prefix[7:]
|
||||
}
|
||||
|
||||
+22
-23
@@ -7,31 +7,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
)
|
||||
|
||||
func getmd5(filePath string) ([]byte, error) {
|
||||
var result []byte
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hash := md5.New()
|
||||
if _, err := io.Copy(hash, file); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return hash.Sum(result), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
period := flag.Duration("period", 200*time.Millisecond, "Sleep period between checks")
|
||||
flag.Parse()
|
||||
@@ -46,7 +30,7 @@ func main() {
|
||||
exists := true
|
||||
size := int64(0)
|
||||
mtime := time.Time{}
|
||||
hash := []byte{}
|
||||
var hash [sha256.Size]byte
|
||||
|
||||
for {
|
||||
time.Sleep(*period)
|
||||
@@ -72,7 +56,7 @@ func main() {
|
||||
if !exists {
|
||||
size = 0
|
||||
mtime = time.Time{}
|
||||
hash = []byte{}
|
||||
hash = [sha256.Size]byte{}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -83,12 +67,12 @@ func main() {
|
||||
newSize := fi.Size()
|
||||
newMtime := fi.ModTime()
|
||||
|
||||
newHash, err := getmd5(file)
|
||||
newHash, err := sha256file(file)
|
||||
if err != nil {
|
||||
fmt.Println("getmd5:", err)
|
||||
fmt.Println("sha256file:", err)
|
||||
}
|
||||
|
||||
if newSize != size || newMtime != mtime || !bytes.Equal(newHash, hash) {
|
||||
if newSize != size || newMtime != mtime || newHash != hash {
|
||||
fmt.Println(file, "Size:", newSize, "Mtime:", newMtime, "Hash:", fmt.Sprintf("%x", newHash))
|
||||
hash = newHash
|
||||
size = newSize
|
||||
@@ -96,3 +80,18 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sha256file(fname string) (hash [sha256.Size]byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
io.Copy(h, f)
|
||||
hb := h.Sum(nil)
|
||||
copy(hash[:], hb)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/scanner"
|
||||
)
|
||||
@@ -79,7 +80,7 @@ func (c *CLI) walk() error {
|
||||
dstFs = fs.NewFilesystem(fs.FilesystemTypeBasic, c.To)
|
||||
}
|
||||
|
||||
return srcFs.Walk("/", func(path string, info fs.FileInfo, err error) error {
|
||||
return srcFs.Walk(".", func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -142,6 +143,10 @@ func (c *CLI) process(srcFs fs.Filesystem, dstFs fs.Filesystem, path string) err
|
||||
return fmt.Errorf("%s: loading metadata trailer: %w", path, err)
|
||||
}
|
||||
|
||||
// Workaround for a bug in <= v1.15.0-rc.5 where we stored names
|
||||
// in native format, while protocol expects wire format (slashes).
|
||||
encFi.Name = osutil.NormalizedFilename(encFi.Name)
|
||||
|
||||
plainFi, err := protocol.DecryptFileInfo(*encFi, c.folderKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: decrypting metadata: %w", path, err)
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
"github.com/syncthing/syncthing/lib/dialer"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
@@ -215,7 +216,7 @@ func main() {
|
||||
// The "cli" subcommand uses a different command line parser, and e.g. help
|
||||
// gets mangled when integrating it as a subcommand -> detect it here at the
|
||||
// beginning.
|
||||
if os.Args[1] == "cli" {
|
||||
if len(os.Args) > 1 && os.Args[1] == "cli" {
|
||||
if err := cli.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
@@ -392,7 +393,8 @@ func (options serveOptions) Run() error {
|
||||
release, err := checkUpgrade()
|
||||
if err == nil {
|
||||
// Use leveldb database locks to protect against concurrent upgrades
|
||||
ldb, err := syncthing.OpenDBBackend(locations.Get(locations.Database), config.TuningAuto)
|
||||
var ldb backend.Backend
|
||||
ldb, err = syncthing.OpenDBBackend(locations.Get(locations.Database), config.TuningAuto)
|
||||
if err != nil {
|
||||
err = upgradeViaRest()
|
||||
} else {
|
||||
|
||||
@@ -135,30 +135,30 @@ func setupDB(db *sql.DB) error {
|
||||
|
||||
row := db.QueryRow(`SELECT 'UniqueDayVersionIndex'::regclass`)
|
||||
if err := row.Scan(&t); err != nil {
|
||||
_, err = db.Exec(`CREATE UNIQUE INDEX UniqueDayVersionIndex ON VersionSummary (Day, Version)`)
|
||||
_, _ = db.Exec(`CREATE UNIQUE INDEX UniqueDayVersionIndex ON VersionSummary (Day, Version)`)
|
||||
}
|
||||
|
||||
row = db.QueryRow(`SELECT 'VersionDayIndex'::regclass`)
|
||||
if err := row.Scan(&t); err != nil {
|
||||
_, err = db.Exec(`CREATE INDEX VersionDayIndex ON VersionSummary (Day)`)
|
||||
_, _ = db.Exec(`CREATE INDEX VersionDayIndex ON VersionSummary (Day)`)
|
||||
}
|
||||
|
||||
row = db.QueryRow(`SELECT 'MovementDayIndex'::regclass`)
|
||||
if err := row.Scan(&t); err != nil {
|
||||
_, err = db.Exec(`CREATE INDEX MovementDayIndex ON UserMovement (Day)`)
|
||||
_, _ = db.Exec(`CREATE INDEX MovementDayIndex ON UserMovement (Day)`)
|
||||
}
|
||||
|
||||
row = db.QueryRow(`SELECT 'PerformanceDayIndex'::regclass`)
|
||||
if err := row.Scan(&t); err != nil {
|
||||
_, err = db.Exec(`CREATE INDEX PerformanceDayIndex ON Performance (Day)`)
|
||||
_, _ = db.Exec(`CREATE INDEX PerformanceDayIndex ON Performance (Day)`)
|
||||
}
|
||||
|
||||
row = db.QueryRow(`SELECT 'BlockStatsDayIndex'::regclass`)
|
||||
if err := row.Scan(&t); err != nil {
|
||||
_, err = db.Exec(`CREATE INDEX BlockStatsDayIndex ON BlockStats (Day)`)
|
||||
_, _ = db.Exec(`CREATE INDEX BlockStatsDayIndex ON BlockStats (Day)`)
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func maxIndexedDay(db *sql.DB, table string) time.Time {
|
||||
|
||||
+3
-3
@@ -89,7 +89,7 @@ var funcs = map[string]interface{}{
|
||||
parts = append(parts, part)
|
||||
}
|
||||
if len(input) > 0 {
|
||||
parts = append(parts, input[:])
|
||||
parts = append(parts, input)
|
||||
}
|
||||
return parts[whichPart-1]
|
||||
},
|
||||
@@ -725,8 +725,8 @@ func getReport(db *sql.DB) map[string]interface{} {
|
||||
|
||||
if rep.NATType != "" {
|
||||
natType := rep.NATType
|
||||
natType = strings.Replace(natType, "unknown", "Unknown", -1)
|
||||
natType = strings.Replace(natType, "Symetric", "Symmetric", -1)
|
||||
natType = strings.ReplaceAll(natType, "unknown", "Unknown")
|
||||
natType = strings.ReplaceAll(natType, "Symetric", "Symmetric")
|
||||
add(featureGroups["Various"]["v3"], "NAT Type", natType, 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
<div class="col-md-6 form-group">
|
||||
<label translate>Folder Type</label>
|
||||
<a href="https://docs.syncthing.net/users/foldertypes.html" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Help</span></a>
|
||||
<select class="form-control" ng-change="setDefaultsForFolderType()" ng-model="currentFolder.type" ng-disabled="editingExisting && currentFolder.type === 'receiveencrypted'">
|
||||
<select class="form-control" ng-change="setDefaultsForFolderType()" ng-model="currentFolder.type" ng-disabled="editingExisting && currentFolder.type == 'receiveencrypted'">
|
||||
<option value="sendreceive" translate>Send & Receive</option>
|
||||
<option value="sendonly" translate>Send Only</option>
|
||||
<option value="receiveonly" translate>Receive Only</option>
|
||||
@@ -214,7 +214,8 @@
|
||||
<p ng-if="currentFolder.type == 'sendonly'" translate class="help-block">Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.</p>
|
||||
<p ng-if="currentFolder.type == 'receiveonly'" translate class="help-block">Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.</p>
|
||||
<p ng-if="currentFolder.type == 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type "{%receiveEncrypted%}" too.</p>
|
||||
<p ng-if="editingExisting" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.</p>
|
||||
<p ng-if="editingExisting && currentFolder.type == 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.</p>
|
||||
<p ng-if="editingExisting && currentFolder.type != 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" can only be set when adding a new folder.</p>
|
||||
</div>
|
||||
<div class="col-md-6 form-group">
|
||||
<label translate>File Pull Order</label>
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sort"
|
||||
@@ -60,9 +59,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/ur"
|
||||
)
|
||||
|
||||
// matches a bcrypt hash and not too much else
|
||||
var bcryptExpr = regexp.MustCompile(`^\$2[aby]\$\d+\$.{50,}`)
|
||||
|
||||
const (
|
||||
DefaultEventMask = events.AllEvents &^ events.LocalChangeDetected &^ events.RemoteChangeDetected
|
||||
DiskEventMask = events.LocalChangeDetected | events.RemoteChangeDetected
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2021 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//+build noassets
|
||||
|
||||
package auto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/assets"
|
||||
)
|
||||
|
||||
func Assets() map[string]assets.Asset {
|
||||
// Return a minimal index.html and nothing else, to allow the trivial
|
||||
// test to pass.
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
gw := gzip.NewWriter(buf)
|
||||
_, _ = gw.Write([]byte("<html></html>"))
|
||||
_ = gw.Flush()
|
||||
return map[string]assets.Asset{
|
||||
"default/index.html": {
|
||||
Gzipped: true,
|
||||
Content: buf.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -187,6 +187,10 @@ func (c *configMuxBuilder) registerDevice(path string) {
|
||||
|
||||
c.Handle(http.MethodDelete, path, func(w http.ResponseWriter, _ *http.Request, p httprouter.Params) {
|
||||
id, err := protocol.DeviceIDFromString(p.ByName("id"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
waiter, err := c.cfg.RemoveDevice(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
|
||||
@@ -90,10 +90,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
|
||||
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority), nil
|
||||
}
|
||||
|
||||
type quicDialerFactory struct {
|
||||
cfg config.Wrapper
|
||||
tlsCfg *tls.Config
|
||||
}
|
||||
type quicDialerFactory struct{}
|
||||
|
||||
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
|
||||
return &quicDialer{commonDialer{
|
||||
|
||||
@@ -79,7 +79,7 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string)
|
||||
}
|
||||
|
||||
func (t *quicListener) serve(ctx context.Context) error {
|
||||
network := strings.Replace(t.uri.Scheme, "quic", "udp", -1)
|
||||
network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp")
|
||||
|
||||
packetConn, err := net.ListenPacket(network, t.uri.Host)
|
||||
if err != nil {
|
||||
@@ -174,7 +174,7 @@ func (t *quicListener) WANAddresses() []*url.URL {
|
||||
|
||||
func (t *quicListener) LANAddresses() []*url.URL {
|
||||
addrs := []*url.URL{t.uri}
|
||||
network := strings.Replace(t.uri.Scheme, "quic", "udp", -1)
|
||||
network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp")
|
||||
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(network, t.uri)...)
|
||||
return addrs
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ func (s *service) handle(ctx context.Context) error {
|
||||
// though, especially in the presence of NAT hairpinning, multiple
|
||||
// clients between the same NAT gateway, and global discovery.
|
||||
if remoteID == s.myID {
|
||||
l.Infof("Connected to myself (%s) at %s - should not happen", remoteID, c)
|
||||
l.Debugf("Connected to myself (%s) at %s", remoteID, c)
|
||||
c.Close()
|
||||
continue
|
||||
}
|
||||
@@ -335,13 +335,7 @@ func (s *service) handle(ctx context.Context) error {
|
||||
isLAN := s.isLAN(c.RemoteAddr())
|
||||
rd, wr := s.limiter.getLimiters(remoteID, c, isLAN)
|
||||
|
||||
var protoConn protocol.Connection
|
||||
passwords := s.cfg.FolderPasswords(remoteID)
|
||||
if len(passwords) > 0 {
|
||||
protoConn = protocol.NewEncryptedConnection(passwords, remoteID, rd, wr, c, s.model, c, deviceCfg.Compression)
|
||||
} else {
|
||||
protoConn = protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression)
|
||||
}
|
||||
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID))
|
||||
|
||||
l.Infof("Established secure connection to %s at %s", remoteID, c)
|
||||
|
||||
@@ -478,7 +472,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
|
||||
// doesn't have much effect, but it may result in getting up and running
|
||||
// quicker if only a subset of configured devices are actually reachable
|
||||
// (by prioritizing those that were reachable recently).
|
||||
dialQueue.Sort(queue)
|
||||
queue.Sort()
|
||||
|
||||
// Perform dials according to the queue, stopping when we've reached the
|
||||
// allowed additional number of connections (if limited).
|
||||
@@ -1023,7 +1017,7 @@ func (s *service) validateIdentity(c internalConn, expectedID protocol.DeviceID)
|
||||
// though, especially in the presence of NAT hairpinning, multiple
|
||||
// clients between the same NAT gateway, and global discovery.
|
||||
if remoteID == s.myID {
|
||||
l.Infof("Connected to myself (%s) at %s - should not happen", remoteID, c)
|
||||
l.Debugf("Connected to myself (%s) at %s", remoteID, c)
|
||||
c.Close()
|
||||
return errors.New("connected to self")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
|
||||
var files, filesUpdated, oneFile, firstHalf, secondHalf, changed100, unchanged100 []protocol.FileInfo
|
||||
var files, oneFile, firstHalf, secondHalf, changed100, unchanged100 []protocol.FileInfo
|
||||
|
||||
func lazyInitBenchFiles() {
|
||||
if files != nil {
|
||||
|
||||
@@ -262,6 +262,9 @@ func TestUpdate0to3(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, err = trans.keyer.GenerateDeviceFileKey(key, folder, vl.Versions[0].Device, name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fi, ok, err := trans.getFileTrunc(key, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -932,6 +932,9 @@ func (db *Lowlevel) recalcMeta(folderStr string) (*metadataTracker, error) {
|
||||
meta.addFile(protocol.GlobalDeviceID, f)
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta.emptyNeeded(protocol.LocalDeviceID)
|
||||
err = t.withNeed(folder, protocol.LocalDeviceID[:], true, func(f protocol.FileIntf) bool {
|
||||
|
||||
+2
-3
@@ -148,10 +148,9 @@ func (m *metadataTracker) countsPtr(dev protocol.DeviceID, flag uint32) *Counts
|
||||
// the metadatatracker, even if there's no change to the need
|
||||
// bucket itself.
|
||||
nkey := metaKey{dev, needFlag}
|
||||
nidx, ok := m.indexes[nkey]
|
||||
if !ok {
|
||||
if _, ok := m.indexes[nkey]; !ok {
|
||||
// Initially a new device needs everything, except deletes
|
||||
nidx = len(m.counts.Counts)
|
||||
nidx := len(m.counts.Counts)
|
||||
m.counts.Counts = append(m.counts.Counts, m.allNeededCounts(dev))
|
||||
m.indexes[nkey] = nidx
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -32,8 +31,6 @@ type migration struct {
|
||||
migration func(prevSchema int) error
|
||||
}
|
||||
|
||||
var errFolderMissing = errors.New("folder present in global list but missing in keyer index")
|
||||
|
||||
type databaseDowngradeError struct {
|
||||
minSyncthingVersion string
|
||||
}
|
||||
@@ -730,6 +727,9 @@ func (db *schemaUpdater) updateSchemaTo14(_ int) error {
|
||||
defer t.close()
|
||||
|
||||
key, err = t.keyer.GenerateDeviceFileKey(key, folder, protocol.LocalDeviceID[:], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it, err := t.NewPrefixIterator(key)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -396,25 +396,6 @@ func (vl *VersionList) findDevice(device []byte) (bool, int, int, bool) {
|
||||
return false, -1, -1, false
|
||||
}
|
||||
|
||||
func (vl *VersionList) popVersion(version protocol.Vector) (FileVersion, bool) {
|
||||
i := vl.versionIndex(version)
|
||||
if i == -1 {
|
||||
return FileVersion{}, false
|
||||
}
|
||||
fv := vl.RawVersions[i]
|
||||
vl.popVersionAt(i)
|
||||
return fv, true
|
||||
}
|
||||
|
||||
func (vl *VersionList) versionIndex(version protocol.Vector) int {
|
||||
for i, v := range vl.RawVersions {
|
||||
if version.Equal(v.Version) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (vl *VersionList) popVersionAt(i int) {
|
||||
vl.RawVersions = append(vl.RawVersions[:i], vl.RawVersions[i+1:]...)
|
||||
}
|
||||
@@ -496,14 +477,6 @@ func popDeviceAt(devices [][]byte, i int) [][]byte {
|
||||
return append(devices[:i], devices[i+1:]...)
|
||||
}
|
||||
|
||||
func popDevice(devices [][]byte, device []byte) ([][]byte, bool) {
|
||||
i := deviceIndex(devices, device)
|
||||
if i == -1 {
|
||||
return devices, false
|
||||
}
|
||||
return popDeviceAt(devices, i), true
|
||||
}
|
||||
|
||||
func newFileVersion(device []byte, version protocol.Vector, invalid, deleted bool) FileVersion {
|
||||
fv := FileVersion{
|
||||
Version: version,
|
||||
|
||||
@@ -237,7 +237,6 @@ type logger struct {
|
||||
events chan Event
|
||||
funcs chan func(context.Context)
|
||||
toUnsubscribe chan *subscription
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
|
||||
@@ -252,8 +252,7 @@ func Canonicalize(file string) (string, error) {
|
||||
file = filepath.Clean(file)
|
||||
|
||||
// It is not acceptable to attempt to traverse upwards.
|
||||
switch file {
|
||||
case "..":
|
||||
if file == ".." {
|
||||
return "", errNotRelative
|
||||
}
|
||||
if strings.HasPrefix(file, ".."+pathSep) {
|
||||
|
||||
+3
-4
@@ -7,11 +7,12 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -50,9 +51,7 @@ func TempNameWithPrefix(name, prefix string) string {
|
||||
tdir := filepath.Dir(name)
|
||||
tbase := filepath.Base(name)
|
||||
if len(tbase) > maxFilenameLength {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(name))
|
||||
tbase = fmt.Sprintf("%x", hash.Sum(nil))
|
||||
tbase = fmt.Sprintf("%x", sha256.Sum256([]byte(name)))
|
||||
}
|
||||
tname := fmt.Sprintf("%s%s.tmp", prefix, tbase)
|
||||
return filepath.Join(tdir, tname)
|
||||
|
||||
@@ -9,7 +9,6 @@ package ignore
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
@@ -373,7 +373,7 @@ func (m *Matcher) SkipIgnoredDirs() bool {
|
||||
}
|
||||
|
||||
func hashPatterns(patterns []Pattern) string {
|
||||
h := md5.New()
|
||||
h := sha256.New()
|
||||
for _, pat := range patterns {
|
||||
h.Write([]byte(pat.String()))
|
||||
h.Write([]byte("\n"))
|
||||
|
||||
@@ -607,8 +607,9 @@ func TestHashOfEmpty(t *testing.T) {
|
||||
firstHash := p1.Hash()
|
||||
|
||||
// Reloading with a non-existent file should empty the patterns and
|
||||
// recalculate the hash. d41d8cd98f00b204e9800998ecf8427e is the md5 of
|
||||
// nothing.
|
||||
// recalculate the hash.
|
||||
// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 is
|
||||
// the sah256 of nothing.
|
||||
|
||||
p1.Load("file/does/not/exist")
|
||||
secondHash := p1.Hash()
|
||||
@@ -616,7 +617,7 @@ func TestHashOfEmpty(t *testing.T) {
|
||||
if firstHash == secondHash {
|
||||
t.Error("hash did not change")
|
||||
}
|
||||
if secondHash != "d41d8cd98f00b204e9800998ecf8427e" {
|
||||
if secondHash != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
||||
t.Error("second hash is not hash of empty string")
|
||||
}
|
||||
if len(p1.patterns) != 0 {
|
||||
|
||||
@@ -115,7 +115,7 @@ func expandLocations() error {
|
||||
newLocations := make(map[LocationEnum]string)
|
||||
for key, dir := range locationTemplates {
|
||||
for varName, value := range baseDirs {
|
||||
dir = strings.Replace(dir, "${"+string(varName)+"}", value, -1)
|
||||
dir = strings.ReplaceAll(dir, "${"+string(varName)+"}", value)
|
||||
}
|
||||
var err error
|
||||
dir, err = fs.ExpandTilde(dir)
|
||||
@@ -197,5 +197,5 @@ func GetTimestamped(key LocationEnum) string {
|
||||
// 2006 replaced by 2015...
|
||||
tpl := locations[key]
|
||||
now := time.Now().Format("20060102-150405")
|
||||
return strings.Replace(tpl, "${timestamp}", now, -1)
|
||||
return strings.ReplaceAll(tpl, "${timestamp}", now)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -46,9 +47,6 @@ func Test_chunk(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_inOrderBlockPullReorderer_Reorder(t *testing.T) {
|
||||
type args struct {
|
||||
blocks []protocol.BlockInfo
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
blocks []protocol.BlockInfo
|
||||
|
||||
@@ -33,7 +33,7 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection {
|
||||
})
|
||||
f.IDReturns(id)
|
||||
f.CloseCalls(func(err error) {
|
||||
model.Closed(f, err)
|
||||
model.Closed(id, err)
|
||||
f.ClosedReturns(true)
|
||||
})
|
||||
return f
|
||||
|
||||
@@ -1496,11 +1496,12 @@ func (f *sendReceiveFolder) pullBlock(state pullBlockState, snap *db.Snapshot, o
|
||||
|
||||
var lastError error
|
||||
candidates := f.model.availabilityInSnapshot(f.FolderConfiguration, snap, state.file, state.block)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
state.fail(errors.Wrap(f.ctx.Err(), "folder stopped"))
|
||||
break
|
||||
break loop
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ type indexSender struct {
|
||||
conn protocol.Connection
|
||||
folder string
|
||||
folderIsReceiveEncrypted bool
|
||||
dev string
|
||||
fset *db.FileSet
|
||||
prevSequence int64
|
||||
evLogger events.Logger
|
||||
@@ -316,9 +315,7 @@ func (r *indexSenderRegistry) addLocked(folder config.FolderConfiguration, fset
|
||||
r.sup.RemoveAndWait(is.token, 0)
|
||||
delete(r.indexSenders, folder.ID)
|
||||
}
|
||||
if _, ok := r.startInfos[folder.ID]; ok {
|
||||
delete(r.startInfos, folder.ID)
|
||||
}
|
||||
delete(r.startInfos, folder.ID)
|
||||
|
||||
is := &indexSender{
|
||||
conn: r.conn,
|
||||
|
||||
@@ -43,10 +43,10 @@ type Model struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}
|
||||
ClosedStub func(protocol.Connection, error)
|
||||
ClosedStub func(protocol.DeviceID, error)
|
||||
closedMutex sync.RWMutex
|
||||
closedArgsForCall []struct {
|
||||
arg1 protocol.Connection
|
||||
arg1 protocol.DeviceID
|
||||
arg2 error
|
||||
}
|
||||
ClusterConfigStub func(protocol.DeviceID, protocol.ClusterConfig) error
|
||||
@@ -684,10 +684,10 @@ func (fake *Model) BringToFrontArgsForCall(i int) (string, string) {
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *Model) Closed(arg1 protocol.Connection, arg2 error) {
|
||||
func (fake *Model) Closed(arg1 protocol.DeviceID, arg2 error) {
|
||||
fake.closedMutex.Lock()
|
||||
fake.closedArgsForCall = append(fake.closedArgsForCall, struct {
|
||||
arg1 protocol.Connection
|
||||
arg1 protocol.DeviceID
|
||||
arg2 error
|
||||
}{arg1, arg2})
|
||||
stub := fake.ClosedStub
|
||||
@@ -704,13 +704,13 @@ func (fake *Model) ClosedCallCount() int {
|
||||
return len(fake.closedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) ClosedCalls(stub func(protocol.Connection, error)) {
|
||||
func (fake *Model) ClosedCalls(stub func(protocol.DeviceID, error)) {
|
||||
fake.closedMutex.Lock()
|
||||
defer fake.closedMutex.Unlock()
|
||||
fake.ClosedStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) ClosedArgsForCall(i int) (protocol.Connection, error) {
|
||||
func (fake *Model) ClosedArgsForCall(i int) (protocol.DeviceID, error) {
|
||||
fake.closedMutex.RLock()
|
||||
defer fake.closedMutex.RUnlock()
|
||||
argsForCall := fake.closedArgsForCall[i]
|
||||
|
||||
+52
-36
@@ -184,7 +184,6 @@ var (
|
||||
errNetworkNotAllowed = errors.New("network not allowed")
|
||||
errNoVersioner = errors.New("folder has no versioner")
|
||||
// errors about why a connection is closed
|
||||
errIgnoredFolderRemoved = errors.New("folder no longer ignored")
|
||||
errReplacingConnection = errors.New("replacing connection")
|
||||
errStopped = errors.New("Syncthing is being stopped")
|
||||
errEncryptionInvConfigLocal = errors.New("can't encrypt data for a device when the folder type is receiveEncrypted")
|
||||
@@ -193,10 +192,12 @@ var (
|
||||
errEncryptionNotEncryptedRemote = errors.New("folder is configured to be encrypted but not announced thus")
|
||||
errEncryptionNotEncryptedUntrusted = errors.New("device is untrusted, but configured to receive not encrypted data")
|
||||
errEncryptionPassword = errors.New("different encryption passwords used")
|
||||
errEncryptionReceivedToken = errors.New("resetting connection to send info on new encrypted folder (new cluster config)")
|
||||
errEncryptionNeedToken = errors.New("require password token for receive-encrypted token")
|
||||
errMissingRemoteInClusterConfig = errors.New("remote device missing in cluster config")
|
||||
errMissingLocalInClusterConfig = errors.New("local device missing in cluster config")
|
||||
errConnLimitReached = errors.New("connection limit reached")
|
||||
// messages for failure reports
|
||||
failureUnexpectedGenerateCCError = "unexpected error occurred in generateClusterConfig"
|
||||
)
|
||||
|
||||
// NewModel creates and starts a new model. The model starts in read-only mode,
|
||||
@@ -294,7 +295,7 @@ func (m *model) initFolders(cfg config.Configuration) error {
|
||||
ignoredDevices := observedDeviceSet(m.cfg.IgnoredDevices())
|
||||
m.cleanPending(cfg.DeviceMap(), cfg.FolderMap(), ignoredDevices, nil)
|
||||
|
||||
m.resendClusterConfig(clusterConfigDevices.AsSlice())
|
||||
m.sendClusterConfig(clusterConfigDevices.AsSlice())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -693,7 +694,7 @@ func (m *model) UsageReportingStats(report *contract.Report, version int, previe
|
||||
if strings.Contains(line, "**") {
|
||||
report.IgnoreStats.DoubleStars++
|
||||
// Remove not to trip up star checks.
|
||||
line = strings.Replace(line, "**", "", -1)
|
||||
line = strings.ReplaceAll(line, "**", "")
|
||||
}
|
||||
|
||||
if strings.Contains(line, "*") {
|
||||
@@ -1366,7 +1367,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
||||
}
|
||||
m.folderEncryptionFailures[folder.ID][deviceID] = err
|
||||
msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
|
||||
if sameError || err == errEncryptionReceivedToken {
|
||||
if sameError {
|
||||
l.Debugln(msg)
|
||||
} else {
|
||||
l.Warnln(msg)
|
||||
@@ -1511,7 +1512,7 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
|
||||
m.fmut.Unlock()
|
||||
// We can only announce ourselfs once we have the token,
|
||||
// thus we need to resend CCs now that we have it.
|
||||
m.resendClusterConfig(fcfg.DeviceIDs())
|
||||
m.sendClusterConfig(fcfg.DeviceIDs())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1521,7 +1522,7 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) resendClusterConfig(ids []protocol.DeviceID) {
|
||||
func (m *model) sendClusterConfig(ids []protocol.DeviceID) {
|
||||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -1535,7 +1536,16 @@ func (m *model) resendClusterConfig(ids []protocol.DeviceID) {
|
||||
m.pmut.RUnlock()
|
||||
// Generating cluster-configs acquires fmut -> must happen outside of pmut.
|
||||
for _, conn := range ccConns {
|
||||
cm := m.generateClusterConfig(conn.ID())
|
||||
cm, passwords, err := m.generateClusterConfig(conn.ID())
|
||||
if err != nil {
|
||||
if err != errEncryptionNeedToken {
|
||||
m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError)
|
||||
continue
|
||||
}
|
||||
go conn.Close(err)
|
||||
continue
|
||||
}
|
||||
conn.SetFolderPasswords(passwords)
|
||||
go conn.ClusterConfig(cm)
|
||||
}
|
||||
}
|
||||
@@ -1701,15 +1711,6 @@ func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Fo
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) newFolderConfiguration(id, label string, fsType fs.FilesystemType, path string) config.FolderConfiguration {
|
||||
fcfg := m.cfg.DefaultFolder()
|
||||
fcfg.ID = id
|
||||
fcfg.Label = label
|
||||
fcfg.FilesystemType = fsType
|
||||
fcfg.Path = path
|
||||
return fcfg
|
||||
}
|
||||
|
||||
func (m *model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) config.DeviceConfiguration {
|
||||
addresses := []string{"dynamic"}
|
||||
for _, addr := range device.Addresses {
|
||||
@@ -1738,9 +1739,7 @@ func (m *model) introduceDevice(device protocol.Device, introducerCfg config.Dev
|
||||
}
|
||||
|
||||
// Closed is called when a connection has been closed
|
||||
func (m *model) Closed(conn protocol.Connection, err error) {
|
||||
device := conn.ID()
|
||||
|
||||
func (m *model) Closed(device protocol.DeviceID, err error) {
|
||||
m.pmut.Lock()
|
||||
conn, ok := m.conn[device]
|
||||
if !ok {
|
||||
@@ -2257,7 +2256,13 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
|
||||
m.pmut.Unlock()
|
||||
|
||||
// Acquires fmut, so has to be done outside of pmut.
|
||||
cm := m.generateClusterConfig(deviceID)
|
||||
cm, passwords, err := m.generateClusterConfig(deviceID)
|
||||
// We ignore errEncryptionNeedToken on a new connection, as the missing
|
||||
// token should be delivered in the cluster-config about to be received.
|
||||
if err != nil && err != errEncryptionNeedToken {
|
||||
m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError)
|
||||
}
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.ClusterConfig(cm)
|
||||
|
||||
if (device.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" {
|
||||
@@ -2417,15 +2422,17 @@ func (m *model) numHashers(folder string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// generateClusterConfig returns a ClusterConfigMessage that is correct for
|
||||
// the given peer device
|
||||
func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig {
|
||||
// generateClusterConfig returns a ClusterConfigMessage that is correct and the
|
||||
// set of folder passwords for the given peer device
|
||||
func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string, error) {
|
||||
var message protocol.ClusterConfig
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
|
||||
for _, folderCfg := range m.cfg.FolderList() {
|
||||
folders := m.cfg.FolderList()
|
||||
passwords := make(map[string]string, len(folders))
|
||||
for _, folderCfg := range folders {
|
||||
if !folderCfg.SharedWith(device) {
|
||||
continue
|
||||
}
|
||||
@@ -2434,10 +2441,10 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
|
||||
var hasEncryptionToken bool
|
||||
if folderCfg.Type == config.FolderTypeReceiveEncrypted {
|
||||
if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken {
|
||||
// We haven't gotten a token for us yet and without
|
||||
// one the other side can't validate us - pretend
|
||||
// we don't have the folder yet.
|
||||
continue
|
||||
// We haven't gotten a token yet and without one the other side
|
||||
// can't validate us - reset the connection to trigger a new
|
||||
// cluster-config and get the token.
|
||||
return message, nil, errEncryptionNeedToken
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2458,8 +2465,8 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
|
||||
// another cluster config once the folder is started.
|
||||
protocolFolder.Paused = folderCfg.Paused || fs == nil
|
||||
|
||||
for _, device := range folderCfg.Devices {
|
||||
deviceCfg, _ := m.cfg.Device(device.DeviceID)
|
||||
for _, folderDevice := range folderCfg.Devices {
|
||||
deviceCfg, _ := m.cfg.Device(folderDevice.DeviceID)
|
||||
|
||||
protocolDevice := protocol.Device{
|
||||
ID: deviceCfg.DeviceID,
|
||||
@@ -2472,8 +2479,11 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
|
||||
|
||||
if deviceCfg.DeviceID == m.id && hasEncryptionToken {
|
||||
protocolDevice.EncryptionPasswordToken = encryptionToken
|
||||
} else if device.EncryptionPassword != "" {
|
||||
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(folderCfg.ID, device.EncryptionPassword)
|
||||
} else if folderDevice.EncryptionPassword != "" {
|
||||
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(folderCfg.ID, folderDevice.EncryptionPassword)
|
||||
if folderDevice.DeviceID == device {
|
||||
passwords[folderCfg.ID] = folderDevice.EncryptionPassword
|
||||
}
|
||||
}
|
||||
|
||||
if fs != nil {
|
||||
@@ -2492,7 +2502,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
|
||||
message.Folders = append(message.Folders, protocolFolder)
|
||||
}
|
||||
|
||||
return message
|
||||
return message, passwords, nil
|
||||
}
|
||||
|
||||
func (m *model) State(folder string) (string, time.Time, error) {
|
||||
@@ -2717,10 +2727,16 @@ func (m *model) Availability(folder string, file protocol.FileInfo, block protoc
|
||||
}
|
||||
defer snap.Release()
|
||||
|
||||
return m.availabilityInSnapshot(cfg, snap, file, block), nil
|
||||
return m.availabilityInSnapshotPRlocked(cfg, snap, file, block), nil
|
||||
}
|
||||
|
||||
func (m *model) availabilityInSnapshot(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
||||
m.pmut.RLock()
|
||||
defer m.pmut.RUnlock()
|
||||
return m.availabilityInSnapshotPRlocked(cfg, snap, file, block)
|
||||
}
|
||||
|
||||
func (m *model) availabilityInSnapshotPRlocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
||||
var availabilities []Availability
|
||||
for _, device := range snap.Availability(file.Name) {
|
||||
if _, ok := m.remotePausedFolders[device]; !ok {
|
||||
@@ -2895,7 +2911,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
// Generating cluster-configs acquires fmut -> must happen outside of pmut.
|
||||
m.resendClusterConfig(clusterConfigDevices.AsSlice())
|
||||
m.sendClusterConfig(clusterConfigDevices.AsSlice())
|
||||
|
||||
ignoredDevices := observedDeviceSet(to.IgnoredDevices)
|
||||
m.cleanPending(toDevices, toFolders, ignoredDevices, removedFolders)
|
||||
|
||||
+16
-10
@@ -341,7 +341,7 @@ func TestDeviceRename(t *testing.T) {
|
||||
t.Errorf("Device already has a name")
|
||||
}
|
||||
|
||||
m.Closed(conn, protocol.ErrTimeout)
|
||||
m.Closed(conn.ID(), protocol.ErrTimeout)
|
||||
hello.DeviceName = "tester"
|
||||
m.AddConnection(conn, hello)
|
||||
|
||||
@@ -349,7 +349,7 @@ func TestDeviceRename(t *testing.T) {
|
||||
t.Errorf("Device did not get a name")
|
||||
}
|
||||
|
||||
m.Closed(conn, protocol.ErrTimeout)
|
||||
m.Closed(conn.ID(), protocol.ErrTimeout)
|
||||
hello.DeviceName = "tester2"
|
||||
m.AddConnection(conn, hello)
|
||||
|
||||
@@ -367,7 +367,7 @@ func TestDeviceRename(t *testing.T) {
|
||||
t.Errorf("Device name not saved in config")
|
||||
}
|
||||
|
||||
m.Closed(conn, protocol.ErrTimeout)
|
||||
m.Closed(conn.ID(), protocol.ErrTimeout)
|
||||
|
||||
waiter, err := cfg.Modify(func(cfg *config.Configuration) {
|
||||
cfg.Options.OverwriteRemoteDevNames = true
|
||||
@@ -428,7 +428,8 @@ func TestClusterConfig(t *testing.T) {
|
||||
m.ServeBackground()
|
||||
defer cleanupModel(m)
|
||||
|
||||
cm := m.generateClusterConfig(device2)
|
||||
cm, _, err := m.generateClusterConfig(device2)
|
||||
must(t, err)
|
||||
|
||||
if l := len(cm.Folders); l != 2 {
|
||||
t.Fatalf("Incorrect number of folders %d != 2", l)
|
||||
@@ -853,7 +854,8 @@ func TestIssue4897(t *testing.T) {
|
||||
defer cleanupModel(m)
|
||||
cancel()
|
||||
|
||||
cm := m.generateClusterConfig(device1)
|
||||
cm, _, err := m.generateClusterConfig(device1)
|
||||
must(t, err)
|
||||
if l := len(cm.Folders); l != 1 {
|
||||
t.Errorf("Cluster config contains %v folders, expected 1", l)
|
||||
}
|
||||
@@ -873,7 +875,7 @@ func TestIssue5063(t *testing.T) {
|
||||
for _, c := range m.conn {
|
||||
conn := c.(*fakeConnection)
|
||||
conn.CloseCalls(func(_ error) {})
|
||||
defer m.Closed(c, errStopped) // to unblock deferred m.Stop()
|
||||
defer m.Closed(c.ID(), errStopped) // to unblock deferred m.Stop()
|
||||
}
|
||||
m.pmut.Unlock()
|
||||
|
||||
@@ -2428,8 +2430,8 @@ func TestNoRequestsFromPausedDevices(t *testing.T) {
|
||||
t.Errorf("should have two available")
|
||||
}
|
||||
|
||||
m.Closed(newFakeConnection(device1, m), errDeviceUnknown)
|
||||
m.Closed(newFakeConnection(device2, m), errDeviceUnknown)
|
||||
m.Closed(device1, errDeviceUnknown)
|
||||
m.Closed(device2, errDeviceUnknown)
|
||||
|
||||
avail = m.testAvailability("default", file, file.Blocks[0])
|
||||
if len(avail) != 0 {
|
||||
@@ -3171,7 +3173,7 @@ func TestConnCloseOnRestart(t *testing.T) {
|
||||
|
||||
br := &testutils.BlockingRW{}
|
||||
nw := &testutils.NoopRW{}
|
||||
m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever), protocol.Hello{})
|
||||
m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil), protocol.Hello{})
|
||||
m.pmut.RLock()
|
||||
if len(m.closed) != 1 {
|
||||
t.Fatalf("Expected just one conn (len(m.conn) == %v)", len(m.conn))
|
||||
@@ -4142,7 +4144,8 @@ func TestCCFolderNotRunning(t *testing.T) {
|
||||
defer cleanupModelAndRemoveDir(m, tfs.URI())
|
||||
|
||||
// A connection can happen before all the folders are started.
|
||||
cc := m.generateClusterConfig(device1)
|
||||
cc, _, err := m.generateClusterConfig(device1)
|
||||
must(t, err)
|
||||
if l := len(cc.Folders); l != 1 {
|
||||
t.Fatalf("Expected 1 folder in CC, got %v", l)
|
||||
}
|
||||
@@ -4185,6 +4188,9 @@ func TestPendingFolder(t *testing.T) {
|
||||
}
|
||||
|
||||
device3, err := protocol.DeviceIDFromString("AIBAEAQ-CAIBAEC-AQCAIBA-EAQCAIA-BAEAQCA-IBAEAQC-CAIBAEA-QCAIBA7")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setDevice(t, w, config.DeviceConfiguration{DeviceID: device3})
|
||||
if err := m.db.AddOrUpdatePendingFolder(pfolder, pfolder, device3, false); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
@@ -346,8 +347,13 @@ func (s *sharedPullerState) finalClose() (bool, error) {
|
||||
// folder from encrypted data we can extract this FileInfo from the end of
|
||||
// the file and regain the original metadata.
|
||||
func (s *sharedPullerState) finalizeEncrypted() error {
|
||||
bs := make([]byte, encryptionTrailerSize(s.file))
|
||||
n, err := s.file.MarshalTo(bs)
|
||||
// Here the file is in native format, while encryption happens in
|
||||
// wire format (always slashes).
|
||||
wireFile := s.file
|
||||
wireFile.Name = osutil.NormalizedFilename(wireFile.Name)
|
||||
|
||||
bs := make([]byte, encryptionTrailerSize(wireFile))
|
||||
n, err := wireFile.MarshalTo(bs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -359,7 +365,7 @@ func (s *sharedPullerState) finalizeEncrypted() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := s.writer.WriteAt(bs, s.file.Size); err != nil {
|
||||
if _, err := s.writer.WriteAt(bs, wireFile.Size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2015 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package osutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func interfaceAddresses(network string, intf *net.Interface) []string {
|
||||
var out []string
|
||||
addrs, err := intf.Addrs()
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if ok && (network == "tcp" || (network == "tcp4" && len(ipnet.IP) == net.IPv4len) || (network == "tcp6" && len(ipnet.IP) == net.IPv6len)) {
|
||||
out = append(out, ipnet.IP.String())
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func MaximizeOpenFileLimit() (int, error) {
|
||||
// macOS doesn't like a soft limit greater then OPEN_MAX
|
||||
// See also: man setrlimit
|
||||
if runtime.GOOS == "darwin" && lim.Max > darwinOpenMax {
|
||||
lim.Cur = darwinOpenMax
|
||||
lim.Max = darwinOpenMax
|
||||
}
|
||||
|
||||
// Try to increase the limit to the max.
|
||||
|
||||
@@ -60,9 +60,9 @@ func benchmarkRequestsTLS(b *testing.B, conn0, conn1 net.Conn) {
|
||||
|
||||
func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
|
||||
// Start up Connections on them
|
||||
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata)
|
||||
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil)
|
||||
c0.Start()
|
||||
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata)
|
||||
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutils.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil)
|
||||
c1.Start()
|
||||
|
||||
// Satisfy the assertions in the protocol by sending an initial cluster config
|
||||
@@ -188,7 +188,7 @@ func (m *fakeModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *fakeModel) Closed(conn Connection, err error) {
|
||||
func (m *fakeModel) Closed(DeviceID, error) {
|
||||
}
|
||||
|
||||
func (m *fakeModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error {
|
||||
|
||||
@@ -49,7 +49,7 @@ func (t *TestModel) Request(deviceID DeviceID, folder, name string, blockNo, siz
|
||||
return &fakeRequestResponse{buf}, nil
|
||||
}
|
||||
|
||||
func (t *TestModel) Closed(conn Connection, err error) {
|
||||
func (t *TestModel) Closed(_ DeviceID, err error) {
|
||||
t.closedErr = err
|
||||
close(t.closedCh)
|
||||
}
|
||||
|
||||
@@ -203,15 +203,15 @@ func chunkify(s string) string {
|
||||
}
|
||||
|
||||
func unchunkify(s string) string {
|
||||
s = strings.Replace(s, "-", "", -1)
|
||||
s = strings.Replace(s, " ", "", -1)
|
||||
s = strings.ReplaceAll(s, "-", "")
|
||||
s = strings.ReplaceAll(s, " ", "")
|
||||
return s
|
||||
}
|
||||
|
||||
func untypeoify(s string) string {
|
||||
s = strings.Replace(s, "0", "O", -1)
|
||||
s = strings.Replace(s, "1", "I", -1)
|
||||
s = strings.Replace(s, "8", "B", -1)
|
||||
s = strings.ReplaceAll(s, "0", "O")
|
||||
s = strings.ReplaceAll(s, "1", "I")
|
||||
s = strings.ReplaceAll(s, "8", "B")
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
+42
-13
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
@@ -41,11 +42,11 @@ const (
|
||||
// must decrypt those and answer requests by encrypting the data.
|
||||
type encryptedModel struct {
|
||||
model Model
|
||||
folderKeys map[string]*[keySize]byte // folder ID -> key
|
||||
folderKeys *folderKeyRegistry
|
||||
}
|
||||
|
||||
func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo) error {
|
||||
if folderKey, ok := e.folderKeys[folder]; ok {
|
||||
if folderKey, ok := e.folderKeys.get(folder); ok {
|
||||
// incoming index data to be decrypted
|
||||
if err := decryptFileInfos(files, folderKey); err != nil {
|
||||
return err
|
||||
@@ -55,7 +56,7 @@ func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo
|
||||
}
|
||||
|
||||
func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error {
|
||||
if folderKey, ok := e.folderKeys[folder]; ok {
|
||||
if folderKey, ok := e.folderKeys.get(folder); ok {
|
||||
// incoming index data to be decrypted
|
||||
if err := decryptFileInfos(files, folderKey); err != nil {
|
||||
return err
|
||||
@@ -65,7 +66,7 @@ func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []Fi
|
||||
}
|
||||
|
||||
func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
|
||||
folderKey, ok := e.folderKeys[folder]
|
||||
folderKey, ok := e.folderKeys.get(folder)
|
||||
if !ok {
|
||||
return e.model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo,
|
||||
}
|
||||
|
||||
func (e encryptedModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error {
|
||||
if _, ok := e.folderKeys[folder]; !ok {
|
||||
if _, ok := e.folderKeys.get(folder); !ok {
|
||||
return e.model.DownloadProgress(deviceID, folder, updates)
|
||||
}
|
||||
|
||||
@@ -135,42 +136,46 @@ func (e encryptedModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) e
|
||||
return e.model.ClusterConfig(deviceID, config)
|
||||
}
|
||||
|
||||
func (e encryptedModel) Closed(conn Connection, err error) {
|
||||
e.model.Closed(conn, err)
|
||||
func (e encryptedModel) Closed(device DeviceID, err error) {
|
||||
e.model.Closed(device, err)
|
||||
}
|
||||
|
||||
// The encryptedConnection sits between the model and the encrypted device. It
|
||||
// encrypts outgoing metadata and decrypts incoming responses.
|
||||
type encryptedConnection struct {
|
||||
ConnectionInfo
|
||||
conn Connection
|
||||
folderKeys map[string]*[keySize]byte // folder ID -> key
|
||||
conn *rawConnection
|
||||
folderKeys *folderKeyRegistry
|
||||
}
|
||||
|
||||
func (e encryptedConnection) Start() {
|
||||
e.conn.Start()
|
||||
}
|
||||
|
||||
func (e encryptedConnection) SetFolderPasswords(passwords map[string]string) {
|
||||
e.folderKeys.setPasswords(passwords)
|
||||
}
|
||||
|
||||
func (e encryptedConnection) ID() DeviceID {
|
||||
return e.conn.ID()
|
||||
}
|
||||
|
||||
func (e encryptedConnection) Index(ctx context.Context, folder string, files []FileInfo) error {
|
||||
if folderKey, ok := e.folderKeys[folder]; ok {
|
||||
if folderKey, ok := e.folderKeys.get(folder); ok {
|
||||
encryptFileInfos(files, folderKey)
|
||||
}
|
||||
return e.conn.Index(ctx, folder, files)
|
||||
}
|
||||
|
||||
func (e encryptedConnection) IndexUpdate(ctx context.Context, folder string, files []FileInfo) error {
|
||||
if folderKey, ok := e.folderKeys[folder]; ok {
|
||||
if folderKey, ok := e.folderKeys.get(folder); ok {
|
||||
encryptFileInfos(files, folderKey)
|
||||
}
|
||||
return e.conn.IndexUpdate(ctx, folder, files)
|
||||
}
|
||||
|
||||
func (e encryptedConnection) Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
|
||||
folderKey, ok := e.folderKeys[folder]
|
||||
folderKey, ok := e.folderKeys.get(folder)
|
||||
if !ok {
|
||||
return e.conn.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
|
||||
}
|
||||
@@ -205,7 +210,7 @@ func (e encryptedConnection) Request(ctx context.Context, folder string, name st
|
||||
}
|
||||
|
||||
func (e encryptedConnection) DownloadProgress(ctx context.Context, folder string, updates []FileDownloadProgressUpdate) {
|
||||
if _, ok := e.folderKeys[folder]; !ok {
|
||||
if _, ok := e.folderKeys.get(folder); !ok {
|
||||
e.conn.DownloadProgress(ctx, folder, updates)
|
||||
}
|
||||
|
||||
@@ -590,3 +595,27 @@ func isEncryptedParentFromComponents(pathComponents []string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type folderKeyRegistry struct {
|
||||
keys map[string]*[keySize]byte // folder ID -> key
|
||||
mut sync.RWMutex
|
||||
}
|
||||
|
||||
func newFolderKeyRegistry(passwords map[string]string) *folderKeyRegistry {
|
||||
return &folderKeyRegistry{
|
||||
keys: keysFromPasswords(passwords),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *folderKeyRegistry) get(folder string) (*[keySize]byte, bool) {
|
||||
r.mut.RLock()
|
||||
key, ok := r.keys[folder]
|
||||
r.mut.RUnlock()
|
||||
return key, ok
|
||||
}
|
||||
|
||||
func (r *folderKeyRegistry) setPasswords(passwords map[string]string) {
|
||||
r.mut.Lock()
|
||||
r.keys = keysFromPasswords(passwords)
|
||||
r.mut.Unlock()
|
||||
}
|
||||
|
||||
@@ -135,6 +135,11 @@ type Connection struct {
|
||||
result1 []byte
|
||||
result2 error
|
||||
}
|
||||
SetFolderPasswordsStub func(map[string]string)
|
||||
setFolderPasswordsMutex sync.RWMutex
|
||||
setFolderPasswordsArgsForCall []struct {
|
||||
arg1 map[string]string
|
||||
}
|
||||
StartStub func()
|
||||
startMutex sync.RWMutex
|
||||
startArgsForCall []struct {
|
||||
@@ -817,6 +822,38 @@ func (fake *Connection) RequestReturnsOnCall(i int, result1 []byte, result2 erro
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswords(arg1 map[string]string) {
|
||||
fake.setFolderPasswordsMutex.Lock()
|
||||
fake.setFolderPasswordsArgsForCall = append(fake.setFolderPasswordsArgsForCall, struct {
|
||||
arg1 map[string]string
|
||||
}{arg1})
|
||||
stub := fake.SetFolderPasswordsStub
|
||||
fake.recordInvocation("SetFolderPasswords", []interface{}{arg1})
|
||||
fake.setFolderPasswordsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.SetFolderPasswordsStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsCallCount() int {
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
return len(fake.setFolderPasswordsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsCalls(stub func(map[string]string)) {
|
||||
fake.setFolderPasswordsMutex.Lock()
|
||||
defer fake.setFolderPasswordsMutex.Unlock()
|
||||
fake.SetFolderPasswordsStub = stub
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsArgsForCall(i int) map[string]string {
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
argsForCall := fake.setFolderPasswordsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Connection) Start() {
|
||||
fake.startMutex.Lock()
|
||||
fake.startArgsForCall = append(fake.startArgsForCall, struct {
|
||||
@@ -1080,6 +1117,8 @@ func (fake *Connection) Invocations() map[string][][]interface{} {
|
||||
defer fake.remoteAddrMutex.RUnlock()
|
||||
fake.requestMutex.RLock()
|
||||
defer fake.requestMutex.RUnlock()
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
fake.startMutex.RLock()
|
||||
defer fake.startMutex.RUnlock()
|
||||
fake.statisticsMutex.RLock()
|
||||
|
||||
@@ -126,8 +126,8 @@ type Model interface {
|
||||
Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error)
|
||||
// A cluster configuration message was received
|
||||
ClusterConfig(deviceID DeviceID, config ClusterConfig) error
|
||||
// The peer device closed the connection
|
||||
Closed(conn Connection, err error)
|
||||
// The peer device closed the connection or an error occurred
|
||||
Closed(device DeviceID, err error)
|
||||
// The peer device sent progress updates for the files it is currently downloading
|
||||
DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error
|
||||
}
|
||||
@@ -140,6 +140,7 @@ type RequestResponse interface {
|
||||
|
||||
type Connection interface {
|
||||
Start()
|
||||
SetFolderPasswords(passwords map[string]string)
|
||||
Close(err error)
|
||||
ID() DeviceID
|
||||
Index(ctx context.Context, folder string, files []FileInfo) error
|
||||
@@ -225,24 +226,16 @@ const (
|
||||
// Should not be modified in production code, just for testing.
|
||||
var CloseTimeout = 10 * time.Second
|
||||
|
||||
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression) Connection {
|
||||
receiver = nativeModel{receiver}
|
||||
rc := newRawConnection(deviceID, reader, writer, closer, receiver, connInfo, compress)
|
||||
return wireFormatConnection{rc}
|
||||
}
|
||||
|
||||
func NewEncryptedConnection(passwords map[string]string, deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression) Connection {
|
||||
keys := keysFromPasswords(passwords)
|
||||
|
||||
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string) Connection {
|
||||
// Encryption / decryption is first (outermost) before conversion to
|
||||
// native path formats.
|
||||
nm := nativeModel{receiver}
|
||||
em := encryptedModel{model: nm, folderKeys: keys}
|
||||
em := &encryptedModel{model: nm, folderKeys: newFolderKeyRegistry(passwords)}
|
||||
|
||||
// We do the wire format conversion first (outermost) so that the
|
||||
// metadata is in wire format when it reaches the encryption step.
|
||||
rc := newRawConnection(deviceID, reader, writer, closer, em, connInfo, compress)
|
||||
ec := encryptedConnection{ConnectionInfo: rc, conn: rc, folderKeys: keys}
|
||||
ec := encryptedConnection{ConnectionInfo: rc, conn: rc, folderKeys: em.folderKeys}
|
||||
wc := wireFormatConnection{ec}
|
||||
|
||||
return wc
|
||||
@@ -748,6 +741,8 @@ func (c *rawConnection) writerLoop() {
|
||||
}
|
||||
|
||||
func (c *rawConnection) writeMessage(msg message) error {
|
||||
msgContext, _ := messageContext(msg)
|
||||
l.Debugf("Writing %v", msgContext)
|
||||
if c.shouldCompressMessage(msg) {
|
||||
return c.writeCompressedMessage(msg)
|
||||
}
|
||||
@@ -955,7 +950,7 @@ func (c *rawConnection) internalClose(err error) {
|
||||
|
||||
<-c.dispatcherLoopStopped
|
||||
|
||||
c.receiver.Closed(c, err)
|
||||
c.receiver.Closed(c.ID(), err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ func TestPing(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c1 := getRawConnection(NewConnection(c1ID, br, aw, testutils.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(ClusterConfig{})
|
||||
@@ -57,10 +57,10 @@ func TestClose(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways)
|
||||
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways, nil)
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(ClusterConfig{})
|
||||
@@ -102,7 +102,7 @@ func TestCloseOnBlockingSend(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutils.NewBlockingRW()
|
||||
c := NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -153,10 +153,10 @@ func TestCloseRace(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutils.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever, nil))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever)
|
||||
c1 := NewConnection(c1ID, br, aw, testutils.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever, nil)
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(ClusterConfig{})
|
||||
@@ -193,7 +193,7 @@ func TestClusterConfigFirst(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutils.NewBlockingRW()
|
||||
c := NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -245,7 +245,7 @@ func TestCloseTimeout(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutils.NewBlockingRW()
|
||||
c := NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -865,7 +865,7 @@ func TestClusterConfigAfterClose(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutils.NewBlockingRW()
|
||||
c := NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -889,7 +889,7 @@ func TestDispatcherToCloseDeadlock(t *testing.T) {
|
||||
// the model callbacks (ClusterConfig).
|
||||
m := newTestModel()
|
||||
rw := testutils.NewBlockingRW()
|
||||
c := NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil))
|
||||
m.ccFn = func(devID DeviceID, cc ClusterConfig) {
|
||||
c.Close(errManual)
|
||||
}
|
||||
@@ -962,17 +962,28 @@ func TestIndexIDString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func closeAndWait(c Connection, closers ...io.Closer) {
|
||||
func closeAndWait(c interface{}, closers ...io.Closer) {
|
||||
for _, closer := range closers {
|
||||
closer.Close()
|
||||
}
|
||||
var raw *rawConnection
|
||||
switch i := c.(type) {
|
||||
case wireFormatConnection:
|
||||
raw = i.Connection.(*rawConnection)
|
||||
case *rawConnection:
|
||||
raw = i
|
||||
default:
|
||||
raw = getRawConnection(c.(Connection))
|
||||
}
|
||||
raw.internalClose(ErrClosed)
|
||||
raw.loopWG.Wait()
|
||||
}
|
||||
|
||||
func getRawConnection(c Connection) *rawConnection {
|
||||
var raw *rawConnection
|
||||
switch i := c.(type) {
|
||||
case wireFormatConnection:
|
||||
raw = i.Connection.(encryptedConnection).conn
|
||||
case encryptedConnection:
|
||||
raw = i.conn
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
@@ -86,6 +87,9 @@ func ReadMessage(r io.Reader) (interface{}, error) {
|
||||
if header.magic != magic {
|
||||
return nil, errors.New("magic mismatch")
|
||||
}
|
||||
if header.messageLength < 0 || header.messageLength > 1024 {
|
||||
return nil, fmt.Errorf("bad length (%d)", header.messageLength)
|
||||
}
|
||||
|
||||
buf = make([]byte, int(header.messageLength))
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
|
||||
@@ -195,17 +195,17 @@ func BenchmarkValidate(b *testing.B) {
|
||||
for i := 0; i < blocksPerType; i++ {
|
||||
var b block
|
||||
b.data = make([]byte, 128<<10)
|
||||
r.Read(b.data[:])
|
||||
b.hash = sha256.Sum256(b.data[:])
|
||||
b.weakhash = origAdler32.Checksum(b.data[:])
|
||||
r.Read(b.data)
|
||||
b.hash = sha256.Sum256(b.data)
|
||||
b.weakhash = origAdler32.Checksum(b.data)
|
||||
blocks = append(blocks, b)
|
||||
}
|
||||
// Blocks where the hash matches, but the weakhash doesn't.
|
||||
for i := 0; i < blocksPerType; i++ {
|
||||
var b block
|
||||
b.data = make([]byte, 128<<10)
|
||||
r.Read(b.data[:])
|
||||
b.hash = sha256.Sum256(b.data[:])
|
||||
r.Read(b.data)
|
||||
b.hash = sha256.Sum256(b.data)
|
||||
b.weakhash = 1 // Zeros causes Validate to skip the weakhash.
|
||||
blocks = append(blocks, b)
|
||||
}
|
||||
@@ -215,7 +215,7 @@ func BenchmarkValidate(b *testing.B) {
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, b := range blocks {
|
||||
Validate(b.data[:], b.hash[:], b.weakhash)
|
||||
Validate(b.data, b.hash[:], b.weakhash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ USER-AGENT: syncthing/1.0
|
||||
`
|
||||
searchStr := fmt.Sprintf(tpl, deviceType, timeout/time.Second)
|
||||
|
||||
search := []byte(strings.Replace(searchStr, "\n", "\r\n", -1) + "\r\n")
|
||||
search := []byte(strings.ReplaceAll(searchStr, "\n", "\r\n") + "\r\n")
|
||||
|
||||
l.Debugln("Starting discovery of device type", deviceType, "on", intf.Name)
|
||||
|
||||
|
||||
@@ -426,7 +426,12 @@ func CpuBench(ctx context.Context, iterations int, duration time.Duration, useWe
|
||||
perf = v
|
||||
}
|
||||
}
|
||||
blocksResult = nil
|
||||
// not looking at the blocksResult makes it unused from a static
|
||||
// analysis / compiler standpoint...
|
||||
// blocksResult may be nil at this point if the context is cancelled
|
||||
if blocksResult != nil {
|
||||
blocksResult = nil
|
||||
}
|
||||
return perf
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ func newExternal(cfg config.FolderConfiguration) Versioner {
|
||||
command := cfg.Versioning.Params["command"]
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
command = strings.Replace(command, `\`, `\\`, -1)
|
||||
command = strings.ReplaceAll(command, `\`, `\\`)
|
||||
}
|
||||
|
||||
s := external{
|
||||
@@ -81,7 +81,7 @@ func (v external) Archive(filePath string) error {
|
||||
|
||||
for i, word := range words {
|
||||
for key, val := range context {
|
||||
word = strings.Replace(word, key, val, -1)
|
||||
word = strings.ReplaceAll(word, key, val)
|
||||
}
|
||||
|
||||
words[i] = word
|
||||
|
||||
+6
-10
@@ -9,7 +9,6 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
cr "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -27,6 +26,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/rc"
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -395,7 +395,7 @@ type fileInfo struct {
|
||||
name string
|
||||
mode os.FileMode
|
||||
mod int64
|
||||
hash [16]byte
|
||||
hash [sha256.Size]byte
|
||||
size int64
|
||||
}
|
||||
|
||||
@@ -442,11 +442,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := md5.New()
|
||||
h.Write([]byte(tgt))
|
||||
hash := h.Sum(nil)
|
||||
|
||||
copy(f.hash[:], hash)
|
||||
f.hash = sha256.Sum256([]byte(tgt))
|
||||
} else if info.IsDir() {
|
||||
f = fileInfo{
|
||||
name: rn,
|
||||
@@ -463,7 +459,7 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
mod: info.ModTime().Unix(),
|
||||
size: info.Size(),
|
||||
}
|
||||
sum, err := md5file(path)
|
||||
sum, err := sha256file(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -490,14 +486,14 @@ func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan er
|
||||
return errc
|
||||
}
|
||||
|
||||
func md5file(fname string) (hash [16]byte, err error) {
|
||||
func sha256file(fname string) (hash [sha256.Size]byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
h := sha256.New()
|
||||
io.Copy(h, f)
|
||||
hb := h.Sum(nil)
|
||||
copy(hash[:], hb)
|
||||
|
||||
Reference in New Issue
Block a user