Compare commits

...

57 Commits

Author SHA1 Message Date
Simon Frei 22e09334ec lib/model: Fix incoming request on receive-enc (fixes #7699) (#7702) 2021-05-22 21:38:49 +02:00
Simon Frei 58592e3ef1 lib/db: Add logging for GC (#7707) 2021-05-22 21:36:43 +02:00
Simon Frei 0126188ba7 lib/config: Set DisableTempIndexes to true on receive-encrypted (#7701) 2021-05-20 22:33:23 +02:00
Simon Frei 5bdb6798a9 all: Regenerate proto (#7696) 2021-05-19 13:30:20 +02:00
Jakob Borg ab2729ab79 gui, man, authors: Update docs, translations, and contributors 2021-05-19 07:45:35 +02:00
Audrius Butkevicius 58e81fdffb cmd/syncthing/cli: Update recli, fix stdin handling (fixes #7685, fixes #7673) (#7694) 2021-05-18 20:09:48 +01:00
tomasz1986 0619a27872 cmd/strelaypoolsrv: Fix minor grammar, use https in links (#7695)
* cmd/strelaypoolsrv: Fix minor grammar, use https in links

Add a few minor grammatical/stylistic fixes. Use `https` instead of
`http` in the MaxMind link in the footer.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>

* wip

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2021-05-18 19:07:09 +01:00
greatroar 0e52ce830a lib/fs: Fix UnicodeLowercaseNormalized on lowercase NFD (#7692)
Co-authored-by: greatroar <@>
2021-05-17 20:43:07 +02:00
Jakob Borg 97437cad64 lib/fs: Ignore normalization differences in case insensitive lookup (fixes #7677) (#7678) 2021-05-17 12:35:03 +02:00
Simon Frei 5b90a98650 lib/model: Fix addFakeConn and other test improvements (#7684) 2021-05-16 17:23:27 +02:00
Audrius Butkevicius 96dae7bfec cmd/uraggregate: Optimise queries (#7679)
* cmd/uraggregate: Optimise queries

* Update main.go
2021-05-16 12:34:46 +01:00
Simon Frei 93a02c677e lib/scanner: Do not drop all not-exist-errors and debug logging (#7675) 2021-05-15 11:51:35 +02:00
Simon Frei 0d054f9b64 lib/model: Don't use empty folder cfg for index sender (fixes #7649) (#7671) 2021-05-15 11:13:39 +02:00
Audrius Butkevicius 1107f6eb5f lib/connections: Reduce default quic redial interval (fixes #7471) (#7672)
* lib/connections: Reduce default quic redial interval (fixes #7471)

* Update quic_dial.go
2021-05-14 14:26:02 +01:00
Simon Frei 3650364017 Merge branch 'release' 2021-05-13 11:44:59 +02:00
bt90 086508f51a docker: Remove sysctl from README (#7670) 2021-05-12 22:17:51 +02:00
Audrius Butkevicius 4ace451013 Update main.go (#7667) 2021-05-12 08:01:18 +01:00
Jakob Borg c9ea773a22 gui, man, authors: Update docs, translations, and contributors 2021-05-12 07:45:34 +02:00
Simon Frei 0f4ae7636d build: Upgrade pfilter (fixes #7664) (#7666) 2021-05-11 20:57:38 +02:00
Simon Frei 87d3a8363b build: Upgrade pfilter (fixes #7664) 2021-05-11 20:45:35 +02:00
Simon Frei c494ced21f lib/connections: Actually remove listenerSupervisor (ref #7644) (#7663) 2021-05-11 14:35:13 +02:00
Audrius Butkevicius a8e2fc6f61 cmd/uraggregate: Handle malformed data, dont abort on error (fixes #7639) (#7659) 2021-05-11 08:02:19 +02:00
Audrius Butkevicius aca1b45e93 lib/connections: Update pfilter to pick up bugfix/oob stuff, support OOB connections (fixes #7636) (#7654) 2021-05-11 07:59:56 +02:00
Simon Frei 5cb2a10138 lib/model: Improve encryption cluster-config errors (#7658) 2021-05-11 07:55:44 +02:00
Audrius Butkevicius 411796606c lib/connections: Correct service termination order (#7657) 2021-05-10 22:29:27 +02:00
Simon Frei 1a9b54c9fa lib/connections: Separate listener supervisors and lower backoff time (#7644) 2021-05-10 22:26:51 +02:00
Simon Frei c7f4f15272 lib/relay, lib/svcutil: Improve service logging (fixes #7580) (#7647) 2021-05-10 22:26:25 +02:00
Simon Frei 713527facf all: Refactor relay invitations (#7646) 2021-05-10 22:25:43 +02:00
Simon Frei 6e662dc9fc lib/suture: Use ServeBackground to start main supervisor (#7626) 2021-05-10 16:50:45 +02:00
Audrius Butkevicius eb178caf3a lib/connections: Add connection benchmarks, allow binding to port zero addresses (#7648)
* Add connbench

* Refactor port fixup

* More cleanup

* touch for build

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2021-05-10 15:44:47 +01:00
Audrius Butkevicius adf3f641ce Ignore GoLand cruft 2021-05-09 23:56:18 +01:00
Simon Frei 6157c766de lib/connections: Correct comments on quic wrapper type (#7652) 2021-05-09 19:15:10 +01:00
Audrius Butkevicius 745cd4744a lib/connections: Revert add more methods to the quic conn wrapper (#7651)
This reverts commit faf15b4567.
2021-05-09 19:43:16 +02:00
Simon Frei faf15b4567 lib/connections: Add more methods to the quic conn wrapper (#7643) 2021-05-09 12:45:08 +01:00
greatroar 3746c899b7 build: List go:generate tools in tools.go (#7599) 2021-05-08 12:52:06 +02:00
Jakob Borg 7bbca12ff8 Merge branch 'release'
* release:
  build: Ignore error from pkill on apt upgrade (fixes #7628) (#7629)
2021-05-06 13:23:02 +02:00
Simon Frei 3967b39a17 build: Ignore error from pkill on apt upgrade (fixes #7628) (#7629) 2021-05-05 09:37:23 +02:00
Jakob Borg 2b2d24fe20 gui, man, authors: Update docs, translations, and contributors 2021-05-05 07:45:35 +02:00
Simon Frei f4e112f404 build: Ignore error from pkill on apt upgrade (fixes #7628) (#7629) 2021-05-04 18:33:25 +02:00
Audrius Butkevicius 87a0eecc31 lib/fs, lib/api, lib/model: Expose mtime remappings as part of /db/file (#7624)
* lib/fs, lib/api, lib/model: Expose mtime remappings as part of /db/file

* Fix wrong error returned by CLI

* Gofmt

* Better names

* Review comments

* Review comments
2021-05-03 11:28:25 +01:00
overkill f09dcb98eb gui: Semicolons (#7597) 2021-05-03 12:14:54 +02:00
dependabot[bot] f90870b99f build: Bump github.com/shirou/gopsutil/v3 from 3.21.3 to 3.21.4 (#7625)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.21.3 to 3.21.4.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.21.3...v3.21.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-03 12:10:04 +02:00
Simon Frei 75b58eb480 lib/fs: Watch attrib. changes on inotify for mod. time (#7623)
* lib/fs: Watch attrib. changes on inotify for mod. time

* fix bsds (real) and darwin (test only)
2021-05-02 16:45:44 +02:00
Jakob Borg ed9cb923fb build: Update most dependencies (fixes: all the dependabot PRs) (#7622) 2021-04-30 09:11:11 +02:00
Simon Frei dd39556759 lib: Revert needing invalid files (fixes #7608, ref #7476) (#7609) 2021-04-29 22:01:46 +02:00
dependabot[bot] d5141c6d51 build: Bump github.com/prometheus/client_golang from 1.8.0 to 1.10.0 (#7612)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.8.0 to 1.10.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.8.0...v1.10.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-29 21:57:22 +02:00
dependabot-preview[bot] 5675644341 build: Upgrade to GitHub-native Dependabot (#7607) 2021-04-29 21:19:06 +02:00
Simon Frei 1f30383866 lib/model: Remove path from enc errors and report only once (#7610) 2021-04-29 19:21:07 +02:00
tomasz1986 40531ef247 gui: Disable versions button when folder is paused (#7611)
Disable the Versions button when the folder is paused, because it does
not work, i.e. the versioned files are not loaded. The folder needs to
be unpaused to actually be able to view the versioned file list.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2021-04-29 18:34:08 +02:00
Wulf Weich 366700dc36 gui: Cut off long remote device names (fixes #7592) (#7606) 2021-04-28 18:11:05 +02:00
Jakob Borg f23fd683a9 gui, man, authors: Update docs, translations, and contributors 2021-04-28 07:45:43 +02:00
Jakob Borg 755ed6f69f Merge branch 'release'
* release:
  lib/model: Handle invalid needed items on send-only (ref #7476) (#7596)
  gui: Handle empty path in validation (ref #7379) (#7595)
  cmd/syncthing: Don't fail early on api setup error (fixes 7558) (#7591)
2021-04-27 07:59:51 +02:00
Gahl Saraf 66662cd678 Trigger connection loop on config device addition (fixes #7600) (#7604)
* Trigger connection loop on config device addition (fixes #7600)

* Also check for device address equality

* Move EqualStrings from api_test to utils, and use in connections/service.go

* Make sure CommitConfiguration cannot block due on the deviceAddressesChanged channel

* Update lib/connections/service.go

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2021-04-26 21:13:59 +01:00
Simon Frei 8734fa65fc lib/model: Handle invalid needed items on send-only (ref #7476) (#7596) 2021-04-26 15:36:51 +02:00
Simon Frei c53e5c5f17 gui: Handle empty path in validation (ref #7379) (#7595) 2021-04-26 15:35:12 +02:00
Jakob Borg 74823e81e9 all: Deprecate TLS 1.2 on sync connections (fixes #7594) (#7598)
This makes us use TLS 1.3+ on sync connections by default. A new option
`insecureAllowOldTLSVersions` exists to allow communication with TLS
1.2-only clients (roughly Syncthing 1.2.2 and older). Even with that
option set you get a slightly simplified setup, with the cipher suite
order fixed instead of auto detected.
2021-04-26 10:04:35 +02:00
Simon Frei ef4b8a2cf8 cmd/syncthing: Don't fail early on api setup error (fixes 7558) (#7591)
* cmd/syncthing: Don't fail early on api setup error (fixes 7558)

* switch to factory pattern

* refactor config command to show help on nothing

* wip

* wip

* already abort in before
2021-04-25 20:48:17 +01:00
124 changed files with 2173 additions and 1355 deletions
+7
View File
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
+1
View File
@@ -19,3 +19,4 @@ deb
/repos
/proto/scripts/protoc-gen-gosyncthing
/gui/next-gen-gui
.idea
+3 -1
View File
@@ -82,7 +82,7 @@ deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.c
Denis A. (dva) <denisva@gmail.com>
Dennis Wilson (snnd) <dw@risu.io>
dependabot-preview[bot] <dependabot-preview[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com>
dependabot[bot] <dependabot[bot]@users.noreply.github.com>
dependabot[bot] <dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
derekriemer <derek.riemer@colorado.edu>
desbma <desbma@users.noreply.github.com>
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
@@ -100,6 +100,7 @@ Felix Lampe <mail@flampe.de>
Felix Unterpaintner (bigbear2nd) <bigbear2nd@gmail.com>
Francois-Xavier Gsell (zukoo) <fxgsell@gmail.com>
Frank Isemann (fti7) <frank@isemann.name>
Gahl Saraf <saraf.gahl@gmail.com>
georgespatton <georgespatton@users.noreply.github.com>
ghjklw <malo@jaffre.info>
Gilli Sigurdsson (gillisig) <gilli@vx.is>
@@ -203,6 +204,7 @@ NoLooseEnds <jon.koslung@gmail.com>
Oliver Freyermuth <o.freyermuth@googlemail.com>
otbutz <tbutz@optitool.de>
Otiel <Otiel@users.noreply.github.com>
overkill <22098433+0verk1ll@users.noreply.github.com>
Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
Pablo <pbaeyens31+github@gmail.com>
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
+1 -4
View File
@@ -16,8 +16,7 @@ the name of the Syncthing instance can be optionally defined by using
**Docker cli**
```
$ docker pull syncthing/syncthing
$ docker run --sysctl net.core.rmem_max=2097152 \
-p 8384:8384 -p 22000:22000/tcp -p 22000:22000/udp \
$ docker run -p 8384:8384 -p 22000:22000/tcp -p 22000:22000/udp \
-v /wherever/st-sync:/var/syncthing \
--hostname=my-syncthing \
syncthing/syncthing:latest
@@ -41,8 +40,6 @@ services:
- 8384:8384
- 22000:22000/tcp
- 22000:22000/udp
sysctls:
- net.core.rmem_max=2097152
restart: unless-stopped
```
+9 -25
View File
@@ -202,18 +202,6 @@ var targets = map[string]target{
},
}
// These are repos we need to clone to run "go generate"
type dependencyRepo struct {
path string
repo string
commit string
}
var dependencyRepos = []dependencyRepo{
{path: "xdr", repo: "https://github.com/calmh/xdr.git", commit: "08e072f9cb16"},
}
func initTargets() {
all := targets["all"]
pkgs, _ := filepath.Glob("cmd/*")
@@ -871,28 +859,24 @@ func shouldRebuildAssets(target, srcdir string) bool {
func proto() {
pv := protobufVersion()
dependencyRepos = append(dependencyRepos,
dependencyRepo{path: "protobuf", repo: "https://github.com/gogo/protobuf.git", commit: pv},
)
repo := "https://github.com/gogo/protobuf.git"
path := filepath.Join("repos", "protobuf")
runPrint(goCmd, "get", fmt.Sprintf("github.com/gogo/protobuf/protoc-gen-gogofast@%v", pv))
os.MkdirAll("repos", 0755)
for _, dep := range dependencyRepos {
path := filepath.Join("repos", dep.path)
if _, err := os.Stat(path); err != nil {
runPrintInDir("repos", "git", "clone", dep.repo, dep.path)
} else {
runPrintInDir(path, "git", "fetch")
}
runPrintInDir(path, "git", "checkout", dep.commit)
if _, err := os.Stat(path); err != nil {
runPrint("git", "clone", repo, path)
} else {
runPrintInDir(path, "git", "fetch")
}
runPrintInDir(path, "git", "checkout", pv)
runPrint(goCmd, "generate", "github.com/syncthing/syncthing/cmd/stdiscosrv")
runPrint(goCmd, "generate", "proto/generate.go")
}
func testmocks() {
runPrint(goCmd, "get", "golang.org/x/tools/cmd/goimports")
runPrint(goCmd, "get", "github.com/maxbrunsfeld/counterfeiter/v6")
args := []string{
"generate",
"github.com/syncthing/syncthing/lib/config",
+3 -12
View File
@@ -510,10 +510,7 @@ func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
@@ -648,10 +645,7 @@ func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
@@ -752,10 +746,7 @@ func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDatabase
}
if (iNdEx + skippy) > l {
+4 -4
View File
@@ -46,18 +46,18 @@
<h1>Relay Pool Data</h1>
<div ng-if="relays === undefined" class="text-center">
<img src="https://cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif" alt=""/>
<p>Please wait while we gather data</p>
<p>Please wait while we gather data</p>
</div>
<div>
<div ng-show="relays !== undefined" class="ng-hide">
<p>
The relays listed on this page are not managed or vetted by the Syncthing project.
Each relay is the responsibility of the relay operator.
Currently {{ relays.length }} relays online.
Currently {{ relays.length }} relays are online.
</p>
</div>
<div id="map"></div> <!-- Can't hide the map, otherwise it freaks out -->
<p>The circle size represents how much bytes the relay transferred relative to other relays</p>
<p>The circle size represents how much bytes the relay has transferred relatively to other relays.</p>
</div>
<div>
<table class="table table-striped table-condensed table">
@@ -188,7 +188,7 @@
<hr>
<p>
This product includes GeoLite2 data created by MaxMind, available from
<a href="http://www.maxmind.com">http://www.maxmind.com</a>.
<a href="https://www.maxmind.com">https://www.maxmind.com</a>.
</p>
</div>
+1 -1
View File
@@ -57,7 +57,7 @@ func main() {
if join {
log.Println("Creating client")
relay, err := client.NewClient(uri, []tls.Certificate{cert}, nil, 10*time.Second)
relay, err := client.NewClient(uri, []tls.Certificate{cert}, 10*time.Second)
if err != nil {
log.Fatal(err)
}
+3 -3
View File
@@ -135,11 +135,11 @@ func (c *apiClient) Post(url, body string) (*http.Response, error) {
}
func checkResponse(response *http.Response) error {
if response.StatusCode == 404 {
if response.StatusCode == http.StatusNotFound {
return errors.New("invalid endpoint or API call")
} else if response.StatusCode == 403 {
} else if response.StatusCode == http.StatusUnauthorized {
return errors.New("invalid API key")
} else if response.StatusCode != 200 {
} else if response.StatusCode != http.StatusOK {
data, err := responseToBArray(response)
if err != nil {
return err
+33 -30
View File
@@ -8,13 +8,13 @@ package cli
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/alecthomas/kong"
"github.com/flynn-archive/go-shlex"
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -97,38 +97,41 @@ func Run() error {
operationCommand,
errorsCommand,
debugCommand,
{
Name: "-",
HideHelp: true,
Usage: "Read commands from stdin",
Action: func(ctx *cli.Context) error {
if ctx.NArg() > 0 {
return errors.New("command does not expect any arguments")
}
// Drop the `-` not to recurse into self.
args := make([]string, len(os.Args)-1)
copy(args, os.Args)
fmt.Println("Reading commands from stdin...", args)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input, err := shlex.Split(scanner.Text())
if err != nil {
return errors.Wrap(err, "parsing input")
}
if len(input) == 0 {
continue
}
err = app.Run(append(args, input...))
if err != nil {
return err
}
}
return scanner.Err()
},
},
},
}}
tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
if !tty {
// Not a TTY, consume from stdin
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input, err := shlex.Split(scanner.Text())
if err != nil {
return errors.Wrap(err, "parsing input")
}
if len(input) == 0 {
continue
}
err = app.Run(append(os.Args, input...))
if err != nil {
return err
}
}
err = scanner.Err()
if err != nil {
return err
}
} else {
err = app.Run(os.Args)
if err != nil {
return err
}
}
return nil
return app.Run(os.Args)
}
func parseFlags(c *preCli) error {
+20 -17
View File
@@ -47,32 +47,32 @@ func main() {
func runAggregation(db *sql.DB) {
since := maxIndexedDay(db, "VersionSummary")
log.Println("Aggregating VersionSummary data since", since)
rows, err := aggregateVersionSummary(db, since)
rows, err := aggregateVersionSummary(db, since.Add(24*time.Hour))
if err != nil {
log.Fatalln("aggregate:", err)
log.Println("aggregate:", err)
}
log.Println("Inserted", rows, "rows")
log.Println("Aggregating UserMovement data")
rows, err = aggregateUserMovement(db)
if err != nil {
log.Fatalln("aggregate:", err)
log.Println("aggregate:", err)
}
log.Println("Inserted", rows, "rows")
log.Println("Aggregating Performance data")
since = maxIndexedDay(db, "Performance")
rows, err = aggregatePerformance(db, since)
log.Println("Aggregating Performance data since", since)
rows, err = aggregatePerformance(db, since.Add(24*time.Hour))
if err != nil {
log.Fatalln("aggregate:", err)
log.Println("aggregate:", err)
}
log.Println("Inserted", rows, "rows")
log.Println("Aggregating BlockStats data")
since = maxIndexedDay(db, "BlockStats")
rows, err = aggregateBlockStats(db, since)
log.Println("Aggregating BlockStats data since", since)
rows, err = aggregateBlockStats(db, since.Add(24*time.Hour))
if err != nil {
log.Fatalln("aggregate:", err)
log.Println("aggregate:", err)
}
log.Println("Inserted", rows, "rows")
}
@@ -163,7 +163,7 @@ func setupDB(db *sql.DB) error {
func maxIndexedDay(db *sql.DB, table string) time.Time {
var t time.Time
row := db.QueryRow("SELECT MAX(Day) FROM " + table)
row := db.QueryRow("SELECT MAX(DATE_TRUNC('day', Day)) FROM " + table)
err := row.Scan(&t)
if err != nil {
return time.Time{}
@@ -179,8 +179,8 @@ func aggregateVersionSummary(db *sql.DB, since time.Time) (int64, error) {
COUNT(*) AS Count
FROM ReportsJson
WHERE
DATE_TRUNC('day', Received) > $1
AND DATE_TRUNC('day', Received) < DATE_TRUNC('day', NOW())
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND Report->>'version' like 'v_.%'
GROUP BY Day, Ver
);
@@ -198,7 +198,8 @@ func aggregateUserMovement(db *sql.DB) (int64, error) {
Report->>'uniqueID'
FROM ReportsJson
WHERE
DATE_TRUNC('day', Received) < DATE_TRUNC('day', NOW())
Report->>'uniqueID' IS NOT NULL
AND Received < DATE_TRUNC('day', NOW())
AND Report->>'version' like 'v_.%'
ORDER BY Day
`)
@@ -283,9 +284,11 @@ func aggregatePerformance(db *sql.DB, since time.Time) (int64, error) {
AVG((Report->>'memoryUsageMiB')::numeric) As MemoryUsageMiB
FROM ReportsJson
WHERE
DATE_TRUNC('day', Received) > $1
AND DATE_TRUNC('day', Received) < DATE_TRUNC('day', NOW())
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND Report->>'version' like 'v_.%'
/* Some custom implementation reported bytes when we expect megabytes, cap at petabyte */
AND (Report->>'memorySize')::numeric < 1073741824
GROUP BY Day
);
`, since)
@@ -312,8 +315,8 @@ func aggregateBlockStats(db *sql.DB, since time.Time) (int64, error) {
SUM((Report->'blockStats'->>'copyElsewhere')::numeric) AS CopyElsewhere
FROM ReportsJson
WHERE
DATE_TRUNC('day', Received) > $1
AND DATE_TRUNC('day', Received) < DATE_TRUNC('day', NOW())
Received > $1
AND Received < DATE_TRUNC('day', NOW())
AND (Report->>'urVersion')::numeric >= 3
AND Report->>'version' like 'v_.%'
AND Report->>'version' NOT LIKE 'v0.14.40%'
+32 -31
View File
@@ -1,57 +1,58 @@
module github.com/syncthing/syncthing
require (
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a
github.com/AudriusButkevicius/recli v0.0.5
github.com/alecthomas/kong v0.2.12
github.com/AudriusButkevicius/pfilter v0.0.0-20210511165305-e9aaf99ab213
github.com/AudriusButkevicius/recli v0.0.6
github.com/alecthomas/kong v0.2.16
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
github.com/calmh/xdr v1.1.0
github.com/ccding/go-stun v0.1.3
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 // indirect
github.com/certifi/gocertifi v0.0.0-20210429200110-83314bf6d27c // indirect
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/d4l3k/messagediff v1.2.1
github.com/dchest/siphash v1.2.2
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
github.com/getsentry/raven-go v0.2.0
github.com/go-ldap/ldap/v3 v3.2.4
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/go-ldap/ldap/v3 v3.3.0
github.com/gobwas/glob v0.2.3
github.com/gogo/protobuf v1.3.1
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
github.com/golang/protobuf v1.4.3
github.com/greatroar/blobloom v0.5.0
github.com/hashicorp/golang-lru v0.5.1
github.com/jackpal/gateway v1.0.6
github.com/gogo/protobuf v1.3.2
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/protobuf v1.5.2 // indirect
github.com/greatroar/blobloom v0.7.0
github.com/hashicorp/golang-lru v0.5.4
github.com/jackpal/gateway v1.0.7
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/lib/pq v1.8.0
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/lib/pq v1.10.1
github.com/lucas-clemente/quic-go v0.19.3
github.com/maruel/panicparse v1.5.1
github.com/mattn/go-isatty v0.0.12
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 // indirect
github.com/minio/sha256-simd v0.1.1
github.com/maruel/panicparse v1.6.1
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0
github.com/minio/sha256-simd v1.0.0
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/onsi/gomega v1.10.3 // indirect
github.com/oschwald/geoip2-golang v1.4.0
github.com/oschwald/geoip2-golang v1.5.0
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.8.0
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
github.com/prometheus/client_golang v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sasha-s/go-deadlock v0.2.0
github.com/shirou/gopsutil/v3 v3.20.11
github.com/shirou/gopsutil/v3 v3.21.4
github.com/syncthing/notify v0.0.0-20210308121556-f45149b04939
github.com/syndtr/goleveldb v1.0.1-0.20200815071216-d9e9293bd0f7
github.com/thejerf/suture/v4 v4.0.0
github.com/urfave/cli v1.22.4
github.com/thejerf/suture/v4 v4.0.1
github.com/urfave/cli v1.22.5
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4
golang.org/x/text v0.3.4
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
golang.org/x/tools v0.1.0 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887
golang.org/x/text v0.3.6
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
golang.org/x/tools v0.1.0
google.golang.org/protobuf v1.26.0
)
go 1.14
+105 -68
View File
@@ -7,10 +7,10 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a h1:dUzIAgRmHLIUU0PT+OJ0X1WI5j1hlLbf+G420gUjIQg=
github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q=
github.com/AudriusButkevicius/recli v0.0.5 h1:xUa55PvWTHBm17T6RvjElRO3y5tALpdceH86vhzQ5wg=
github.com/AudriusButkevicius/recli v0.0.5/go.mod h1:Q2E26yc6RvWWEz/TJ/goUp6yXvipYdJI096hpoaqsNs=
github.com/AudriusButkevicius/pfilter v0.0.0-20210511165305-e9aaf99ab213 h1:9E6vGKdipZ+AAkU19TUb5JQKMf44CGAYMtXDAyfonO4=
github.com/AudriusButkevicius/pfilter v0.0.0-20210511165305-e9aaf99ab213/go.mod h1:EEEtt5r8y0gGHlRFF2+cLx0WUy/rKHnjALmom5E0+74=
github.com/AudriusButkevicius/recli v0.0.6 h1:hY9KH09vIbx0fYpkvdWbvnh67uDiuJEVDGhXlefysDQ=
github.com/AudriusButkevicius/recli v0.0.6/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -21,8 +21,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/kong v0.2.12 h1:X3kkCOXGUNzLmiu+nQtoxWqj4U2a39MpSJR3QdQXOwI=
github.com/alecthomas/kong v0.2.12/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/alecthomas/kong v0.2.16 h1:F232CiYSn54Tnl1sJGTeHmx4vJDNLVP2b9yCVMOQwHQ=
github.com/alecthomas/kong v0.2.16/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -50,14 +50,12 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7
github.com/calmh/xdr v1.1.0 h1:U/Dd4CXNLoo8EiQ4ulJUXkgO1/EyQLgDKLgpY1SOoJE=
github.com/calmh/xdr v1.1.0/go.mod h1:E8sz2ByAdXC8MbANf1LCRYzedSnnc+/sXXJs/PVqoeg=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/ccding/go-stun v0.1.2 h1:1CZhjVwfyO/jGxk06a+0OSOGBWZu588kuZQQO4nihsw=
github.com/ccding/go-stun v0.1.2/go.mod h1:3FK1bMar37f7jqVY7q/63k3OMX1c47pGCufzt3X0sYE=
github.com/ccding/go-stun v0.1.3 h1:uEAFsxqPBuo4tvILfloEHUBO3b4BlEAMnx2PZdh54jE=
github.com/ccding/go-stun v0.1.3/go.mod h1:cCZjJ1J3WFSJV6Wj8Y9Di8JMTsEXh6uv2eNmLzKaUeM=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20210429200110-83314bf6d27c h1:Tt0GPSnn14HRN/+db/WGBPDnUykj4b7EBNtBdHVQFiY=
github.com/certifi/gocertifi v0.0.0-20210429200110-83314bf6d27c/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
@@ -72,8 +70,9 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
@@ -106,14 +105,15 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag=
github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E=
github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -127,20 +127,23 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -150,8 +153,10 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -160,8 +165,11 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -176,12 +184,13 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/greatroar/blobloom v0.5.0 h1:jNbCsgDpZ23AI6jgZsXm7oFatkFaLCxr+ZWzlYasONU=
github.com/greatroar/blobloom v0.5.0/go.mod h1:M+yFtr/P96aNZYDYowvNWL3WdDluSMK2PPPHN49LMw8=
github.com/greatroar/blobloom v0.7.0 h1:gZXk/iFa20AgD3merUnWBIJmsvUJTsTJExO6+7N7Mlw=
github.com/greatroar/blobloom v0.7.0/go.mod h1:M+yFtr/P96aNZYDYowvNWL3WdDluSMK2PPPHN49LMw8=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -198,8 +207,9 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
@@ -208,8 +218,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackpal/gateway v1.0.6 h1:/MJORKvJEwNVldtGVJC2p2cwCnsSoLn3hl3zxmZT7tk=
github.com/jackpal/gateway v1.0.6/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/jackpal/gateway v1.0.7 h1:7tIFeCGmpyrMx9qvT0EgYUi7cxVW48a0mMvnIL17bPM=
github.com/jackpal/gateway v1.0.7/go.mod h1:aRcO0UFKt+MgIZmRmvOmnejdDT4Y1DNiNOsSd1AcIbA=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@@ -228,8 +238,11 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -238,8 +251,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4=
@@ -252,10 +265,13 @@ github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl5
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/maruel/panicparse v1.5.1 h1:hUPcXI7ubtEqj/k+P34KsHQqb86zuVk7zBfkP6tBBPc=
github.com/maruel/panicparse v1.5.1/go.mod h1:aOutY/MUjdj80R0AEVI9qE2zHqig+67t2ffUDDiLzAM=
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/maruel/panicparse v1.6.1 h1:803MjBzGcUgE1vYgg3UMNq3G1oyYeKkMu3t6hBS97x0=
github.com/maruel/panicparse v1.6.1/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0=
github.com/maruel/panicparse/v2 v2.1.1/go.mod h1:AeTWdCE4lcq8OKsLb6cHSj1RWHVSnV9HBCk7sKLF4Jg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@@ -265,11 +281,11 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 h1:8E6DrFvII6QR4eJ3PkFvV+lc03P+2qwqTPLm1ax7694=
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0/go.mod h1:fcEyUyXZXoV4Abw8DX0t7wyL8mCDxXyU4iAFZfT3IHw=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY=
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -321,10 +337,10 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oschwald/geoip2-golang v1.4.0 h1:5RlrjCgRyIGDz/mBmPfnAF4h8k0IAcRv9PvrpOfz+Ug=
github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng=
github.com/oschwald/maxminddb-golang v1.6.0 h1:KAJSjdHQ8Kv45nFIbtoLGrGWqHFajOIm7skTyz/+Dls=
github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w=
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -347,8 +363,8 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -361,34 +377,36 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.20.11 h1:NeVf1K0cgxsWz+N3671ojRptdgzvp7BXL3KV21R0JnA=
github.com/shirou/gopsutil/v3 v3.20.11/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
github.com/shirou/gopsutil/v3 v3.21.4 h1:XB/+p+kVnyYLuPHCfa99lxz2aJyvVhnyd+FxZqH/k7M=
github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
@@ -409,7 +427,6 @@ github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
@@ -432,25 +449,29 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syncthing/notify v0.0.0-20210308121556-f45149b04939 h1:InjitJPCBfhc1/DP0Z8OglJq5qvQD+J0o64TFyenf68=
github.com/syncthing/notify v0.0.0-20210308121556-f45149b04939/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
github.com/syndtr/goleveldb v1.0.1-0.20200815071216-d9e9293bd0f7 h1:udtnv1cokhJYqnUfCMCppJ71bFN9VKfG1BQ6UsYZnx8=
github.com/syndtr/goleveldb v1.0.1-0.20200815071216-d9e9293bd0f7/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/thejerf/suture/v4 v4.0.0 h1:GX3X+1Qaewtj9flL2wgoTBfLA5NcmrCY39TJRpPbUrI=
github.com/thejerf/suture/v4 v4.0.0/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs=
github.com/thejerf/suture/v4 v4.0.1 h1:CLnC1wxLAiHA5zTbbvhSWMupVuGe5ZJ7YddWE3lvb4M=
github.com/thejerf/suture/v4 v4.0.1/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0 h1:okhMind4q9H1OxF44gNegWkiP4H/gsTFLalHFa4OOUI=
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0/go.mod h1:TTbGUfE+cXXceWtbTHq6lqcTvYPBKLNejBEbnUsQJtU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
@@ -478,8 +499,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -489,6 +510,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -510,6 +532,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -517,8 +540,9 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -531,6 +555,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -560,29 +585,35 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -595,7 +626,9 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -619,6 +652,7 @@ google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@@ -630,14 +664,17 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+1 -1
View File
@@ -18,7 +18,7 @@
"Advanced": "Avanceret",
"Advanced Configuration": "Avanceret konfiguration",
"All Data": "Alt data",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alle mapper delt med denne enhed, skal beskyttes med adgangskode, således at alle sendte data er ikke-læsbare uden den angivne adgangskode.",
"Allow Anonymous Usage Reporting?": "Tillad anonym brugerstatistik?",
"Allowed Networks": "Tilladte netværk",
"Alphabetic": "Alfabetisk",
+4 -4
View File
@@ -25,7 +25,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"Are you sure you want to continue?": "Sei sicuro di voler continuare?",
"Are you sure you want to permanently delete all these files?": "Sei sicuro di voler eliminare definitivamente tutti questi file?",
"Are you sure you want to remove device {%name%}?": "Sei sicuro di voler rimuovere il dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Sei sicuro di voler rimuovere la cartella {{label}}?",
@@ -163,7 +163,7 @@
"Ignored at": "Ignorato a",
"Incoming Rate Limit (KiB/s)": "Limite Velocità in Ingresso (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configurazione non corretta potrebbe danneggiare il contenuto delle cartelle e rendere Syncthing inoperativo.",
"Introduced By": "Introdotto Da",
"Introduced By": "Introdotto da",
"Introducer": "Introduttore",
"Inversion of the given condition (i.e. do not exclude)": "Inversione della condizione indicata (ad es. non escludere)",
"Keep Versions": "Versioni Mantenute",
@@ -378,7 +378,7 @@
"Uptime": "Tempo di Funzionamento",
"Usage reporting is always enabled for candidate releases.": "Le segnalazioni di utilizzo sono sempre abilitate le versioni candidate al rilascio.",
"Use HTTPS for GUI": "Utilizza HTTPS per l'interfaccia grafica",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Utente/Password non sono stati impostati per l'autenticazione GUI. Considerane la configurazione.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Utente/password non sono stati impostati per autenticazione GUI. Considerane la configurazione.",
"Version": "Versione",
"Versions": "Versioni",
"Versions Path": "Percorso Cartella Versioni",
@@ -386,7 +386,7 @@
"Waiting to Clean": "In attesa di Pulizia",
"Waiting to Scan": "In attesa di Scansione",
"Waiting to Sync": "In attesa di Sincronizzazione",
"Warning": "Warning",
"Warning": "Attenzione",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Attenzione, questo percorso è una cartella superiore di una cartella esistente \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Attenzione, questo percorso è una cartella superiore di una cartella esistente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Attenzione, questo percorso è una sottocartella di una cartella esistente \"{{otherFolder}}\".",
+8 -8
View File
@@ -62,7 +62,7 @@
"Currently Shared With Devices": "現在共有中のデバイス",
"Danger!": "危険!",
"Debugging Facilities": "デバッグ機能",
"Default Configuration": "Default Configuration",
"Default Configuration": "デフォルトの設定",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "デフォルトのフォルダーパス",
@@ -102,9 +102,9 @@
"Downloading": "ダウンロード中",
"Edit": "編集",
"Edit Device": "デバイスの編集",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Device Defaults": "デバイスのデフォルトの編集",
"Edit Folder": "フォルダーの編集",
"Edit Folder Defaults": "Edit Folder Defaults",
"Edit Folder Defaults": "フォルダーのデフォルトの編集",
"Editing {%path%}.": "{{path}} を編集中",
"Enable Crash Reporting": "クラッシュレポートを有効にする",
"Enable NAT traversal": "NATトラバーサルを有効にする",
@@ -153,7 +153,7 @@
"Help": "ヘルプ",
"Home page": "ホームページ",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"If untrusted, enter encryption password": "信頼しない場合、暗号化パスワードを入力",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
"Ignore": "無視",
"Ignore Patterns": "無視するファイル名",
@@ -202,7 +202,7 @@
"No File Versioning": "バージョン管理をしない",
"No files will be deleted as a result of this operation.": "この操作を行っても、ファイルが削除されることはありません。",
"No upgrades": "アップグレードしない",
"Not shared": "Not shared",
"Not shared": "非共有",
"Notice": "通知",
"OK": "OK",
"Off": "オフ",
@@ -279,7 +279,7 @@
"Share Folder": "フォルダーを共有する",
"Share Folders With Device": "このデバイスと共有するフォルダー",
"Share this folder?": "このフォルダーを共有しますか?",
"Shared Folders": "Shared Folders",
"Shared Folders": "共有中のフォルダー",
"Shared With": "共有中のデバイス",
"Sharing": "共有",
"Show ID": "IDを表示",
@@ -367,8 +367,8 @@
"Unknown": "不明",
"Unshared": "非共有",
"Unshared Devices": "非共有のデバイス",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Unshared Folders": "非共有のフォルダー",
"Untrusted": "信頼しない",
"Up to Date": "最新",
"Updated": "更新",
"Upgrade": "アップグレード",
+11 -11
View File
@@ -42,7 +42,7 @@
"Bugs": "Ошибки",
"Changelog": "Журнал изменений",
"Clean out after": "Очистить после",
"Cleaning Versions": "Cleaning Versions",
"Cleaning Versions": "Очистка Версий",
"Cleanup Interval": "Интервал очистки",
"Click to see discovery failures": "Щёлкните, чтобы посмотреть ошибки",
"Close": "Закрыть",
@@ -77,7 +77,7 @@
"Device ID": "ID устройства",
"Device Identification": "Идентификация устройства",
"Device Name": "Имя устройства",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device is untrusted, enter encryption password": "Устройство ненадёжно, укажите пароль шифрования",
"Device rate limits": "Ограничения скорости для устройства",
"Device that last modified the item": "Устройство, последним изменившее объект",
"Devices": "Устройства",
@@ -102,9 +102,9 @@
"Downloading": "Загрузка",
"Edit": "Редактировать",
"Edit Device": "Редактирование устройства",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Device Defaults": "Изменить умолчания устройства",
"Edit Folder": "Редактирование папки",
"Edit Folder Defaults": "Edit Folder Defaults",
"Edit Folder Defaults": "Изменить умолчания папки",
"Editing {%path%}.": "Правка {{path}}.",
"Enable Crash Reporting": "Включить отчёты о сбоях",
"Enable NAT traversal": "Включить NAT traversal",
@@ -134,8 +134,8 @@
"Folder Label": "Ярлык папки",
"Folder Path": "Путь к папке",
"Folder Type": "Тип папки",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Folder type \"{{receiveEncrypted}}\" can only be set when adding a new folder.",
"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.": "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.",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Тип папки \"{{receiveEncrypted}}\" может быть указан только при создании новой.",
"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.": "Тип папки \"{{receiveEncrypted}}\" не может быть изменён после добавления. Вам необходимо убрать папку, удалить или дешифровать данные на диске, а затем добавить папку заново.",
"Folders": "Папки",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Для следующих папок произошла ошибка при запуске отслеживания изменений. Попытки будут повторяться раз в минуту, и ошибки скоро могут быть устранены. Если этого не произойдёт, попробуйте разобраться в причинах и попросите поддержки, если у вас не получится.",
"Full Rescan Interval (s)": "Интервал полного сканирования (в секундах)",
@@ -153,7 +153,7 @@
"Help": "Помощь",
"Home page": "Сайт",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Ваши настройки указывают что вы не хотите, чтобы эта функция была включена. Мы отключили отправку отчетов о сбоях.",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"If untrusted, enter encryption password": "Если ненадёжное, укажите пароль шифрования",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Если вы хотите запретить другим пользователям на этом компьютере доступ к Syncthing и через него к вашим файлам, подумайте о настройке аутентификации.",
"Ignore": "Игнорировать",
"Ignore Patterns": "Шаблоны игнорирования",
@@ -328,8 +328,8 @@
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Введён недопустимый ID устройства. Он должен состоять из букв и цифр, может включать пробелы и дефисы, длина должна быть 52 или 56 символов.",
"The folder ID cannot be blank.": "ID папки не может быть пустым.",
"The folder ID must be unique.": "ID папки должен быть уникальным.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Содержание папки на других устройствах будет перезаписано и станет идентично этому устройству. Файлы, отсутствующие здесь, будут удалены на других устройствах.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Содержание папки на этом устройстве будет перезаписано и станет идентично другим устройствам. Новые файлы на этом устройстве будут удалены.",
"The folder path cannot be blank.": "Путь к папке не должен быть пустым.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Используются следующие интервалы: в первый час версия меняется каждые 30 секунд, в первый день - каждый час, первые 30 дней - каждый день, после, до максимального срока - каждую неделю.",
"The following items could not be synchronized.": "Невозможно синхронизировать следующие объекты",
@@ -368,7 +368,7 @@
"Unshared": "Необщедоступно",
"Unshared Devices": "Устройства без общего доступа",
"Unshared Folders": "Необщедоступные папки",
"Untrusted": "Untrusted",
"Untrusted": "Ненадёжный",
"Up to Date": "В актуальном состоянии",
"Updated": "Обновлено",
"Upgrade": "Обновить",
@@ -414,5 +414,5 @@
"seconds": "сек.",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хочет поделиться папкой «{{folder}}».",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хочет поделиться папкой «{{folderlabel}}» ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} может повторно рекомендовать это устройство."
}
+2 -2
View File
@@ -161,7 +161,7 @@
"Ignored Devices": "Ignorerade enheter",
"Ignored Folders": "Ignorerade mappar",
"Ignored at": "Ignorerad vid",
"Incoming Rate Limit (KiB/s)": "Inkommande hastighetsbegränsning (KiB/s)",
"Incoming Rate Limit (KiB/s)": "Inkommande hastighetsgräns (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Inkorrekt konfiguration kan skada innehållet i mappen and få Syncthing att sluta fungera.",
"Introduced By": "Introducerad av",
"Introducer": "Introduktör",
@@ -211,7 +211,7 @@
"Options": "Alternativ",
"Out of Sync": "Osynkroniserad",
"Out of Sync Items": "Osynkroniserade objekt",
"Outgoing Rate Limit (KiB/s)": "Utgående hastighetsbegränsning (KiB/s)",
"Outgoing Rate Limit (KiB/s)": "Utgående hastighetsgräns (KiB/s)",
"Override Changes": "Åsidosätt förändringar",
"Path": "Sökväg",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Sökväg till mappen på din dator. Kommer att skapas om det inte finns. Tecknet tilde (~) kan användas som en genväg för",
+37 -37
View File
@@ -1,6 +1,6 @@
{
"A device with that ID is already added.": "您已添加过相同 ID 的设备",
"A negative number of days doesn't make sense.": "负数天数没有意义。",
"A negative number of days doesn't make sense.": "天数不能为负。",
"A new major version may not be compatible with previous versions.": "重大更新可能与之前的版本之间无法兼容",
"API Key": "API Key",
"About": "关于",
@@ -18,14 +18,14 @@
"Advanced": "高级",
"Advanced Configuration": "高级配置",
"All Data": "所有数据",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "与此设备共享的所有文件夹都必须有密码保护,这样所有发送的数据在没有密码的情况下是不可读的。",
"Allow Anonymous Usage Reporting?": "允许匿名使用报告?",
"Allowed Networks": "允许的网络",
"Alphabetic": "字母顺序",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令接管了版本控制。该外部命令必须自行从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
"Anonymous Usage Reporting": "匿名使用报告",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情况的报告格式已经变更。是否要迁移到新的格式?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"Are you sure you want to continue?": "您确定要继续吗?",
"Are you sure you want to permanently delete all these files?": "确认要永久删除这些文件吗?",
"Are you sure you want to remove device {%name%}?": "您确定要移除设备 {{name}} 吗?",
"Are you sure you want to remove folder {%label%}?": "您确定要移除文件夹 {{label}} 吗?",
@@ -55,23 +55,23 @@
"Connection Type": "连接类型",
"Connections": "连接",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing 现在可以持续监视更改了。这将检测磁盘上的更改,然后对有修改的路径发起扫描。这样的好处是更改可以更快地传播,且需要的完整扫描会更少。",
"Copied from elsewhere": "从其设备复制",
"Copied from elsewhere": "从其设备复制",
"Copied from original": "从源复制",
"Copyright © 2014-2019 the following Contributors:": "版权所有 © 2014-2019 以下贡献者:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "正在创建忽略模式,覆盖位于 {{path}} 的已有文件。",
"Currently Shared With Devices": "当前设备已共享",
"Danger!": "危险!",
"Debugging Facilities": "调试功能",
"Default Configuration": "Default Configuration",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Configuration": "默认配置",
"Default Device": "默认设备",
"Default Folder": "默认文件夹",
"Default Folder Path": "默认文件夹路径",
"Defaults": "Defaults",
"Delete Unexpected Items": "Delete Unexpected Items",
"Defaults": "默认值",
"Delete Unexpected Items": "删除特殊项目",
"Deleted": "已删除",
"Deselect All": "取消全选",
"Deselect devices to stop sharing this folder with.": "反选设备以停止共享此文件夹",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
"Deselect folders to stop sharing with this device.": "取消选择文件夹以停止与此设备共享。",
"Device": "设备",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "设备 \"{{name}}\"(位于 {{address}} 的 {{device}})请求连接。是否添加新设备?",
"Device ID": "设备 ID",
@@ -102,9 +102,9 @@
"Downloading": "下载中",
"Edit": "选项",
"Edit Device": "编辑设备",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Device Defaults": "编辑设备默认值",
"Edit Folder": "编辑文件夹",
"Edit Folder Defaults": "Edit Folder Defaults",
"Edit Folder Defaults": "编辑文件夹默认值",
"Editing {%path%}.": "正在编辑 {{path}}。",
"Enable Crash Reporting": "启用自动发送崩溃报告",
"Enable NAT traversal": "启用 NAT 遍历",
@@ -134,11 +134,11 @@
"Folder Label": "文件夹标签",
"Folder Path": "文件夹路径",
"Folder Type": "文件夹类型",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Folder type \"{{receiveEncrypted}}\" can only be set when adding a new folder.",
"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.": "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.",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "只能在添加新文件夹时设置文件夹类型{{receiveEncrypted}}",
"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.": "添加文件夹后,无法更改文件夹类型“ {{receiveEncrypted}}”。您需要删除该文件夹,删除或解密磁盘上的数据,然后再次添加该文件夹。",
"Folders": "文件夹",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "开始监视下列文件夹时发生错误。由于每分钟都会重试,所以错误可能很快就消失。如果它们仍存在,请试着修复潜在问题,如不会则请求帮助。",
"Full Rescan Interval (s)": "完整扫描间隔",
"Full Rescan Interval (s)": "完整扫描间隔(秒)",
"GUI": "图形用户界面",
"GUI Authentication Password": "图形管理界面密码",
"GUI Authentication User": "图形管理界面用户名",
@@ -153,7 +153,7 @@
"Help": "帮助",
"Home page": "主页",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "我们已经为您关闭了自动崩溃报告发送功能,因为您当前的设置显示您可能并不想启用该功能。",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"If untrusted, enter encryption password": "如想更安全,请输入加密密码",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "如果要阻止此计算机上的其他用户访问Syncthing并通过它访问文件,请考虑设置身份验证。",
"Ignore": "忽略",
"Ignore Patterns": "忽略模式",
@@ -165,7 +165,7 @@
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "错误的配置可能损坏您文件夹内的内容,使得 Syncthing 无法工作。",
"Introduced By": "介绍自",
"Introducer": "作为中介",
"Inversion of the given condition (i.e. do not exclude)": "本条件取反(例如:不排除某项",
"Inversion of the given condition (i.e. do not exclude)": "反转本条件(即:不排除)",
"Keep Versions": "保留版本数量",
"LDAP": "LDAP",
"Largest First": "大文件优先",
@@ -202,7 +202,7 @@
"No File Versioning": "不启用版本控制",
"No files will be deleted as a result of this operation.": "此操作结果不会删除任何文件。",
"No upgrades": "无更新",
"Not shared": "Not shared",
"Not shared": "不共享",
"Notice": "提示",
"OK": "确定",
"Off": "关闭",
@@ -236,15 +236,15 @@
"Preview Usage Report": "预览使用报告",
"Quick guide to supported patterns": "支持的通配符的简单教程:",
"Random": "随机顺序",
"Receive Encrypted": "Receive Encrypted",
"Receive Encrypted": "加密接收",
"Receive Only": "仅接收",
"Received data is already encrypted": "Received data is already encrypted",
"Received data is already encrypted": "已加密接收到的数据",
"Recent Changes": "最近更改",
"Reduced by ignore patterns": "已由忽略模式缩减",
"Release Notes": "发布说明",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "发布候选版包含最新的特性和修复。它们跟传统的 Syncthing 双周发布版类似。",
"Remote Devices": "远程设备",
"Remote GUI": "Remote GUI",
"Remote GUI": "远程GUI",
"Remove": "移除",
"Remove Device": "移除设备",
"Remove Folder": "移除文件夹",
@@ -268,7 +268,7 @@
"Select All": "全选",
"Select a version": "选择版本",
"Select additional devices to share this folder with.": "选择其他共享此文件夹的设备",
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
"Select additional folders to share with this device.": "选择要与此设备共享的其它文件夹。",
"Select latest version": "选择最新的版本",
"Select oldest version": "选择最旧的版本",
"Select the folders to share with this device.": "选择与该设备共享的文件夹。",
@@ -279,7 +279,7 @@
"Share Folder": "共享文件夹",
"Share Folders With Device": "将指定文件夹共享给设备",
"Share this folder?": "是否共享该文件夹?",
"Shared Folders": "Shared Folders",
"Shared Folders": "共享文件夹",
"Shared With": "共享给",
"Sharing": "共享",
"Show ID": "显示 ID",
@@ -302,7 +302,7 @@
"Start Browser": "启动浏览器",
"Statistics": "统计",
"Stopped": "已停止",
"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.": "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.",
"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.": "仅存储和同步加密的数据。所有连接的设备上的文件夹也需要使用相同的密码设置,或者也必须设置为“ {{receiveEncrypted}}”类型。",
"Support": "支持",
"Support Bundle": "支持捆绑包",
"Sync Protocol Listen Addresses": "协议监听地址",
@@ -328,13 +328,13 @@
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "输入的设备 ID 似乎无效。设备 ID 包含字母和数字,长度为 52 或 56,空格和横线不计在内。",
"The folder ID cannot be blank.": "文件夹 ID 不能为空。",
"The folder ID must be unique.": "文件夹 ID 不得重复。",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "其它设备上的文件夹内容将被覆盖,以与此设备相同。此处不存在的文件将在其他设备上删除。",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "该设备上的文件夹内容将被覆盖,使其与其他设备相同。在此处新添加的文件将被删除。",
"The folder path cannot be blank.": "文件夹路径不能为空。",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保留的历史版本会遵循以下条件:最近一小时内的历史版本,更新间隔小于三十秒的仅保留一份。最近一天内的历史版本,更新间隔小于一小时的仅保留一份。最近一个月内的历史版本,更新间隔小于一天的仅保留一份。距离现在超过一个月且小于最长保留时间的,更新间隔小于一周的仅保留一份。",
"The following items could not be synchronized.": "下列项目无法被同步。",
"The following items were changed locally.": "下列项目存在本地更改。",
"The following unexpected items were found.": "The following unexpected items were found.",
"The following unexpected items were found.": "找到了以下特殊项。",
"The interval must be a positive number of seconds.": "间隔必须为正数秒。",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "在版本目录中运行清理的间隔(秒)。0表示禁用定期清除。",
"The maximum age must be a number and cannot be blank.": "最长保留时间必须为数字,且不能为空。",
@@ -347,7 +347,7 @@
"The rate limit must be a non-negative number (0: no limit)": "传输速度限制为非负整数(0 表示不限制)",
"The rescan interval must be a non-negative number of seconds.": "扫描间隔单位为秒,且不能为负数。",
"There are no devices to share this folder with.": "没有设备共享此文件夹",
"There are no folders to share with this device.": "There are no folders to share with this device.",
"There are no folders to share with this device.": "没有文件夹与此设备共享。",
"They are retried automatically and will be synced when the error is resolved.": "系统将会自动重试,当错误被解决时,它们将会被同步。",
"This Device": "当前设备",
"This can easily give hackers access to read and change any files on your computer.": "这会让骇客能够轻而易举地访问及修改您的文件。",
@@ -361,14 +361,14 @@
"Unavailable": "无效",
"Unavailable/Disabled by administrator or maintainer": "无效/禁用(由管理员或维护者)",
"Undecided (will prompt)": "待定(将提示)",
"Unexpected Items": "Unexpected Items",
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
"Unexpected Items": "特殊项目",
"Unexpected items have been found in this folder.": "在此文件夹中发现了特殊项目",
"Unignore": "解除忽略",
"Unknown": "未知",
"Unshared": "共享",
"Unshared Devices": "共享设备",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Unshared": "共享",
"Unshared Devices": "共享设备",
"Unshared Folders": "非共享文件夹",
"Untrusted": "不可信的",
"Up to Date": "同步完成",
"Updated": "已更新",
"Upgrade": "更新",
@@ -386,7 +386,7 @@
"Waiting to Clean": "等待清除",
"Waiting to Scan": "等待扫描",
"Waiting to Sync": "等待同步",
"Warning": "Warning",
"Warning": "警告",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "警告,该路径是已有文件夹\"{{otherFolder}}\"的上级目录。",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告,该路径是已有文件夹\"{{otherFolderLabel}}\" ({{otherFolder}})的上级目录。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告,该路径是已有文件夹\"{{otherFolder}}\"的下级目录。",
@@ -405,7 +405,7 @@
"You have no ignored folders.": "你没有已忽略的文件夹。",
"You have unsaved changes. Do you really want to discard them?": "你有未保存的更改。你真的要丢弃它们吗?",
"You must keep at least one version.": "您必须保留至少一个版本。",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "您绝对不应在“ {{receiveEncrypted}}”文件夹中添加或更改任何本地内容。",
"days": "天",
"directories": "目录",
"files": "文件",
@@ -414,5 +414,5 @@
"seconds": "秒",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想将 “{{folder}}” 文件夹共享给您。",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享 \"{{folderlabel}}\" ({{folder}}) 文件夹给您。",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}}可能会重新引入此设备。"
}
+2 -2
View File
@@ -560,7 +560,7 @@
<button ng-if="folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, false)">
<span class="fas fa-play"></span>&nbsp;<span translate>Resume</span>
</button>
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type && folder.versioning.type != 'external'">
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type && folder.versioning.type != 'external'" ng-disabled="folder.paused">
<span class="fas fa-undo"></span>&nbsp;<span translate>Versions</span>
</button>
<button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-disabled="['idle', 'stopped', 'unshared', 'outofsync', 'faileditems', 'localadditions'].indexOf(folderStatus(folder)) < 0">
@@ -713,7 +713,7 @@
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs" aria-label="{{'Disconnected' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
<span ng-switch-when="unused-disconnected"><span class="hidden-xs" translate>Disconnected (Unused)</span><span class="visible-xs" aria-label="{{'Disconnected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
</span>
<span>{{deviceName(deviceCfg)}}</span>
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
</h4>
</button>
<div id="device-{{$index}}" class="panel-collapse collapse">
@@ -19,7 +19,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, deepsource-autofix[bot], dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, mclang, mv1005, otbutz, perewa, rubenbe, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, deepsource-autofix[bot], dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, rubenbe, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙
</div>
</div>
<hr />
@@ -42,7 +42,7 @@ angular.module('syncthing.core')
$scope.pendingFolders = {};
$scope.progress = {};
$scope.version = {};
$scope.needed = {}
$scope.needed = {};
$scope.neededFolder = '';
$scope.failed = {};
$scope.localChanged = {};
@@ -673,7 +673,7 @@ angular.module('syncthing.core')
console.log("refreshNeed", $scope.neededFolder, data);
parseNeeded(data);
}).error($scope.emitHTTPError);
}
};
function needAction(file) {
var fDelete = 4096;
@@ -1010,7 +1010,7 @@ angular.module('syncthing.core')
if (days > 31) {
return '> 1 month';
}
res.push('' + days + 'd')
res.push('' + days + 'd');
seconds = seconds % 86400;
}
@@ -1018,7 +1018,7 @@ angular.module('syncthing.core')
var hours = 0;
if (seconds > 3600) {
hours = Math.floor(seconds / 3600);
res.push('' + hours + 'h')
res.push('' + hours + 'h');
seconds = seconds % 3600;
}
@@ -1759,7 +1759,7 @@ angular.module('syncthing.core')
};
$scope.config.devices = deviceList($scope.devices);
$scope.saveConfig();
}
};
$scope.isAtleastOneDevicePausedStateSetTo = function (pause) {
for (var id in $scope.devices) {
@@ -1768,8 +1768,8 @@ angular.module('syncthing.core')
}
}
return false
}
return false;
};
$scope.errorList = function () {
if (!$scope.errors) {
@@ -2779,7 +2779,7 @@ angular.module('syncthing.core')
var time = $filter('date')(status.when, "HH:mm:ss")
var err = status.error.replace(/.+: /, '');
return err + " (" + time + ")";
}
};
$scope.setCrashReportingEnabled = function (enabled) {
$scope.config.options.crashReportingEnabled = enabled;
@@ -2791,7 +2791,7 @@ angular.module('syncthing.core')
(address.indexOf('/') == 0 ||
address.indexOf('unix://') == 0 ||
address.indexOf('unixs://') == 0);
}
};
})
.directive('shareTemplate', function () {
return {
+13 -2
View File
@@ -32,7 +32,7 @@ import (
"unicode"
"github.com/julienschmidt/httprouter"
metrics "github.com/rcrowley/go-metrics"
"github.com/rcrowley/go-metrics"
"github.com/thejerf/suture/v4"
"github.com/vitrun/qart/qr"
"golang.org/x/text/runes"
@@ -162,7 +162,7 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
if err != nil {
return nil, err
}
tlsCfg := tlsutil.SecureDefault()
tlsCfg := tlsutil.SecureDefaultWithTLS12()
tlsCfg.Certificates = []tls.Certificate{cert}
if guiCfg.Network() == "unix" {
@@ -915,11 +915,16 @@ func (s *service) getDBFile(w http.ResponseWriter, r *http.Request) {
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
mtimeMapping, mtimeErr := s.model.GetMtimeMapping(folder, file)
sendJSON(w, map[string]interface{}{
"global": jsonFileInfo(gf),
"local": jsonFileInfo(lf),
"availability": av,
"mtime": map[string]interface{}{
"err": mtimeErr,
"value": mtimeMapping,
},
})
}
@@ -934,6 +939,8 @@ func (s *service) getDebugFile(w http.ResponseWriter, r *http.Request) {
return
}
mtimeMapping, mtimeErr := s.model.GetMtimeMapping(folder, file)
lf, _ := snap.Get(protocol.LocalDeviceID, file)
gf, _ := snap.GetGlobal(file)
av := snap.Availability(file)
@@ -944,6 +951,10 @@ func (s *service) getDebugFile(w http.ResponseWriter, r *http.Request) {
"local": jsonFileInfo(lf),
"availability": av,
"globalVersions": vl.String(),
"mtime": map[string]interface{}{
"err": mtimeErr,
"value": mtimeMapping,
},
})
}
+3 -13
View File
@@ -42,6 +42,7 @@ import (
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil"
"github.com/syncthing/syncthing/lib/ur"
"github.com/syncthing/syncthing/lib/util"
"github.com/thejerf/suture/v4"
)
@@ -126,6 +127,7 @@ func TestStopAfterBrokenConfig(t *testing.T) {
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, false).(*service)
defer os.Remove(token)
srv.started = make(chan string)
sup := suture.New("test", svcutil.SpecWithDebugLogger(l))
@@ -1178,7 +1180,7 @@ func TestBrowse(t *testing.T) {
for _, tc := range cases {
ret := browseFiles(tc.current, fs.FilesystemTypeBasic)
if !equalStrings(ret, tc.returns) {
if !util.EqualStrings(ret, tc.returns) {
t.Errorf("browseFiles(%q) => %q, expected %q", tc.current, ret, tc.returns)
}
}
@@ -1389,18 +1391,6 @@ func TestSanitizedHostname(t *testing.T) {
}
}
func equalStrings(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
// runningInContainer returns true if we are inside Docker or LXC. It might
// be prone to false negatives if things change in the future, but likely
// not false positives.
+2 -8
View File
@@ -721,10 +721,7 @@ func (m *Configuration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthConfig
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthConfig
}
if (iNdEx + skippy) > l {
@@ -840,10 +837,7 @@ func (m *Defaults) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthConfig
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthConfig
}
if (iNdEx + skippy) > l {
+27
View File
@@ -1343,3 +1343,30 @@ func TestInternalVersioningConfiguration(t *testing.T) {
}
}
}
func TestReceiveEncryptedFolderFixed(t *testing.T) {
cfg := Configuration{
Folders: []FolderConfiguration{
{
ID: "foo",
Path: "testdata",
Type: FolderTypeReceiveEncrypted,
DisableTempIndexes: false,
IgnorePerms: false,
},
},
}
cfg.prepare(device1)
if len(cfg.Folders) != 1 {
t.Fatal("Expected one folder")
}
f := cfg.Folders[0]
if !f.DisableTempIndexes {
t.Error("DisableTempIndexes should be true")
}
if !f.IgnorePerms {
t.Error("IgnorePerms should be true")
}
}
+1 -4
View File
@@ -924,10 +924,7 @@ func (m *DeviceConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDeviceconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDeviceconfiguration
}
if (iNdEx + skippy) > l {
+1
View File
@@ -214,6 +214,7 @@ func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices ma
}
if f.Type == FolderTypeReceiveEncrypted {
f.DisableTempIndexes = true
f.IgnorePerms = true
}
}
+2 -8
View File
@@ -968,10 +968,7 @@ func (m *FolderDeviceConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthFolderconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFolderconfiguration
}
if (iNdEx + skippy) > l {
@@ -1823,10 +1820,7 @@ func (m *FolderConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthFolderconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFolderconfiguration
}
if (iNdEx + skippy) > l {
+1 -4
View File
@@ -702,10 +702,7 @@ func (m *GUIConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGuiconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGuiconfiguration
}
if (iNdEx + skippy) > l {
+1 -4
View File
@@ -425,10 +425,7 @@ func (m *LDAPConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthLdapconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthLdapconfiguration
}
if (iNdEx + skippy) > l {
+2 -8
View File
@@ -435,10 +435,7 @@ func (m *ObservedFolder) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthObserved
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthObserved
}
if (iNdEx + skippy) > l {
@@ -618,10 +615,7 @@ func (m *ObservedDevice) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthObserved
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthObserved
}
if (iNdEx + skippy) > l {
+245 -206
View File
@@ -81,6 +81,9 @@ type OptionsConfiguration struct {
// meaning no limit. Affects incoming connections and prevents
// attempting outgoing connections.
ConnectionLimitMax int `protobuf:"varint,52,opt,name=connection_limit_max,json=connectionLimitMax,proto3,casttype=int" json:"connectionLimitMax" xml:"connectionLimitMax"`
// When set, this allows TLS 1.2 on sync connections, where we otherwise
// default to TLS 1.3+ only.
InsecureAllowOldTLSVersions bool `protobuf:"varint,53,opt,name=insecure_allow_old_tls_versions,json=insecureAllowOldTlsVersions,proto3" json:"insecureAllowOldTLSVersions" xml:"insecureAllowOldTLSVersions"`
// Legacy deprecated
DeprecatedUPnPEnabled bool `protobuf:"varint,9000,opt,name=upnp_enabled,json=upnpEnabled,proto3" json:"-" xml:"upnpEnabled,omitempty"` // Deprecated: Do not use.
DeprecatedUPnPLeaseM int `protobuf:"varint,9001,opt,name=upnp_lease_m,json=upnpLeaseM,proto3,casttype=int" json:"-" xml:"upnpLeaseMinutes,omitempty"` // Deprecated: Do not use.
@@ -133,209 +136,213 @@ func init() {
}
var fileDescriptor_d09882599506ca03 = []byte{
// 3218 bytes of a gzipped FileDescriptorProto
// 3295 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x5a, 0x5d, 0x6c, 0x1d, 0x47,
0xf5, 0xcf, 0x26, 0x4d, 0xda, 0x6c, 0x1c, 0x27, 0x1e, 0x3b, 0xf6, 0x36, 0x49, 0xbd, 0xee, 0xcd,
0x4d, 0xeb, 0x7e, 0x25, 0xb6, 0x93, 0xe6, 0x9f, 0x46, 0xfa, 0xab, 0xf8, 0xa3, 0x26, 0x6e, 0xec,
0xc4, 0x1a, 0xdb, 0x2a, 0x2a, 0x42, 0xab, 0xb9, 0x7b, 0xe7, 0xda, 0x8b, 0xf7, 0xce, 0xde, 0xee,
0xcc, 0xfa, 0xda, 0x2d, 0x82, 0xaa, 0x88, 0x8f, 0x37, 0xc0, 0xe2, 0x43, 0x02, 0x09, 0x15, 0x01,
0x12, 0xa5, 0x14, 0x21, 0x21, 0x21, 0xc1, 0x0b, 0x08, 0x09, 0xa9, 0x82, 0x07, 0xfb, 0xb1, 0x12,
0x65, 0x51, 0x9d, 0x3e, 0xdd, 0x07, 0x1e, 0xee, 0xa3, 0x79, 0x41, 0x33, 0xfb, 0x35, 0xbb, 0x3b,
0xb7, 0xc9, 0xdb, 0xdd, 0xf3, 0x3b, 0x73, 0xe6, 0x77, 0xe6, 0xe3, 0xcc, 0x39, 0x33, 0x57, 0xbf,
0xec, 0x3a, 0xb5, 0xab, 0xb6, 0x47, 0x1a, 0xce, 0xfa, 0x55, 0xaf, 0xc5, 0x1c, 0x8f, 0xd0, 0xe8,
0x2b, 0xf0, 0x11, 0xff, 0xba, 0xd2, 0xf2, 0x3d, 0xe6, 0x81, 0x13, 0x91, 0xf0, 0xfc, 0x88, 0xa4,
0xce, 0x02, 0xe2, 0x90, 0xf5, 0x48, 0xe1, 0xfc, 0x39, 0x09, 0xa0, 0xce, 0x9b, 0x38, 0x16, 0x9f,
0xc4, 0xdb, 0x2c, 0xfa, 0x59, 0xf9, 0xe8, 0xb6, 0x3e, 0x74, 0x2f, 0xea, 0x61, 0x56, 0xee, 0x01,
0xfc, 0x54, 0xd3, 0xcf, 0xba, 0x0e, 0x65, 0x98, 0x58, 0xa8, 0x5e, 0xf7, 0x31, 0xa5, 0x98, 0x1a,
0xda, 0xd8, 0xb1, 0xf1, 0x93, 0x33, 0xf4, 0x20, 0x34, 0x01, 0x44, 0xed, 0x45, 0x01, 0x4f, 0x27,
0x68, 0x27, 0x34, 0xcf, 0xb8, 0x79, 0x51, 0x37, 0x34, 0x2f, 0x6f, 0x37, 0xdd, 0x5b, 0x95, 0x9c,
0xbc, 0x32, 0x56, 0xc7, 0x0d, 0x14, 0xb8, 0xec, 0x56, 0x25, 0xfe, 0x51, 0x39, 0xdc, 0xab, 0x3e,
0x1a, 0xff, 0xde, 0xdd, 0xaf, 0x2a, 0x8c, 0xc3, 0xa2, 0x69, 0xf0, 0x1f, 0x4d, 0x37, 0xd6, 0x5d,
0xaf, 0x86, 0x5c, 0xab, 0xee, 0x50, 0xdb, 0xdb, 0xc2, 0xfe, 0x8e, 0x45, 0xb1, 0xbf, 0x85, 0x7d,
0x6a, 0x1c, 0x15, 0x44, 0x7f, 0xaf, 0x1d, 0x84, 0xe6, 0x20, 0x44, 0xed, 0xcf, 0x0b, 0xbd, 0x69,
0x42, 0x56, 0x22, 0xbc, 0x13, 0x9a, 0xe7, 0xd6, 0x13, 0x99, 0x17, 0x10, 0x1b, 0xc7, 0x40, 0x37,
0x34, 0x9f, 0x17, 0x84, 0x55, 0xa8, 0x82, 0x77, 0x67, 0xaf, 0x3a, 0xa4, 0x52, 0xed, 0xee, 0x55,
0xd5, 0x1d, 0xe4, 0x1d, 0x55, 0x71, 0x83, 0xc3, 0x51, 0xc3, 0xb9, 0xc4, 0xa9, 0x58, 0x0e, 0x3e,
0x55, 0x39, 0x8c, 0x09, 0xaa, 0xb9, 0xb8, 0x6e, 0x1c, 0x1b, 0xd3, 0xc6, 0x1f, 0x9b, 0x79, 0x8f,
0x3b, 0x7c, 0x36, 0xb5, 0xf8, 0x4a, 0x04, 0x96, 0xbd, 0x8d, 0x81, 0x6e, 0x68, 0x3e, 0xab, 0xf0,
0x36, 0x46, 0x25, 0x77, 0x99, 0x1f, 0x60, 0xee, 0x6b, 0x0f, 0x33, 0xbd, 0x80, 0xc3, 0xbd, 0xea,
0x23, 0xbc, 0xe9, 0xee, 0x7e, 0xb5, 0x44, 0xaa, 0xe4, 0x66, 0x2c, 0x07, 0x1f, 0x6b, 0xfa, 0x88,
0xeb, 0xd9, 0x4a, 0x2f, 0x1f, 0x11, 0x5e, 0xfe, 0x9c, 0x7b, 0x79, 0x66, 0x91, 0xeb, 0xe4, 0x9c,
0x1c, 0x72, 0x63, 0x51, 0xc1, 0xc7, 0x67, 0xa2, 0x25, 0xa8, 0x00, 0x15, 0x2e, 0xaa, 0x8d, 0xf4,
0x90, 0x4b, 0x0e, 0x16, 0xf9, 0xc0, 0x73, 0xa2, 0x41, 0xc9, 0xbd, 0x7f, 0x68, 0xfa, 0x60, 0xe4,
0x1e, 0x8a, 0x6d, 0x59, 0x2d, 0xcf, 0x67, 0xc6, 0xf1, 0x31, 0x6d, 0xfc, 0xf8, 0xcc, 0x8f, 0xb9,
0x6b, 0x7d, 0x89, 0xa9, 0x65, 0xcf, 0x67, 0x9d, 0xd0, 0x1c, 0xc8, 0x75, 0xcd, 0x85, 0xdd, 0xd0,
0x7c, 0xba, 0xec, 0x14, 0x47, 0x24, 0x8f, 0xa6, 0x26, 0x27, 0xa6, 0xfe, 0xaf, 0x72, 0x18, 0x9a,
0xc7, 0x1c, 0xc2, 0x3a, 0x7b, 0x55, 0x85, 0x19, 0x95, 0xf0, 0x70, 0xaf, 0x7a, 0x5c, 0x34, 0xdd,
0xdd, 0xaf, 0xe6, 0x98, 0xc0, 0xb2, 0x2e, 0xf8, 0xfa, 0x51, 0x7d, 0xac, 0xe0, 0x4d, 0x33, 0x70,
0x99, 0x63, 0x23, 0xca, 0x92, 0xb8, 0x61, 0x9c, 0x18, 0xd3, 0xc6, 0x4f, 0xce, 0xfc, 0x91, 0xbb,
0xd6, 0x9f, 0x18, 0x5c, 0x9a, 0xe5, 0x3b, 0xb9, 0x13, 0x9a, 0x83, 0x39, 0xa3, 0x91, 0xb8, 0x1b,
0x9a, 0x37, 0xca, 0xee, 0x45, 0x98, 0xe4, 0xe0, 0x17, 0x1b, 0x8d, 0xc9, 0xa9, 0x5b, 0xb7, 0x6e,
0x5e, 0xbb, 0x79, 0xfd, 0x4b, 0xb7, 0x22, 0x6f, 0x3b, 0x7b, 0x55, 0xa5, 0x41, 0xb5, 0xf8, 0x70,
0xaf, 0x0a, 0xca, 0x46, 0x76, 0xf7, 0xab, 0x05, 0x9a, 0xf0, 0x89, 0x7c, 0xe3, 0xc4, 0xc3, 0x38,
0x18, 0x81, 0x7b, 0xfa, 0xe9, 0x26, 0xda, 0xb6, 0x28, 0x26, 0x75, 0x6b, 0xb3, 0xd6, 0xa2, 0xc6,
0xa3, 0x62, 0x32, 0x9f, 0xeb, 0x84, 0xe6, 0xa9, 0x26, 0xda, 0x5e, 0xc1, 0xa4, 0x7e, 0xa7, 0xd6,
0xe2, 0xc1, 0x65, 0x40, 0xb8, 0x25, 0xc9, 0x92, 0xf9, 0x81, 0xb2, 0x62, 0x62, 0xd0, 0xc7, 0xf6,
0x56, 0x64, 0xf0, 0xb1, 0x9c, 0x41, 0x88, 0xed, 0xad, 0xa2, 0xc1, 0x44, 0x96, 0x33, 0x98, 0x08,
0xc1, 0x1f, 0x34, 0x7d, 0xc4, 0xc7, 0xb6, 0x47, 0x08, 0xb6, 0x79, 0x78, 0xb7, 0x1c, 0xc2, 0xb0,
0xbf, 0x85, 0x5c, 0x8b, 0x1a, 0x27, 0x85, 0xed, 0xaf, 0x8a, 0xa0, 0x9e, 0xa8, 0x2c, 0xc4, 0xf0,
0x0a, 0x8f, 0x1d, 0x72, 0xc3, 0x14, 0xe8, 0x86, 0xe6, 0xb8, 0xe8, 0x5b, 0x89, 0x4a, 0xb3, 0x74,
0x63, 0x22, 0xa1, 0x74, 0xb8, 0x57, 0x3d, 0x7a, 0x63, 0x42, 0xc4, 0xf7, 0x52, 0x3f, 0x50, 0xdd,
0x0b, 0x68, 0xe8, 0xfd, 0x3e, 0x76, 0xd1, 0x0e, 0x4d, 0x63, 0x80, 0x2e, 0x62, 0xc0, 0xcb, 0x9d,
0xd0, 0x3c, 0x1d, 0x21, 0xd9, 0x46, 0xaf, 0xc4, 0x84, 0x24, 0x69, 0x71, 0x87, 0x27, 0x3b, 0x16,
0xe6, 0x1b, 0x83, 0x77, 0x8e, 0xea, 0x17, 0xe2, 0x8e, 0x52, 0x22, 0xd9, 0x20, 0x35, 0x8d, 0x53,
0x62, 0x90, 0xfe, 0xca, 0xd7, 0xf0, 0x08, 0xe4, 0x7a, 0x25, 0x17, 0x96, 0x3a, 0xa1, 0x39, 0xe2,
0xab, 0xa1, 0x34, 0xd0, 0xf6, 0xc0, 0x25, 0x96, 0x93, 0x13, 0xd2, 0x96, 0xed, 0x69, 0xaf, 0x37,
0xc4, 0x07, 0x79, 0x92, 0x0f, 0x72, 0x2f, 0x9a, 0xd0, 0x88, 0xfc, 0x2c, 0x23, 0xa0, 0xa6, 0x9f,
0xa6, 0x0c, 0xf9, 0xcc, 0xaa, 0xf9, 0x5e, 0x9b, 0x62, 0xdf, 0xe8, 0x13, 0x63, 0xfd, 0xff, 0x9d,
0xd0, 0xec, 0x13, 0xc0, 0x4c, 0x24, 0xef, 0x86, 0xe6, 0x93, 0xc2, 0x1d, 0x59, 0xd8, 0x73, 0xa4,
0x73, 0x4d, 0xc1, 0x2f, 0x35, 0xfd, 0x1c, 0x41, 0xcc, 0x62, 0x3e, 0xe2, 0xa7, 0x1a, 0x72, 0xd3,
0x89, 0xed, 0x17, 0x9d, 0xbd, 0x71, 0x10, 0x9a, 0xfa, 0xdd, 0xe9, 0xd5, 0x2c, 0xac, 0xeb, 0x04,
0xb1, 0x6c, 0x8e, 0x4d, 0xd1, 0x71, 0x26, 0x52, 0x84, 0x70, 0xb9, 0x41, 0xee, 0x4b, 0x0a, 0xd7,
0x52, 0x17, 0x70, 0x90, 0x20, 0xb6, 0x9a, 0xd0, 0x49, 0x16, 0xc4, 0x9f, 0x4a, 0x3c, 0x5d, 0x8c,
0x28, 0xb6, 0x9a, 0xc6, 0x19, 0xb1, 0x14, 0xbe, 0xc9, 0x97, 0xc2, 0xc9, 0xbb, 0xd3, 0xab, 0x8b,
0x5c, 0xcc, 0x27, 0xff, 0x0c, 0x41, 0x2c, 0xfa, 0x70, 0x48, 0xc0, 0x44, 0xf2, 0x53, 0x49, 0xc8,
0xca, 0x72, 0xe5, 0xde, 0xe8, 0xec, 0x55, 0x4b, 0xed, 0xcb, 0xa2, 0x74, 0x07, 0x65, 0x1d, 0x43,
0x20, 0xb3, 0x8f, 0x64, 0xe0, 0xef, 0x9a, 0x3e, 0x92, 0x27, 0xef, 0x63, 0x82, 0xdb, 0x62, 0x25,
0x9f, 0x15, 0xf4, 0x77, 0x39, 0xfd, 0x53, 0x77, 0xa7, 0x57, 0x61, 0x04, 0x70, 0x07, 0x06, 0x08,
0x62, 0xc9, 0x67, 0xea, 0x42, 0x35, 0x71, 0x21, 0x8f, 0x48, 0x4e, 0x5c, 0x93, 0x9d, 0x50, 0xd8,
0x50, 0x09, 0xb9, 0x23, 0xd7, 0xb8, 0x23, 0x32, 0x05, 0x38, 0x24, 0xbb, 0x92, 0x48, 0x15, 0xce,
0x30, 0xa7, 0x89, 0xbd, 0x80, 0x59, 0xd4, 0x18, 0xc8, 0x3b, 0xb3, 0x1a, 0x01, 0x2b, 0xb1, 0x33,
0xc9, 0x27, 0x5f, 0xe9, 0xf5, 0x9c, 0x33, 0x79, 0xa4, 0xd7, 0xf6, 0x53, 0xd8, 0x50, 0x09, 0xd3,
0x2d, 0x27, 0x53, 0xc8, 0x3b, 0x93, 0x48, 0xc1, 0x4f, 0x34, 0xdd, 0x08, 0x28, 0x5a, 0xc7, 0x96,
0x8f, 0xf9, 0xb9, 0xef, 0x90, 0x75, 0x0b, 0xd9, 0x36, 0x6e, 0x31, 0x5c, 0x37, 0x80, 0xf0, 0x06,
0xf1, 0x1d, 0xb0, 0x06, 0xa7, 0x63, 0x29, 0xdf, 0x01, 0x81, 0x9f, 0x7c, 0x75, 0x43, 0xf3, 0xac,
0x70, 0x22, 0x13, 0x49, 0x84, 0x65, 0xc5, 0xdc, 0x17, 0x5f, 0xf1, 0x99, 0x49, 0x38, 0x2c, 0x28,
0xc0, 0x84, 0x41, 0x22, 0x07, 0x6f, 0xe9, 0x43, 0x45, 0x72, 0x14, 0x63, 0x62, 0x0c, 0x0a, 0x62,
0x0b, 0x07, 0xa1, 0x79, 0x62, 0x0d, 0xae, 0x60, 0x4c, 0x3a, 0xa1, 0x79, 0x22, 0xf0, 0xf9, 0xaf,
0x6e, 0x68, 0xf6, 0xc5, 0x84, 0xf8, 0xa7, 0x44, 0x26, 0x51, 0x48, 0x7f, 0xed, 0xee, 0x57, 0xe3,
0xe6, 0x10, 0xe4, 0x09, 0x70, 0x19, 0xf8, 0x81, 0xa6, 0x3f, 0x5e, 0xec, 0x3d, 0x20, 0xce, 0x1b,
0x01, 0xb6, 0x9c, 0xba, 0x31, 0x24, 0x92, 0x88, 0xd7, 0xa3, 0xb1, 0x59, 0x13, 0xe2, 0x85, 0xb9,
0x68, 0x6c, 0xe2, 0x2f, 0x79, 0x6c, 0x12, 0x85, 0x4a, 0x34, 0x28, 0xc9, 0x67, 0x57, 0xfe, 0x8a,
0x07, 0x25, 0xc1, 0x8a, 0x83, 0x92, 0x68, 0x81, 0xbf, 0x68, 0xfa, 0x60, 0x89, 0x97, 0xef, 0x1a,
0xe7, 0x04, 0xa3, 0xef, 0xf0, 0xb5, 0x77, 0x7c, 0x0d, 0xae, 0xc1, 0xc5, 0x4e, 0x68, 0x1e, 0x0f,
0xfc, 0x35, 0xb8, 0xd8, 0x0d, 0xcd, 0x9b, 0x09, 0x11, 0xb8, 0x28, 0xad, 0xae, 0x0d, 0xc6, 0x5a,
0xf4, 0xd6, 0xd5, 0xab, 0x75, 0xc4, 0xd0, 0x15, 0xba, 0x43, 0x6c, 0xb6, 0xc1, 0x8b, 0x35, 0x82,
0xd9, 0x55, 0x82, 0xdb, 0x5c, 0xca, 0x09, 0xc7, 0x46, 0x92, 0x1f, 0x87, 0x7b, 0xd5, 0x87, 0x68,
0xb8, 0xbb, 0x5f, 0x8d, 0x58, 0xc0, 0x81, 0x82, 0x1f, 0xbe, 0x0b, 0xfe, 0xad, 0xe9, 0x66, 0xd1,
0x85, 0x96, 0x47, 0xf9, 0x09, 0x47, 0xb1, 0x1d, 0xf8, 0xd8, 0xdd, 0x31, 0x86, 0x45, 0xf8, 0xfd,
0x91, 0xa8, 0x20, 0xd6, 0xe0, 0xb2, 0x47, 0xd9, 0x42, 0x0a, 0x76, 0x42, 0xf3, 0x6c, 0xe0, 0xe7,
0x65, 0xdd, 0xd0, 0x7c, 0x2a, 0x76, 0x32, 0x0f, 0x48, 0xfe, 0x36, 0x90, 0x4b, 0x45, 0x48, 0x2e,
0xb7, 0x56, 0xc8, 0x78, 0xe6, 0x29, 0x5a, 0xf0, 0x7a, 0xa1, 0x48, 0x01, 0x5e, 0xcc, 0xbb, 0x95,
0x47, 0xc1, 0xbf, 0x14, 0x1e, 0x3a, 0xc4, 0x61, 0x0e, 0xaf, 0x23, 0xf8, 0x79, 0x67, 0x51, 0x63,
0x44, 0xac, 0xe2, 0x1f, 0x8a, 0xea, 0x61, 0x0d, 0x2e, 0x44, 0xe8, 0x1c, 0x07, 0x79, 0xc0, 0x38,
0x13, 0xf8, 0x39, 0x51, 0x1a, 0x2e, 0x0a, 0x72, 0x39, 0x58, 0xdc, 0x9c, 0xc8, 0x05, 0xf0, 0xa2,
0x85, 0xb2, 0x88, 0x9f, 0x40, 0xbc, 0x15, 0x2f, 0x18, 0x0a, 0x14, 0xe0, 0x85, 0xbc, 0x83, 0x39,
0x10, 0x78, 0xfa, 0x80, 0x8f, 0xa3, 0xc3, 0xd9, 0x23, 0x56, 0x1b, 0x6d, 0xe2, 0xa0, 0x65, 0x18,
0x62, 0xca, 0x66, 0x39, 0xf9, 0x18, 0xbc, 0x47, 0x5e, 0x13, 0x50, 0x4a, 0xbe, 0x20, 0xef, 0x79,
0x48, 0x17, 0x0d, 0x80, 0x6f, 0x69, 0xfa, 0x08, 0x0a, 0x98, 0x67, 0x05, 0xad, 0x75, 0x1f, 0xd5,
0x71, 0x96, 0x0c, 0x6d, 0x18, 0x8f, 0x8b, 0x81, 0x5c, 0xe6, 0x25, 0x17, 0x57, 0x59, 0x8b, 0x34,
0x92, 0x3c, 0xe2, 0x76, 0x5a, 0x9d, 0xa8, 0x40, 0x79, 0xf8, 0xa6, 0xe4, 0xcc, 0x70, 0x72, 0x0a,
0x2a, 0xad, 0x81, 0xa6, 0x3e, 0x92, 0x70, 0x60, 0x9e, 0xd5, 0xf2, 0xf9, 0x14, 0x8b, 0xb3, 0x98,
0x1a, 0xe7, 0xc5, 0x00, 0xdc, 0xe0, 0x44, 0x62, 0x95, 0x55, 0x6f, 0xd9, 0xc7, 0x30, 0xc6, 0xbb,
0xa1, 0x79, 0x3e, 0x9a, 0x42, 0x05, 0x58, 0x81, 0xca, 0x36, 0x60, 0x4b, 0x07, 0x9b, 0x18, 0xb7,
0x2c, 0x86, 0x9b, 0x2d, 0xcf, 0x47, 0xbe, 0x83, 0xa9, 0xb5, 0x61, 0x5c, 0x10, 0x2e, 0xdf, 0xe6,
0x1b, 0x81, 0xa3, 0xab, 0x19, 0xc8, 0xdd, 0xbd, 0x24, 0x7a, 0x29, 0x02, 0x72, 0x2d, 0x76, 0x5d,
0x76, 0x75, 0xea, 0x3a, 0x2c, 0x59, 0x01, 0x3b, 0xfa, 0xa0, 0x8d, 0xec, 0x0d, 0x6c, 0x39, 0xeb,
0xc4, 0xf3, 0x71, 0xdd, 0x6a, 0x38, 0x2e, 0xa6, 0xc6, 0x45, 0xe1, 0xe2, 0x02, 0x3f, 0xd1, 0x04,
0xbc, 0x10, 0xa1, 0xf3, 0x1c, 0x4c, 0x07, 0xba, 0x84, 0x94, 0xf6, 0x60, 0xba, 0xb7, 0x60, 0xd9,
0x0c, 0xf8, 0x9e, 0xa6, 0x9f, 0x6f, 0xf9, 0xde, 0x3a, 0x2f, 0x66, 0xac, 0xa0, 0x55, 0x47, 0x0c,
0xcb, 0x05, 0xc2, 0x13, 0xc2, 0xf7, 0x55, 0x9e, 0xdf, 0x26, 0x5a, 0x6b, 0x42, 0x49, 0x2e, 0x06,
0xa2, 0x22, 0xbb, 0x07, 0x2e, 0xd1, 0x79, 0x51, 0x1a, 0x08, 0xed, 0x45, 0xd8, 0xcb, 0x22, 0x78,
0x47, 0xd3, 0x87, 0x5d, 0xa7, 0xe9, 0x30, 0xab, 0x86, 0x48, 0xbd, 0xed, 0xd4, 0xd9, 0x86, 0xe5,
0x10, 0xcb, 0x45, 0xc4, 0x18, 0x15, 0x43, 0xb2, 0x24, 0x8a, 0x47, 0xae, 0x31, 0x93, 0x28, 0x2c,
0x90, 0x45, 0x44, 0xb2, 0x82, 0xbf, 0x8c, 0x7d, 0xc6, 0xb0, 0xa8, 0x4c, 0x81, 0xb7, 0x35, 0x1d,
0x34, 0x1d, 0x62, 0x6d, 0x78, 0x4d, 0x6c, 0xd5, 0x1d, 0xba, 0x69, 0x35, 0x7c, 0x8c, 0x0d, 0x73,
0x4c, 0x1b, 0x3f, 0x35, 0xd5, 0x77, 0x25, 0xba, 0x59, 0xbb, 0xb2, 0xe2, 0xbc, 0x89, 0x67, 0x5e,
0xf9, 0x30, 0x34, 0x8f, 0xf0, 0x9d, 0xd8, 0x74, 0xc8, 0x6d, 0xaf, 0x89, 0xe7, 0x1c, 0xba, 0x39,
0xef, 0x63, 0x9c, 0xae, 0x8e, 0x82, 0x5c, 0xde, 0x07, 0x63, 0x97, 0x39, 0x91, 0x63, 0x93, 0x63,
0x97, 0x61, 0xb1, 0x39, 0xb8, 0xaf, 0xe9, 0x7d, 0xc9, 0x7a, 0x17, 0xc7, 0xce, 0x98, 0x38, 0x76,
0xfe, 0x2c, 0x52, 0x9e, 0x64, 0xd1, 0x46, 0x87, 0xcf, 0x29, 0x3f, 0xfb, 0xec, 0x86, 0xe6, 0x5c,
0x52, 0x71, 0x24, 0x32, 0xc5, 0x41, 0x14, 0xef, 0x00, 0x5a, 0x38, 0x53, 0x9a, 0x98, 0xa1, 0x2b,
0x5f, 0xa6, 0x1e, 0xe1, 0xb1, 0x3b, 0x67, 0x36, 0xff, 0x79, 0xb8, 0x57, 0x1d, 0x7f, 0x58, 0x53,
0x3c, 0x3f, 0x92, 0xf8, 0xc2, 0xcc, 0x8e, 0xef, 0x82, 0xd7, 0xf4, 0x01, 0xe4, 0xb6, 0x79, 0xf5,
0x15, 0xdd, 0x26, 0x10, 0xcc, 0xa8, 0xf1, 0xa4, 0xb8, 0xc4, 0xe3, 0x45, 0xef, 0x99, 0x08, 0x14,
0x55, 0xf9, 0x5d, 0xcc, 0xf8, 0xc2, 0x1f, 0x8a, 0x22, 0x4c, 0x4e, 0x5e, 0x81, 0x45, 0x45, 0xf0,
0x5f, 0x4d, 0x1f, 0xf7, 0xb6, 0xb0, 0xdf, 0xf6, 0x1d, 0xc6, 0x03, 0x47, 0xd3, 0x63, 0xd8, 0xaa,
0xe3, 0x2d, 0xc7, 0xc6, 0x16, 0x41, 0x4d, 0x4c, 0x79, 0x38, 0x8d, 0x0b, 0x21, 0xa3, 0x92, 0x5d,
0x2f, 0x8d, 0xdc, 0x4b, 0x1a, 0x41, 0xd1, 0x66, 0x0e, 0x6f, 0xdd, 0xe5, 0xea, 0x9d, 0xd0, 0xbc,
0xe4, 0x95, 0x20, 0xc7, 0xc6, 0x02, 0xbd, 0x47, 0x66, 0x23, 0x53, 0xdd, 0xd0, 0x7c, 0x49, 0x10,
0x7c, 0x08, 0xdd, 0xde, 0x8b, 0x92, 0x57, 0x71, 0x3d, 0x78, 0xc0, 0x87, 0x61, 0x01, 0xbe, 0xa6,
0x9f, 0xe3, 0x61, 0xcc, 0x72, 0x48, 0x1d, 0x6f, 0x5b, 0x7c, 0x25, 0xd7, 0x5c, 0xcf, 0xde, 0xa4,
0xc6, 0x25, 0xb1, 0xa5, 0xf9, 0xa2, 0x01, 0x5c, 0x61, 0x81, 0xe3, 0x4b, 0x0e, 0x99, 0x11, 0x68,
0x7a, 0x6b, 0x5b, 0x86, 0x94, 0x99, 0x72, 0x94, 0xff, 0x42, 0x85, 0x25, 0xf0, 0x4f, 0x9e, 0xee,
0x12, 0x64, 0x6f, 0xe2, 0xba, 0x45, 0x3c, 0xe6, 0x34, 0x1c, 0x1b, 0x45, 0xf7, 0x0f, 0x75, 0x6a,
0x54, 0xc5, 0xfc, 0xbe, 0xcb, 0x87, 0x7b, 0x78, 0x2d, 0x52, 0xba, 0x2b, 0xe9, 0x2c, 0xcc, 0xf1,
0xd1, 0x1e, 0x0e, 0x94, 0x48, 0x37, 0x34, 0x2f, 0x44, 0xa1, 0x5d, 0x05, 0x8b, 0xbb, 0x4a, 0x25,
0xd2, 0xdd, 0xab, 0xf6, 0xb0, 0xb8, 0xbb, 0x5f, 0xed, 0xc1, 0x02, 0x2a, 0x5b, 0xd4, 0x29, 0x80,
0xfa, 0x69, 0xe6, 0xa3, 0x46, 0xc3, 0xb1, 0x2d, 0xdb, 0x45, 0x94, 0x1a, 0x97, 0xc5, 0xb0, 0xbe,
0xc0, 0xeb, 0xe5, 0x18, 0x98, 0xe5, 0xf2, 0x6e, 0x68, 0x82, 0x68, 0x40, 0x25, 0x61, 0x7a, 0x51,
0x93, 0x53, 0x05, 0x6f, 0xe9, 0x83, 0xf1, 0x10, 0x5b, 0x0d, 0xcf, 0xad, 0x63, 0xdf, 0x6a, 0x21,
0xb6, 0x61, 0x3c, 0x25, 0x76, 0xfd, 0x9d, 0x83, 0xd0, 0xbc, 0x30, 0x87, 0x5b, 0x3e, 0xb6, 0x11,
0xc3, 0xf5, 0xb9, 0x48, 0x71, 0x5e, 0xe8, 0x2d, 0x23, 0xb6, 0xd1, 0x09, 0x4d, 0xed, 0x85, 0xb4,
0x3a, 0xaf, 0x17, 0xe1, 0xe7, 0xbd, 0xa6, 0xc3, 0x27, 0x89, 0xed, 0x54, 0x0c, 0x0d, 0x0e, 0x94,
0x70, 0xb0, 0xa9, 0x9f, 0xa5, 0x98, 0x59, 0xae, 0xd7, 0xb6, 0x5a, 0xbe, 0xe3, 0xf9, 0x0e, 0xdb,
0x31, 0x9e, 0x16, 0x9b, 0x62, 0xba, 0x13, 0x9a, 0xfd, 0x14, 0xb3, 0x45, 0xaf, 0xbd, 0x1c, 0x23,
0x69, 0x64, 0xcb, 0x8b, 0x7b, 0xa6, 0x18, 0x85, 0xe6, 0xe0, 0x3d, 0x4d, 0x1f, 0x6e, 0xa2, 0xed,
0xc4, 0x4d, 0xdb, 0x23, 0x76, 0xe0, 0xfb, 0x98, 0xd8, 0x3b, 0xc6, 0xb8, 0x18, 0x47, 0x2a, 0x2e,
0x5b, 0x50, 0x7b, 0x09, 0x6d, 0x47, 0x1c, 0x67, 0x33, 0x15, 0x7e, 0xe4, 0x37, 0x15, 0xf2, 0xf4,
0xc8, 0x57, 0x81, 0xc9, 0x90, 0x8b, 0xdb, 0x11, 0xb5, 0x5d, 0xa8, 0xb4, 0x0a, 0x3e, 0xd6, 0xf4,
0x41, 0xdb, 0x47, 0x74, 0xa3, 0x50, 0x03, 0x3c, 0x23, 0xa6, 0xe5, 0x7d, 0x51, 0x03, 0xcc, 0x26,
0x35, 0x80, 0x1d, 0xd7, 0x00, 0xf3, 0xd1, 0xd9, 0xcc, 0x9b, 0x65, 0xd9, 0xb8, 0x32, 0x0c, 0x0b,
0x9d, 0x72, 0x5e, 0x2f, 0xc4, 0x7c, 0x2d, 0x0f, 0x94, 0x8c, 0xf0, 0xea, 0xc0, 0x8e, 0xab, 0x83,
0xea, 0xc3, 0x98, 0xe1, 0xf5, 0xc1, 0x6c, 0x54, 0x1f, 0x14, 0x8c, 0xf9, 0x2e, 0xf8, 0x99, 0xa6,
0x8f, 0x14, 0xdd, 0x4b, 0xae, 0x65, 0x9e, 0x15, 0xf3, 0xef, 0x1c, 0x84, 0xe6, 0xc9, 0x59, 0x28,
0xbd, 0x28, 0xe4, 0xad, 0x14, 0x5f, 0x14, 0x94, 0x68, 0xaf, 0xa5, 0xb1, 0xbb, 0x5f, 0xcd, 0x6c,
0x43, 0xb5, 0x65, 0xf0, 0x0d, 0x4d, 0x1f, 0xa6, 0x2c, 0x20, 0x16, 0xcf, 0x9c, 0x90, 0xeb, 0x6c,
0x61, 0x2b, 0xca, 0x87, 0xa9, 0xf1, 0x5c, 0x9a, 0x8f, 0x0e, 0x72, 0x8d, 0x3b, 0x89, 0xc2, 0x0a,
0xc7, 0x57, 0xd2, 0x2c, 0x49, 0x81, 0xe5, 0x93, 0x79, 0x29, 0xa0, 0x1d, 0x9b, 0xbc, 0x39, 0x01,
0x55, 0xd6, 0x78, 0x8d, 0x5c, 0xa0, 0xc1, 0xe3, 0x2a, 0x35, 0x9e, 0x17, 0x24, 0x5e, 0xe5, 0x89,
0x5a, 0xae, 0xd9, 0x92, 0x43, 0xb2, 0x5a, 0xa2, 0x84, 0xc8, 0x39, 0x62, 0x2e, 0xa0, 0x4e, 0x4d,
0xc0, 0xb2, 0x1d, 0x9e, 0x95, 0xf7, 0x89, 0xde, 0x93, 0x87, 0xae, 0x17, 0x44, 0x0c, 0xad, 0x1f,
0x84, 0x66, 0x3f, 0x44, 0xed, 0x15, 0x16, 0x48, 0x4f, 0x5c, 0xa7, 0x68, 0xf6, 0x99, 0x5e, 0x46,
0x65, 0xb2, 0x07, 0x3e, 0xc3, 0x15, 0x2c, 0x42, 0xd9, 0x1e, 0xd8, 0xd2, 0xcf, 0xf0, 0xb2, 0xb3,
0x86, 0x28, 0xb6, 0xa2, 0x37, 0x47, 0xe3, 0xca, 0x98, 0x36, 0xde, 0x3f, 0xd5, 0x9f, 0xa4, 0x45,
0xab, 0x42, 0x2a, 0x6e, 0x0f, 0xfb, 0x13, 0xd5, 0x48, 0x96, 0x46, 0x8e, 0xbc, 0xb8, 0x32, 0x16,
0x17, 0x21, 0xf1, 0xf2, 0x78, 0x7b, 0xbf, 0xaa, 0xc1, 0x42, 0x53, 0xf0, 0xfd, 0xa3, 0xfa, 0x25,
0x1e, 0x35, 0xd2, 0x70, 0xc1, 0x8b, 0x58, 0xdb, 0x6b, 0xf2, 0x25, 0xeb, 0xe3, 0x37, 0x02, 0x4c,
0x99, 0xb5, 0xe9, 0xd4, 0x8c, 0xab, 0x62, 0x3a, 0xfe, 0xa6, 0xc5, 0x6f, 0x95, 0x4b, 0x68, 0x7b,
0x76, 0x01, 0x46, 0xf8, 0x1d, 0x67, 0xa6, 0x13, 0x9a, 0x66, 0x13, 0x6d, 0xa7, 0x5b, 0x9c, 0x2d,
0xc4, 0x36, 0x32, 0x95, 0xf4, 0x14, 0x7c, 0x80, 0x9e, 0x54, 0x00, 0x3e, 0xd0, 0xe4, 0x83, 0x55,
0xe2, 0xd7, 0xcf, 0x02, 0x5d, 0xf8, 0x80, 0x66, 0x35, 0xf0, 0xa9, 0xa6, 0x0f, 0xa7, 0x4f, 0x30,
0x2e, 0x92, 0x1f, 0x6d, 0x27, 0xc4, 0x06, 0xfe, 0x80, 0x8f, 0xc4, 0x50, 0xf2, 0x84, 0xb1, 0x38,
0x7d, 0x57, 0x7e, 0xb7, 0x1d, 0x42, 0x0a, 0x79, 0x9a, 0x48, 0xab, 0x40, 0xd5, 0xcb, 0x99, 0xd2,
0x48, 0x0f, 0xb9, 0xb4, 0xf5, 0x95, 0xa4, 0x60, 0xd6, 0x0a, 0x49, 0x8f, 0xbe, 0x5b, 0xfa, 0x79,
0xf1, 0xca, 0xd2, 0x08, 0x5c, 0x37, 0xce, 0x6a, 0x3c, 0x92, 0x94, 0xa8, 0xc6, 0xa4, 0xf0, 0xf4,
0x16, 0xcf, 0x1a, 0xb8, 0xd6, 0x7c, 0xe0, 0xba, 0x22, 0x1f, 0xb9, 0x47, 0xe2, 0xa2, 0xb2, 0x1b,
0x9a, 0x17, 0xe3, 0x23, 0x4b, 0x05, 0x57, 0x60, 0x8f, 0x76, 0xe0, 0x55, 0xfd, 0x74, 0x03, 0x23,
0x16, 0xf8, 0xd8, 0x6a, 0xb8, 0x68, 0x9d, 0x1a, 0x53, 0x62, 0xdf, 0x5d, 0xe6, 0x27, 0x7d, 0x0c,
0xcc, 0x73, 0x79, 0xfa, 0x22, 0x23, 0x09, 0x2b, 0x30, 0xa7, 0x02, 0xda, 0xfa, 0x88, 0xf4, 0x10,
0x13, 0xd5, 0x38, 0x98, 0x78, 0xc1, 0xfa, 0x86, 0x71, 0x4d, 0x2c, 0xda, 0x97, 0x45, 0x78, 0x4d,
0x55, 0x16, 0xb9, 0xc6, 0x2b, 0x42, 0x21, 0xcd, 0x7a, 0x94, 0x68, 0x9a, 0x51, 0xa8, 0x1b, 0x83,
0x4d, 0x7d, 0xa8, 0xd4, 0x71, 0x13, 0x6d, 0x1b, 0xd7, 0x45, 0xaf, 0x2f, 0xf1, 0x64, 0xb0, 0xd0,
0x70, 0x09, 0x6d, 0x77, 0x43, 0xd3, 0x50, 0x75, 0xb9, 0x84, 0xb6, 0xd3, 0xfe, 0x14, 0xcd, 0xc0,
0x57, 0xf4, 0xbe, 0xa0, 0x45, 0x5a, 0xe9, 0x31, 0xf2, 0xab, 0x79, 0x31, 0x39, 0x5f, 0x38, 0x08,
0xcd, 0x73, 0x59, 0x06, 0xb3, 0xb6, 0x4c, 0x96, 0xb3, 0x33, 0x45, 0xe4, 0x2e, 0x71, 0x5a, 0xd7,
0x22, 0xad, 0x18, 0x90, 0xb2, 0x96, 0xdd, 0xfd, 0xaa, 0xba, 0xb1, 0xa1, 0xc1, 0x53, 0x52, 0x13,
0xf0, 0x0b, 0x2d, 0xee, 0x3e, 0xb9, 0xb4, 0x7f, 0x6f, 0x5e, 0x38, 0xf9, 0xb6, 0xd8, 0x05, 0x79,
0x13, 0xe9, 0x05, 0xbe, 0xe8, 0x7e, 0x2c, 0xed, 0x5e, 0xbe, 0x78, 0x97, 0x38, 0x64, 0xdb, 0xfd,
0x7c, 0x6f, 0x2d, 0xbe, 0xac, 0x55, 0xbd, 0x18, 0x1a, 0xd4, 0xb3, 0x56, 0xe0, 0x77, 0x9a, 0xde,
0x2f, 0x68, 0x66, 0xd7, 0xf3, 0xbf, 0x8e, 0x88, 0x7e, 0x5b, 0x64, 0xc5, 0x79, 0x13, 0xd2, 0x55,
0xbd, 0xa0, 0x5a, 0x49, 0xa9, 0xe6, 0x2f, 0xd7, 0x95, 0x64, 0x2f, 0x7e, 0x96, 0x1e, 0xcf, 0x7d,
0xd5, 0x7d, 0x19, 0x1a, 0xec, 0x93, 0x5b, 0x66, 0x94, 0xb3, 0x4b, 0xf8, 0xf7, 0x7b, 0x53, 0x96,
0x2e, 0xe4, 0x0b, 0x94, 0xf3, 0x57, 0xe8, 0xbd, 0x29, 0xf7, 0xd2, 0x2b, 0x53, 0x4e, 0x34, 0x13,
0xca, 0xe9, 0x9d, 0x7b, 0x43, 0x8f, 0x1e, 0xfb, 0xd2, 0x43, 0xf3, 0x37, 0xf3, 0x62, 0xf7, 0x7e,
0x2e, 0xcf, 0x57, 0xbc, 0x97, 0x65, 0xa7, 0xa7, 0xb4, 0x18, 0xfd, 0x0c, 0xc9, 0xa7, 0xd0, 0x7d,
0x12, 0x42, 0xc5, 0x95, 0x45, 0xf9, 0xb6, 0xc0, 0x6a, 0xd9, 0xcc, 0xf8, 0x80, 0x0f, 0x91, 0x36,
0xb3, 0x74, 0x10, 0x9a, 0x17, 0xb3, 0x1e, 0x97, 0xf2, 0xb5, 0xfe, 0xb2, 0xcd, 0xf2, 0xe3, 0xd4,
0x2c, 0xe1, 0xf9, 0xee, 0x41, 0x59, 0x81, 0x67, 0x08, 0x43, 0x85, 0xf3, 0x91, 0xda, 0x88, 0x50,
0xe3, 0xb7, 0xd1, 0x2c, 0xad, 0x16, 0x28, 0xc8, 0xe7, 0xca, 0x0a, 0x57, 0x2c, 0x50, 0x28, 0xe1,
0xe5, 0xa9, 0x12, 0x4c, 0x4a, 0x7a, 0x33, 0x77, 0x3e, 0xfc, 0x64, 0xf4, 0xc8, 0xfe, 0x27, 0xa3,
0x47, 0x3e, 0x3c, 0x18, 0xd5, 0xf6, 0x0f, 0x46, 0xb5, 0xef, 0xde, 0x1f, 0x3d, 0xf2, 0xee, 0xfd,
0x51, 0x6d, 0xff, 0xfe, 0xe8, 0x91, 0x8f, 0xee, 0x8f, 0x1e, 0x79, 0xfd, 0x99, 0x75, 0x87, 0x6d,
0x04, 0xb5, 0x2b, 0xb6, 0xd7, 0xbc, 0x9a, 0x66, 0xad, 0xd2, 0xaf, 0xec, 0xdf, 0x4b, 0xb5, 0x13,
0xe2, 0xef, 0x4a, 0xd7, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x30, 0xd1, 0x0d, 0x1a, 0x25,
0x00, 0x00,
0xd9, 0xce, 0x26, 0x4d, 0xda, 0x6c, 0x1c, 0x27, 0x1e, 0x3b, 0xf6, 0x36, 0x49, 0xbd, 0xee, 0xc9,
0x49, 0xeb, 0xfe, 0x25, 0xb6, 0x93, 0xe6, 0x4b, 0x23, 0x7d, 0xea, 0xe7, 0x9f, 0xfa, 0xab, 0x1b,
0x3b, 0xb1, 0xc6, 0xf6, 0xd7, 0x4f, 0x45, 0x68, 0x35, 0xde, 0x9d, 0x63, 0x2f, 0xde, 0x33, 0x7b,
0xba, 0x33, 0xeb, 0x9f, 0x16, 0x41, 0x55, 0x04, 0xe5, 0x0e, 0xb0, 0xf8, 0x91, 0x40, 0x42, 0x45,
0x80, 0x44, 0x29, 0x45, 0x48, 0x48, 0x48, 0x70, 0x43, 0x85, 0x84, 0x54, 0xc1, 0x85, 0x7d, 0x89,
0x44, 0x59, 0x54, 0xa7, 0x57, 0xe7, 0x82, 0x8b, 0x73, 0x69, 0x6e, 0xd0, 0xcc, 0xfe, 0xcd, 0xee,
0xce, 0x49, 0x72, 0x77, 0xf6, 0x7d, 0xde, 0x79, 0xe7, 0x79, 0xe7, 0xe7, 0x9d, 0xf7, 0x9d, 0x39,
0xfa, 0x65, 0xcf, 0x5d, 0xbd, 0x6a, 0xfb, 0xa4, 0xe1, 0xae, 0x5d, 0xf5, 0x5b, 0xcc, 0xf5, 0x09,
0x8d, 0xbf, 0xc2, 0x00, 0xf1, 0xaf, 0x2b, 0xad, 0xc0, 0x67, 0x3e, 0x38, 0x11, 0x0b, 0xcf, 0x0f,
0x49, 0xea, 0x2c, 0x24, 0x2e, 0x59, 0x8b, 0x15, 0xce, 0x9f, 0x93, 0x00, 0xea, 0xbe, 0x85, 0x13,
0xf1, 0x49, 0xbc, 0xcd, 0xe2, 0x9f, 0xb5, 0x83, 0xd7, 0xf4, 0x81, 0xbb, 0x71, 0x0f, 0xd3, 0x72,
0x0f, 0xe0, 0xc7, 0x9a, 0x7e, 0xd6, 0x73, 0x29, 0xc3, 0xc4, 0x42, 0x8e, 0x13, 0x60, 0x4a, 0x31,
0x35, 0xb4, 0x91, 0x63, 0xa3, 0x27, 0xa7, 0xe8, 0x41, 0x64, 0x02, 0x88, 0xb6, 0xe6, 0x05, 0x3c,
0x99, 0xa2, 0xed, 0xc8, 0x3c, 0xe3, 0x15, 0x45, 0x9d, 0xc8, 0xbc, 0xbc, 0xdd, 0xf4, 0x6e, 0xd5,
0x0a, 0xf2, 0xda, 0x88, 0x83, 0x1b, 0x28, 0xf4, 0xd8, 0xad, 0x5a, 0xf2, 0xa3, 0x76, 0xb8, 0x57,
0x7f, 0x34, 0xf9, 0xbd, 0xbb, 0x5f, 0x57, 0x18, 0x87, 0x65, 0xd3, 0xe0, 0x5f, 0x9a, 0x6e, 0xac,
0x79, 0xfe, 0x2a, 0xf2, 0x2c, 0xc7, 0xa5, 0xb6, 0xbf, 0x89, 0x83, 0x1d, 0x8b, 0xe2, 0x60, 0x13,
0x07, 0xd4, 0x38, 0x2a, 0x88, 0xfe, 0x56, 0x3b, 0x88, 0xcc, 0x7e, 0x88, 0xb6, 0xfe, 0x57, 0xe8,
0x4d, 0x12, 0xb2, 0x14, 0xe3, 0xed, 0xc8, 0x3c, 0xb7, 0x96, 0xca, 0xfc, 0x90, 0xd8, 0x38, 0x01,
0x3a, 0x91, 0xf9, 0xbc, 0x20, 0xac, 0x42, 0x15, 0xbc, 0xdb, 0x7b, 0xf5, 0x01, 0x95, 0x6a, 0x67,
0xaf, 0xae, 0xee, 0xa0, 0xe8, 0xa8, 0x8a, 0x1b, 0x1c, 0x8c, 0x1b, 0xce, 0xa4, 0x4e, 0x25, 0x72,
0xf0, 0xb9, 0xca, 0x61, 0x4c, 0xd0, 0xaa, 0x87, 0x1d, 0xe3, 0xd8, 0x88, 0x36, 0xfa, 0xd8, 0xd4,
0x07, 0xdc, 0xe1, 0xb3, 0x99, 0xc5, 0x57, 0x62, 0xb0, 0xea, 0x6d, 0x02, 0x74, 0x22, 0xf3, 0x59,
0x85, 0xb7, 0x09, 0x2a, 0xb9, 0xcb, 0x82, 0x10, 0x73, 0x5f, 0xbb, 0x98, 0xe9, 0x06, 0x1c, 0xee,
0xd5, 0x1f, 0xe1, 0x4d, 0x77, 0xf7, 0xeb, 0x15, 0x52, 0x15, 0x37, 0x13, 0x39, 0xf8, 0x54, 0xd3,
0x87, 0x3c, 0xdf, 0x56, 0x7a, 0xf9, 0x88, 0xf0, 0xf2, 0xa7, 0xdc, 0xcb, 0x33, 0xf3, 0x5c, 0xa7,
0xe0, 0xe4, 0x80, 0x97, 0x88, 0x4a, 0x3e, 0x3e, 0x13, 0x2f, 0x41, 0x05, 0xa8, 0x70, 0x51, 0x6d,
0xa4, 0x8b, 0x5c, 0x72, 0xb0, 0xcc, 0x07, 0x9e, 0x13, 0x0d, 0x2a, 0xee, 0xfd, 0x55, 0xd3, 0xfb,
0x63, 0xf7, 0x50, 0x62, 0xcb, 0x6a, 0xf9, 0x01, 0x33, 0x8e, 0x8f, 0x68, 0xa3, 0xc7, 0xa7, 0x7e,
0xc8, 0x5d, 0xeb, 0x49, 0x4d, 0x2d, 0xfa, 0x01, 0x6b, 0x47, 0x66, 0x5f, 0xa1, 0x6b, 0x2e, 0xec,
0x44, 0xe6, 0xd3, 0x55, 0xa7, 0x38, 0x22, 0x79, 0x34, 0x31, 0x3e, 0x36, 0xf1, 0x5f, 0xb5, 0xc3,
0xc8, 0x3c, 0xe6, 0x12, 0xd6, 0xde, 0xab, 0x2b, 0xcc, 0xa8, 0x84, 0x87, 0x7b, 0xf5, 0xe3, 0xa2,
0xe9, 0xee, 0x7e, 0xbd, 0xc0, 0x04, 0x56, 0x75, 0xc1, 0xd7, 0x8e, 0xea, 0x23, 0x25, 0x6f, 0x9a,
0xa1, 0xc7, 0x5c, 0x1b, 0x51, 0x96, 0xc6, 0x0d, 0xe3, 0xc4, 0x88, 0x36, 0x7a, 0x72, 0xea, 0xf7,
0xdc, 0xb5, 0xde, 0xd4, 0xe0, 0xc2, 0x34, 0xdf, 0xc9, 0xed, 0xc8, 0xec, 0x2f, 0x18, 0x8d, 0xc5,
0x9d, 0xc8, 0xbc, 0x51, 0x75, 0x2f, 0xc6, 0x24, 0x07, 0xbf, 0xd0, 0x68, 0x8c, 0x4f, 0xdc, 0xba,
0x75, 0xf3, 0xda, 0xcd, 0xeb, 0x5f, 0xbc, 0x15, 0x7b, 0xdb, 0xde, 0xab, 0x2b, 0x0d, 0xaa, 0xc5,
0x87, 0x7b, 0x75, 0x50, 0x35, 0xb2, 0xbb, 0x5f, 0x2f, 0xd1, 0x84, 0x4f, 0x14, 0x1b, 0xa7, 0x1e,
0x26, 0xc1, 0x08, 0xdc, 0xd5, 0x4f, 0x37, 0xd1, 0xb6, 0x45, 0x31, 0x71, 0xac, 0x8d, 0xd5, 0x16,
0x35, 0x1e, 0x15, 0x93, 0xf9, 0x5c, 0x3b, 0x32, 0x4f, 0x35, 0xd1, 0xf6, 0x12, 0x26, 0xce, 0xed,
0xd5, 0x16, 0x0f, 0x2e, 0x7d, 0xc2, 0x2d, 0x49, 0x96, 0xce, 0x0f, 0x94, 0x15, 0x53, 0x83, 0x01,
0xb6, 0x37, 0x63, 0x83, 0x8f, 0x15, 0x0c, 0x42, 0x6c, 0x6f, 0x96, 0x0d, 0xa6, 0xb2, 0x82, 0xc1,
0x54, 0x08, 0x7e, 0xa7, 0xe9, 0x43, 0x01, 0xb6, 0x7d, 0x42, 0xb0, 0xcd, 0xc3, 0xbb, 0xe5, 0x12,
0x86, 0x83, 0x4d, 0xe4, 0x59, 0xd4, 0x38, 0x29, 0x6c, 0x7f, 0x45, 0x04, 0xf5, 0x54, 0x65, 0x2e,
0x81, 0x97, 0x78, 0xec, 0x90, 0x1b, 0x66, 0x40, 0x27, 0x32, 0x47, 0x45, 0xdf, 0x4a, 0x54, 0x9a,
0xa5, 0x1b, 0x63, 0x29, 0xa5, 0xc3, 0xbd, 0xfa, 0xd1, 0x1b, 0x63, 0x22, 0xbe, 0x57, 0xfa, 0x81,
0xea, 0x5e, 0x40, 0x43, 0xef, 0x0d, 0xb0, 0x87, 0x76, 0x68, 0x16, 0x03, 0x74, 0x11, 0x03, 0x5e,
0x6e, 0x47, 0xe6, 0xe9, 0x18, 0xc9, 0x37, 0x7a, 0x2d, 0x21, 0x24, 0x49, 0xcb, 0x3b, 0x3c, 0xdd,
0xb1, 0xb0, 0xd8, 0x18, 0xbc, 0x7b, 0x54, 0xbf, 0x90, 0x74, 0x94, 0x11, 0xc9, 0x07, 0xa9, 0x69,
0x9c, 0x12, 0x83, 0xf4, 0x27, 0xbe, 0x86, 0x87, 0x20, 0xd7, 0xab, 0xb8, 0xb0, 0xd0, 0x8e, 0xcc,
0xa1, 0x40, 0x0d, 0x65, 0x81, 0xb6, 0x0b, 0x2e, 0xb1, 0x1c, 0x1f, 0x93, 0xb6, 0x6c, 0x57, 0x7b,
0xdd, 0x21, 0x3e, 0xc8, 0xe3, 0x7c, 0x90, 0xbb, 0xd1, 0x84, 0x46, 0xec, 0x67, 0x15, 0x01, 0xab,
0xfa, 0x69, 0xca, 0x50, 0xc0, 0xac, 0xd5, 0xc0, 0xdf, 0xa2, 0x38, 0x30, 0x7a, 0xc4, 0x58, 0xff,
0x77, 0x3b, 0x32, 0x7b, 0x04, 0x30, 0x15, 0xcb, 0x3b, 0x91, 0xf9, 0xa4, 0x70, 0x47, 0x16, 0x76,
0x1d, 0xe9, 0x42, 0x53, 0xf0, 0x73, 0x4d, 0x3f, 0x47, 0x10, 0xb3, 0x58, 0x80, 0xf8, 0xa9, 0x86,
0xbc, 0x6c, 0x62, 0x7b, 0x45, 0x67, 0x6f, 0x1e, 0x44, 0xa6, 0x7e, 0x67, 0x72, 0x39, 0x0f, 0xeb,
0x3a, 0x41, 0x2c, 0x9f, 0x63, 0x53, 0x74, 0x9c, 0x8b, 0x14, 0x21, 0x5c, 0x6e, 0x50, 0xf8, 0x92,
0xc2, 0xb5, 0xd4, 0x05, 0xec, 0x27, 0x88, 0x2d, 0xa7, 0x74, 0xd2, 0x05, 0xf1, 0x87, 0x0a, 0x4f,
0x0f, 0x23, 0x8a, 0xad, 0xa6, 0x71, 0x46, 0x2c, 0x85, 0x6f, 0xf0, 0xa5, 0x70, 0xf2, 0xce, 0xe4,
0xf2, 0x3c, 0x17, 0xf3, 0xc9, 0x3f, 0x43, 0x10, 0x8b, 0x3f, 0x5c, 0x12, 0x32, 0x91, 0xfc, 0xd4,
0x52, 0xb2, 0xb2, 0x5c, 0xb9, 0x37, 0xda, 0x7b, 0xf5, 0x4a, 0xfb, 0xaa, 0x28, 0xdb, 0x41, 0x79,
0xc7, 0x10, 0xc8, 0xec, 0x63, 0x19, 0xf8, 0x8b, 0xa6, 0x0f, 0x15, 0xc9, 0x07, 0x98, 0xe0, 0x2d,
0xb1, 0x92, 0xcf, 0x0a, 0xfa, 0xbb, 0x9c, 0xfe, 0xa9, 0x3b, 0x93, 0xcb, 0x30, 0x06, 0xb8, 0x03,
0x7d, 0x04, 0xb1, 0xf4, 0x33, 0x73, 0xa1, 0x9e, 0xba, 0x50, 0x44, 0x24, 0x27, 0xae, 0xc9, 0x4e,
0x28, 0x6c, 0xa8, 0x84, 0xdc, 0x91, 0x6b, 0xdc, 0x11, 0x99, 0x02, 0x1c, 0x90, 0x5d, 0x49, 0xa5,
0x0a, 0x67, 0x98, 0xdb, 0xc4, 0x7e, 0xc8, 0x2c, 0x6a, 0xf4, 0x15, 0x9d, 0x59, 0x8e, 0x81, 0xa5,
0xc4, 0x99, 0xf4, 0x93, 0xaf, 0x74, 0xa7, 0xe0, 0x4c, 0x11, 0xe9, 0xb6, 0xfd, 0x14, 0x36, 0x54,
0xc2, 0x6c, 0xcb, 0xc9, 0x14, 0x8a, 0xce, 0xa4, 0x52, 0xf0, 0x23, 0x4d, 0x37, 0x42, 0x8a, 0xd6,
0xb0, 0x15, 0x60, 0x7e, 0xee, 0xbb, 0x64, 0xcd, 0x42, 0xb6, 0x8d, 0x5b, 0x0c, 0x3b, 0x06, 0x10,
0xde, 0x20, 0xbe, 0x03, 0x56, 0xe0, 0x64, 0x22, 0xe5, 0x3b, 0x20, 0x0c, 0xd2, 0xaf, 0x4e, 0x64,
0x9e, 0x15, 0x4e, 0xe4, 0x22, 0x89, 0xb0, 0xac, 0x58, 0xf8, 0xe2, 0x2b, 0x3e, 0x37, 0x09, 0x07,
0x05, 0x05, 0x98, 0x32, 0x48, 0xe5, 0xe0, 0x6d, 0x7d, 0xa0, 0x4c, 0x8e, 0x62, 0x4c, 0x8c, 0x7e,
0x41, 0x6c, 0xee, 0x20, 0x32, 0x4f, 0xac, 0xc0, 0x25, 0x8c, 0x49, 0x3b, 0x32, 0x4f, 0x84, 0x01,
0xff, 0xd5, 0x89, 0xcc, 0x9e, 0x84, 0x10, 0xff, 0x94, 0xc8, 0xa4, 0x0a, 0xd9, 0xaf, 0xdd, 0xfd,
0x7a, 0xd2, 0x1c, 0x82, 0x22, 0x01, 0x2e, 0x03, 0xdf, 0xd3, 0xf4, 0xc7, 0xcb, 0xbd, 0x87, 0xc4,
0x7d, 0x33, 0xc4, 0x96, 0xeb, 0x18, 0x03, 0x22, 0x89, 0x78, 0x23, 0x1e, 0x9b, 0x15, 0x21, 0x9e,
0x9b, 0x89, 0xc7, 0x26, 0xf9, 0x92, 0xc7, 0x26, 0x55, 0xa8, 0xc5, 0x83, 0x92, 0x7e, 0x76, 0xe4,
0xaf, 0x64, 0x50, 0x52, 0xac, 0x3c, 0x28, 0xa9, 0x16, 0xf8, 0x58, 0xd3, 0xfb, 0x2b, 0xbc, 0x02,
0xcf, 0x38, 0x27, 0x18, 0x7d, 0x8b, 0xaf, 0xbd, 0xe3, 0x2b, 0x70, 0x05, 0xce, 0xb7, 0x23, 0xf3,
0x78, 0x18, 0xac, 0xc0, 0xf9, 0x4e, 0x64, 0xde, 0x4c, 0x89, 0xc0, 0x79, 0x69, 0x75, 0xad, 0x33,
0xd6, 0xa2, 0xb7, 0xae, 0x5e, 0x75, 0x10, 0x43, 0x57, 0xe8, 0x0e, 0xb1, 0xd9, 0x3a, 0x2f, 0xd6,
0x08, 0x66, 0x57, 0x09, 0xde, 0xe2, 0x52, 0x4e, 0x38, 0x31, 0x92, 0xfe, 0x38, 0xdc, 0xab, 0x3f,
0x44, 0xc3, 0xdd, 0xfd, 0x7a, 0xcc, 0x02, 0xf6, 0x95, 0xfc, 0x08, 0x3c, 0xf0, 0x4f, 0x4d, 0x37,
0xcb, 0x2e, 0xb4, 0x7c, 0xca, 0x4f, 0x38, 0x8a, 0xed, 0x30, 0xc0, 0xde, 0x8e, 0x31, 0x28, 0xc2,
0xef, 0x0f, 0x44, 0x05, 0xb1, 0x02, 0x17, 0x7d, 0xca, 0xe6, 0x32, 0xb0, 0x1d, 0x99, 0x67, 0xc3,
0xa0, 0x28, 0xeb, 0x44, 0xe6, 0x53, 0x89, 0x93, 0x45, 0x40, 0xf2, 0xb7, 0x81, 0x3c, 0x2a, 0x42,
0x72, 0xb5, 0xb5, 0x42, 0xc6, 0x33, 0x4f, 0xd1, 0x82, 0xd7, 0x0b, 0x65, 0x0a, 0xf0, 0x62, 0xd1,
0xad, 0x22, 0x0a, 0xfe, 0xa1, 0xf0, 0xd0, 0x25, 0x2e, 0x73, 0x79, 0x1d, 0xc1, 0xcf, 0x3b, 0x8b,
0x1a, 0x43, 0x62, 0x15, 0x7f, 0x5f, 0x54, 0x0f, 0x2b, 0x70, 0x2e, 0x46, 0x67, 0x38, 0xc8, 0x03,
0xc6, 0x99, 0x30, 0x28, 0x88, 0xb2, 0x70, 0x51, 0x92, 0xcb, 0xc1, 0xe2, 0xe6, 0x58, 0x21, 0x80,
0x97, 0x2d, 0x54, 0x45, 0xfc, 0x04, 0xe2, 0xad, 0x78, 0xc1, 0x50, 0xa2, 0x00, 0x2f, 0x14, 0x1d,
0x2c, 0x80, 0xc0, 0xd7, 0xfb, 0x02, 0x1c, 0x1f, 0xce, 0x3e, 0xb1, 0xb6, 0xd0, 0x06, 0x0e, 0x5b,
0x86, 0x21, 0xa6, 0x6c, 0x9a, 0x93, 0x4f, 0xc0, 0xbb, 0xe4, 0x75, 0x01, 0x65, 0xe4, 0x4b, 0xf2,
0xae, 0x87, 0x74, 0xd9, 0x00, 0x78, 0x4f, 0xd3, 0x87, 0x50, 0xc8, 0x7c, 0x2b, 0x6c, 0xad, 0x05,
0xc8, 0xc1, 0x79, 0x32, 0xb4, 0x6e, 0x3c, 0x2e, 0x06, 0x72, 0x91, 0x97, 0x5c, 0x5c, 0x65, 0x25,
0xd6, 0x48, 0xf3, 0x88, 0x57, 0xb3, 0xea, 0x44, 0x05, 0xca, 0xc3, 0x37, 0x21, 0x67, 0x86, 0xe3,
0x13, 0x50, 0x69, 0x0d, 0x34, 0xf5, 0xa1, 0x94, 0x03, 0xf3, 0xad, 0x56, 0xc0, 0xa7, 0x58, 0x9c,
0xc5, 0xd4, 0x38, 0x2f, 0x06, 0xe0, 0x06, 0x27, 0x92, 0xa8, 0x2c, 0xfb, 0x8b, 0x01, 0x86, 0x09,
0xde, 0x89, 0xcc, 0xf3, 0xf1, 0x14, 0x2a, 0xc0, 0x1a, 0x54, 0xb6, 0x01, 0x9b, 0x3a, 0xd8, 0xc0,
0xb8, 0x65, 0x31, 0xdc, 0x6c, 0xf9, 0x01, 0x0a, 0x5c, 0x4c, 0xad, 0x75, 0xe3, 0x82, 0x70, 0xf9,
0x55, 0xbe, 0x11, 0x38, 0xba, 0x9c, 0x83, 0xdc, 0xdd, 0x4b, 0xa2, 0x97, 0x32, 0x20, 0xd7, 0x62,
0xd7, 0x65, 0x57, 0x27, 0xae, 0xc3, 0x8a, 0x15, 0xb0, 0xa3, 0xf7, 0xdb, 0xc8, 0x5e, 0xc7, 0x96,
0xbb, 0x46, 0xfc, 0x00, 0x3b, 0x56, 0xc3, 0xf5, 0x30, 0x35, 0x2e, 0x0a, 0x17, 0xe7, 0xf8, 0x89,
0x26, 0xe0, 0xb9, 0x18, 0x9d, 0xe5, 0x60, 0x36, 0xd0, 0x15, 0xa4, 0xb2, 0x07, 0xb3, 0xbd, 0x05,
0xab, 0x66, 0xc0, 0x77, 0x34, 0xfd, 0x7c, 0x2b, 0xf0, 0xd7, 0x78, 0x31, 0x63, 0x85, 0x2d, 0x07,
0x31, 0x2c, 0x17, 0x08, 0x4f, 0x08, 0xdf, 0x97, 0x79, 0x7e, 0x9b, 0x6a, 0xad, 0x08, 0x25, 0xb9,
0x18, 0x88, 0x8b, 0xec, 0x2e, 0xb8, 0x44, 0xe7, 0x45, 0x69, 0x20, 0xb4, 0x17, 0x61, 0x37, 0x8b,
0xe0, 0x5d, 0x4d, 0x1f, 0xf4, 0xdc, 0xa6, 0xcb, 0xac, 0x55, 0x44, 0x9c, 0x2d, 0xd7, 0x61, 0xeb,
0x96, 0x4b, 0x2c, 0x0f, 0x11, 0x63, 0x58, 0x0c, 0xc9, 0x82, 0x28, 0x1e, 0xb9, 0xc6, 0x54, 0xaa,
0x30, 0x47, 0xe6, 0x11, 0xc9, 0x0b, 0xfe, 0x2a, 0x76, 0x9f, 0x61, 0x51, 0x99, 0x02, 0xef, 0x68,
0x3a, 0x68, 0xba, 0xc4, 0x5a, 0xf7, 0x9b, 0xd8, 0x72, 0x5c, 0xba, 0x61, 0x35, 0x02, 0x8c, 0x0d,
0x73, 0x44, 0x1b, 0x3d, 0x35, 0xd1, 0x73, 0x25, 0xbe, 0x59, 0xbb, 0xb2, 0xe4, 0xbe, 0x85, 0xa7,
0x5e, 0xf9, 0x24, 0x32, 0x8f, 0xf0, 0x9d, 0xd8, 0x74, 0xc9, 0xab, 0x7e, 0x13, 0xcf, 0xb8, 0x74,
0x63, 0x36, 0xc0, 0x38, 0x5b, 0x1d, 0x25, 0xb9, 0xbc, 0x0f, 0x46, 0x2e, 0x73, 0x22, 0xc7, 0xc6,
0x47, 0x2e, 0xc3, 0x72, 0x73, 0x70, 0x4f, 0xd3, 0x7b, 0xd2, 0xf5, 0x2e, 0x8e, 0x9d, 0x11, 0x71,
0xec, 0xfc, 0x51, 0xa4, 0x3c, 0xe9, 0xa2, 0x8d, 0x0f, 0x9f, 0x53, 0x41, 0xfe, 0xd9, 0x89, 0xcc,
0x99, 0xb4, 0xe2, 0x48, 0x65, 0x8a, 0x83, 0x28, 0xd9, 0x01, 0xb4, 0x74, 0xa6, 0x34, 0x31, 0x43,
0x57, 0xbe, 0x44, 0x7d, 0xc2, 0x63, 0x77, 0xc1, 0x6c, 0xf1, 0xf3, 0x70, 0xaf, 0x3e, 0xfa, 0xb0,
0xa6, 0x78, 0x7e, 0x24, 0xf1, 0x85, 0xb9, 0x9d, 0xc0, 0x03, 0xaf, 0xeb, 0x7d, 0xc8, 0xdb, 0xe2,
0xd5, 0x57, 0x7c, 0x9b, 0x40, 0x30, 0xa3, 0xc6, 0x93, 0xe2, 0x12, 0x8f, 0x17, 0xbd, 0x67, 0x62,
0x50, 0x54, 0xe5, 0x77, 0x30, 0xe3, 0x0b, 0x7f, 0x20, 0x8e, 0x30, 0x05, 0x79, 0x0d, 0x96, 0x15,
0xc1, 0xbf, 0x35, 0x7d, 0xd4, 0xdf, 0xc4, 0xc1, 0x56, 0xe0, 0x32, 0x1e, 0x38, 0x9a, 0x3e, 0xc3,
0x96, 0x83, 0x37, 0x5d, 0x1b, 0x5b, 0x04, 0x35, 0x31, 0xe5, 0xe1, 0x34, 0x29, 0x84, 0x8c, 0x5a,
0x7e, 0xbd, 0x34, 0x74, 0x37, 0x6d, 0x04, 0x45, 0x9b, 0x19, 0xbc, 0x79, 0x87, 0xab, 0xb7, 0x23,
0xf3, 0x92, 0x5f, 0x81, 0x5c, 0x1b, 0x0b, 0xf4, 0x2e, 0x99, 0x8e, 0x4d, 0x75, 0x22, 0xf3, 0x25,
0x41, 0xf0, 0x21, 0x74, 0xbb, 0x2f, 0x4a, 0x5e, 0xc5, 0x75, 0xe1, 0x01, 0x1f, 0x86, 0x05, 0xf8,
0xaa, 0x7e, 0x8e, 0x87, 0x31, 0xcb, 0x25, 0x0e, 0xde, 0xb6, 0xf8, 0x4a, 0x5e, 0xf5, 0x7c, 0x7b,
0x83, 0x1a, 0x97, 0xc4, 0x96, 0xe6, 0x8b, 0x06, 0x70, 0x85, 0x39, 0x8e, 0x2f, 0xb8, 0x64, 0x4a,
0xa0, 0xd9, 0xad, 0x6d, 0x15, 0x52, 0x66, 0xca, 0x71, 0xfe, 0x0b, 0x15, 0x96, 0xc0, 0xdf, 0x79,
0xba, 0x4b, 0x90, 0xbd, 0x81, 0x1d, 0x8b, 0xf8, 0xcc, 0x6d, 0xb8, 0x36, 0x8a, 0xef, 0x1f, 0x1c,
0x6a, 0xd4, 0xc5, 0xfc, 0xbe, 0xcf, 0x87, 0x7b, 0x70, 0x25, 0x56, 0xba, 0x23, 0xe9, 0xcc, 0xcd,
0xf0, 0xd1, 0x1e, 0x0c, 0x95, 0x48, 0x27, 0x32, 0x2f, 0xc4, 0xa1, 0x5d, 0x05, 0x8b, 0xbb, 0x4a,
0x25, 0xd2, 0xd9, 0xab, 0x77, 0xb1, 0xb8, 0xbb, 0x5f, 0xef, 0xc2, 0x02, 0x2a, 0x5b, 0x38, 0x14,
0x40, 0xfd, 0x34, 0x0b, 0x50, 0xa3, 0xe1, 0xda, 0x96, 0xed, 0x21, 0x4a, 0x8d, 0xcb, 0x62, 0x58,
0x5f, 0xe0, 0xf5, 0x72, 0x02, 0x4c, 0x73, 0x79, 0x27, 0x32, 0x41, 0x3c, 0xa0, 0x92, 0x30, 0xbb,
0xa8, 0x29, 0xa8, 0x82, 0xb7, 0xf5, 0xfe, 0x64, 0x88, 0xad, 0x86, 0xef, 0x39, 0x38, 0xb0, 0x5a,
0x88, 0xad, 0x1b, 0x4f, 0x89, 0x5d, 0x7f, 0xfb, 0x20, 0x32, 0x2f, 0xcc, 0xe0, 0x56, 0x80, 0x6d,
0xc4, 0xb0, 0x33, 0x13, 0x2b, 0xce, 0x0a, 0xbd, 0x45, 0xc4, 0xd6, 0xdb, 0x91, 0xa9, 0xbd, 0x90,
0x55, 0xe7, 0x4e, 0x19, 0x7e, 0xde, 0x6f, 0xba, 0x7c, 0x92, 0xd8, 0x4e, 0xcd, 0xd0, 0x60, 0x5f,
0x05, 0x07, 0x1b, 0xfa, 0x59, 0x8a, 0x99, 0xe5, 0xf9, 0x5b, 0x56, 0x2b, 0x70, 0xfd, 0xc0, 0x65,
0x3b, 0xc6, 0xd3, 0x62, 0x53, 0x4c, 0xb6, 0x23, 0xb3, 0x97, 0x62, 0x36, 0xef, 0x6f, 0x2d, 0x26,
0x48, 0x16, 0xd9, 0x8a, 0xe2, 0xae, 0x29, 0x46, 0xa9, 0x39, 0xf8, 0x40, 0xd3, 0x07, 0x9b, 0x68,
0x3b, 0x75, 0xd3, 0xf6, 0x89, 0x1d, 0x06, 0x01, 0x26, 0xf6, 0x8e, 0x31, 0x2a, 0xc6, 0x91, 0x8a,
0xcb, 0x16, 0xb4, 0xb5, 0x80, 0xb6, 0x63, 0x8e, 0xd3, 0xb9, 0x0a, 0x3f, 0xf2, 0x9b, 0x0a, 0x79,
0x76, 0xe4, 0xab, 0xc0, 0x74, 0xc8, 0xc5, 0xed, 0x88, 0xda, 0x2e, 0x54, 0x5a, 0x05, 0x9f, 0x6a,
0x7a, 0xbf, 0x1d, 0x20, 0xba, 0x5e, 0xaa, 0x01, 0x9e, 0x11, 0xd3, 0xf2, 0xa1, 0xa8, 0x01, 0xa6,
0xd3, 0x1a, 0xc0, 0x4e, 0x6a, 0x80, 0xd9, 0xf8, 0x6c, 0xe6, 0xcd, 0xf2, 0x6c, 0x5c, 0x19, 0x86,
0x85, 0x4e, 0x35, 0xaf, 0x17, 0x62, 0xbe, 0x96, 0xfb, 0x2a, 0x46, 0x78, 0x75, 0x60, 0x27, 0xd5,
0x41, 0xfd, 0x61, 0xcc, 0xf0, 0xfa, 0x60, 0x3a, 0xae, 0x0f, 0x4a, 0xc6, 0x02, 0x0f, 0xfc, 0x44,
0xd3, 0x87, 0xca, 0xee, 0xa5, 0xd7, 0x32, 0xcf, 0x8a, 0xf9, 0x77, 0x0f, 0x22, 0xf3, 0xe4, 0x34,
0x94, 0x5e, 0x14, 0x8a, 0x56, 0xca, 0x2f, 0x0a, 0x4a, 0xb4, 0xdb, 0xd2, 0xd8, 0xdd, 0xaf, 0xe7,
0xb6, 0xa1, 0xda, 0x32, 0xf8, 0xba, 0xa6, 0x0f, 0x52, 0x16, 0x12, 0x8b, 0x67, 0x4e, 0xc8, 0x73,
0x37, 0xb1, 0x15, 0xe7, 0xc3, 0xd4, 0x78, 0x2e, 0xcb, 0x47, 0xfb, 0xb9, 0xc6, 0xed, 0x54, 0x61,
0x89, 0xe3, 0x4b, 0x59, 0x96, 0xa4, 0xc0, 0x8a, 0xc9, 0xbc, 0x14, 0xd0, 0x8e, 0x8d, 0xdf, 0x1c,
0x83, 0x2a, 0x6b, 0xbc, 0x46, 0x2e, 0xd1, 0xe0, 0x71, 0x95, 0x1a, 0xcf, 0x0b, 0x12, 0xaf, 0xf1,
0x44, 0xad, 0xd0, 0x6c, 0xc1, 0x25, 0x79, 0x2d, 0x51, 0x41, 0xe4, 0x1c, 0xb1, 0x10, 0x50, 0x27,
0xc6, 0x60, 0xd5, 0x0e, 0xcf, 0xca, 0x7b, 0x44, 0xef, 0xe9, 0x43, 0xd7, 0x0b, 0x22, 0x86, 0x3a,
0x07, 0x91, 0xd9, 0x0b, 0xd1, 0xd6, 0x12, 0x0b, 0xa5, 0x27, 0xae, 0x53, 0x34, 0xff, 0xcc, 0x2e,
0xa3, 0x72, 0xd9, 0x03, 0x9f, 0xe1, 0x4a, 0x16, 0xa1, 0x6c, 0x0f, 0x6c, 0xea, 0x67, 0x78, 0xd9,
0xb9, 0x8a, 0x28, 0xb6, 0xe2, 0x37, 0x47, 0xe3, 0xca, 0x88, 0x36, 0xda, 0x3b, 0xd1, 0x9b, 0xa6,
0x45, 0xcb, 0x42, 0x2a, 0x6e, 0x0f, 0x7b, 0x53, 0xd5, 0x58, 0x96, 0x45, 0x8e, 0xa2, 0xb8, 0x36,
0x92, 0x14, 0x21, 0xc9, 0xf2, 0x78, 0x67, 0xbf, 0xae, 0xc1, 0x52, 0x53, 0xf0, 0xdd, 0xa3, 0xfa,
0x25, 0x1e, 0x35, 0xb2, 0x70, 0xc1, 0x8b, 0x58, 0xdb, 0x6f, 0xf2, 0x25, 0x1b, 0xe0, 0x37, 0x43,
0x4c, 0x99, 0xb5, 0xe1, 0xae, 0x1a, 0x57, 0xc5, 0x74, 0xfc, 0x59, 0x4b, 0xde, 0x2a, 0x17, 0xd0,
0xf6, 0xf4, 0x1c, 0x8c, 0xf1, 0xdb, 0xee, 0x54, 0x3b, 0x32, 0xcd, 0x26, 0xda, 0xce, 0xb6, 0x38,
0x9b, 0x4b, 0x6c, 0xe4, 0x2a, 0xd9, 0x29, 0xf8, 0x00, 0x3d, 0xa9, 0x00, 0x7c, 0xa0, 0xc9, 0x07,
0xab, 0x24, 0xaf, 0x9f, 0x25, 0xba, 0xf0, 0x01, 0xcd, 0x56, 0xc1, 0xe7, 0x9a, 0x3e, 0x98, 0x3d,
0xc1, 0x78, 0x48, 0x7e, 0xb4, 0x1d, 0x13, 0x1b, 0xf8, 0x23, 0x3e, 0x12, 0x03, 0xe9, 0x13, 0xc6,
0xfc, 0xe4, 0x1d, 0xf9, 0xdd, 0x76, 0x00, 0x29, 0xe4, 0x59, 0x22, 0xad, 0x02, 0x55, 0x2f, 0x67,
0x4a, 0x23, 0x5d, 0xe4, 0xd2, 0xd6, 0x57, 0x92, 0x82, 0x79, 0x2b, 0x24, 0x3d, 0xfa, 0x6e, 0xea,
0xe7, 0xc5, 0x2b, 0x4b, 0x23, 0xf4, 0xbc, 0x24, 0xab, 0xf1, 0x49, 0x5a, 0xa2, 0x1a, 0xe3, 0xc2,
0xd3, 0x5b, 0x3c, 0x6b, 0xe0, 0x5a, 0xb3, 0xa1, 0xe7, 0x89, 0x7c, 0xe4, 0x2e, 0x49, 0x8a, 0xca,
0x4e, 0x64, 0x5e, 0x4c, 0x8e, 0x2c, 0x15, 0x5c, 0x83, 0x5d, 0xda, 0x81, 0xd7, 0xf4, 0xd3, 0x0d,
0x8c, 0x58, 0x18, 0x60, 0xab, 0xe1, 0xa1, 0x35, 0x6a, 0x4c, 0x88, 0x7d, 0x77, 0x99, 0x9f, 0xf4,
0x09, 0x30, 0xcb, 0xe5, 0xd9, 0x8b, 0x8c, 0x24, 0xac, 0xc1, 0x82, 0x0a, 0xd8, 0xd2, 0x87, 0xa4,
0x87, 0x98, 0xb8, 0xc6, 0xc1, 0xc4, 0x0f, 0xd7, 0xd6, 0x8d, 0x6b, 0x62, 0xd1, 0xbe, 0x2c, 0xc2,
0x6b, 0xa6, 0x32, 0xcf, 0x35, 0x5e, 0x11, 0x0a, 0x59, 0xd6, 0xa3, 0x44, 0xb3, 0x8c, 0x42, 0xdd,
0x18, 0x6c, 0xe8, 0x03, 0x95, 0x8e, 0x9b, 0x68, 0xdb, 0xb8, 0x2e, 0x7a, 0x7d, 0x89, 0x27, 0x83,
0xa5, 0x86, 0x0b, 0x68, 0xbb, 0x13, 0x99, 0x86, 0xaa, 0xcb, 0x05, 0xb4, 0x9d, 0xf5, 0xa7, 0x68,
0x06, 0xde, 0x3b, 0xaa, 0x9b, 0xe9, 0xed, 0x92, 0x85, 0x3c, 0x9e, 0x52, 0xf8, 0x9e, 0x63, 0x31,
0x8f, 0x5a, 0x3c, 0x7e, 0xb8, 0x3e, 0xa1, 0xc6, 0x8b, 0x62, 0xbe, 0x3e, 0xe6, 0x2b, 0xf3, 0x42,
0x7a, 0x97, 0x33, 0xc9, 0x55, 0xef, 0x7a, 0xce, 0xf2, 0xfc, 0xd2, 0xff, 0x25, 0x7a, 0xed, 0xc8,
0xbc, 0xe0, 0x76, 0x87, 0xb3, 0x7c, 0xe7, 0x3e, 0x3a, 0x7c, 0x7d, 0xde, 0xd7, 0xc6, 0xfd, 0xe1,
0xdd, 0xfd, 0xfa, 0xfd, 0x08, 0xc2, 0x6a, 0x5b, 0x8f, 0xa6, 0x20, 0xf8, 0xb2, 0xde, 0x13, 0xb6,
0x48, 0x2b, 0x3b, 0x50, 0x7f, 0x31, 0x2b, 0xdc, 0xfe, 0xff, 0x83, 0xc8, 0x3c, 0x97, 0xe7, 0x72,
0x2b, 0x8b, 0x64, 0x31, 0x3f, 0x5d, 0x45, 0x16, 0x97, 0x24, 0xb8, 0x2d, 0xd2, 0x4a, 0x00, 0x29,
0x7f, 0xdb, 0xdd, 0xaf, 0xab, 0x1b, 0x1b, 0x1a, 0x3c, 0x25, 0x35, 0x01, 0x3f, 0xd3, 0x92, 0xee,
0xd3, 0xe7, 0x8b, 0x0f, 0x66, 0xc5, 0x74, 0xbf, 0x23, 0xe2, 0x41, 0xd1, 0x44, 0xf6, 0x94, 0x21,
0xba, 0x1f, 0xc9, 0xba, 0x97, 0x9f, 0x20, 0x24, 0x0e, 0x79, 0xe0, 0x3b, 0xdf, 0x5d, 0x8b, 0x6f,
0x70, 0x55, 0x2f, 0x86, 0x06, 0xf5, 0xbc, 0x15, 0xf8, 0x8d, 0xa6, 0xf7, 0x0a, 0x9a, 0xf9, 0x43,
0xc5, 0x2f, 0x63, 0xa2, 0xdf, 0x14, 0xf5, 0x41, 0xd1, 0x84, 0xf4, 0x68, 0x21, 0xa8, 0xd6, 0x32,
0xaa, 0xc5, 0x67, 0x06, 0x25, 0xd9, 0x8b, 0xf7, 0xd3, 0xe3, 0x55, 0x80, 0xba, 0x2f, 0x43, 0x83,
0x3d, 0x72, 0xcb, 0x9c, 0x72, 0xfe, 0x1c, 0xf1, 0x61, 0x77, 0xca, 0xd2, 0xd3, 0x44, 0x89, 0x72,
0xf1, 0x31, 0xa1, 0x3b, 0xe5, 0x6e, 0x7a, 0x55, 0xca, 0xa9, 0x66, 0x4a, 0x39, 0x7b, 0x7d, 0x68,
0xe8, 0xf1, 0xb3, 0x67, 0x96, 0x3e, 0xfc, 0x6a, 0x56, 0xc4, 0xb1, 0xff, 0x29, 0xf2, 0x15, 0x2f,
0x87, 0x79, 0x1e, 0x21, 0x2d, 0xc6, 0x20, 0x47, 0x8a, 0xc5, 0x44, 0x8f, 0x84, 0x50, 0x71, 0x79,
0x53, 0xbd, 0x37, 0xb1, 0x5a, 0x36, 0x33, 0x3e, 0xe2, 0x43, 0xa4, 0x4d, 0x2d, 0x1c, 0x44, 0xe6,
0xc5, 0xbc, 0xc7, 0x85, 0xe2, 0xad, 0xc7, 0xa2, 0xcd, 0x8a, 0xe3, 0xd4, 0xac, 0xe0, 0xc5, 0xee,
0x41, 0x55, 0x81, 0xe7, 0x4a, 0x03, 0xa5, 0x4c, 0x81, 0xda, 0x88, 0x50, 0xe3, 0xd7, 0xf1, 0x2c,
0x2d, 0x97, 0x28, 0xc8, 0x27, 0xec, 0x12, 0x57, 0x2c, 0x51, 0xa8, 0xe0, 0xd5, 0xa9, 0x12, 0x4c,
0x2a, 0x7a, 0x53, 0xb7, 0x3f, 0xf9, 0x6c, 0xf8, 0xc8, 0xfe, 0x67, 0xc3, 0x47, 0x3e, 0x39, 0x18,
0xd6, 0xf6, 0x0f, 0x86, 0xb5, 0x6f, 0xdf, 0x1b, 0x3e, 0xf2, 0xfe, 0xbd, 0x61, 0x6d, 0xff, 0xde,
0xf0, 0x91, 0xbf, 0xdd, 0x1b, 0x3e, 0xf2, 0xc6, 0x33, 0x6b, 0x2e, 0x5b, 0x0f, 0x57, 0xaf, 0xd8,
0x7e, 0xf3, 0x6a, 0x96, 0xbf, 0x4b, 0xbf, 0xf2, 0xff, 0x71, 0xad, 0x9e, 0x10, 0x7f, 0xdc, 0xba,
0xf6, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x3d, 0xe9, 0x4b, 0x24, 0x26, 0x00, 0x00,
}
func (m *OptionsConfiguration) Marshal() (dAtA []byte, err error) {
@@ -431,6 +438,18 @@ func (m *OptionsConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0xc0
}
if m.InsecureAllowOldTLSVersions {
i--
if m.InsecureAllowOldTLSVersions {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x3
i--
dAtA[i] = 0xa8
}
if m.ConnectionLimitMax != 0 {
i = encodeVarintOptionsconfiguration(dAtA, i, uint64(m.ConnectionLimitMax))
i--
@@ -1072,6 +1091,9 @@ func (m *OptionsConfiguration) ProtoSize() (n int) {
if m.ConnectionLimitMax != 0 {
n += 2 + sovOptionsconfiguration(uint64(m.ConnectionLimitMax))
}
if m.InsecureAllowOldTLSVersions {
n += 3
}
if m.DeprecatedUPnPEnabled {
n += 4
}
@@ -2288,6 +2310,26 @@ func (m *OptionsConfiguration) Unmarshal(dAtA []byte) error {
break
}
}
case 53:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field InsecureAllowOldTLSVersions", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowOptionsconfiguration
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.InsecureAllowOldTLSVersions = bool(v != 0)
case 9000:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedUPnPEnabled", wireType)
@@ -2433,10 +2475,7 @@ func (m *OptionsConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthOptionsconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthOptionsconfiguration
}
if (iNdEx + skippy) > l {
+1 -4
View File
@@ -235,10 +235,7 @@ func (m *Size) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthSize
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthSize
}
if (iNdEx + skippy) > l {
+2 -5
View File
@@ -403,7 +403,7 @@ func (m *VersioningConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthVersioningconfiguration
}
if (iNdEx + skippy) > postIndex {
@@ -490,10 +490,7 @@ func (m *VersioningConfiguration) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthVersioningconfiguration
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthVersioningconfiguration
}
if (iNdEx + skippy) > l {
+1
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/mocked_wrapper.go --fake-name Wrapper . Wrapper
package config
+207
View File
@@ -8,12 +8,26 @@ package connections
import (
"context"
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/url"
"os"
"strings"
"testing"
"time"
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil"
)
func TestFixupPort(t *testing.T) {
@@ -216,3 +230,196 @@ func TestConnectionStatus(t *testing.T) {
check(nil, nil)
}
func BenchmarkConnections(pb *testing.B) {
addrs := []string{
"tcp://127.0.0.1:0",
"quic://127.0.0.1:0",
"relay://127.0.0.1:22067",
}
sizes := []int{
1 << 10,
1 << 15,
1 << 20,
1 << 22,
}
haveRelay := false
// Check if we have a relay running locally
conn, err := net.DialTimeout("tcp", "127.0.0.1:22067", 100*time.Millisecond)
if err == nil {
haveRelay = true
_ = conn.Close()
}
for _, addr := range addrs {
for _, sz := range sizes {
for _, direction := range []string{"cs", "sc"} {
proto := strings.SplitN(addr, ":", 2)[0]
pb.Run(fmt.Sprintf("%s_%d_%s", proto, sz, direction), func(b *testing.B) {
if proto == "relay" && !haveRelay {
b.Skip("could not connect to relay")
}
withConnectionPair(b, addr, func(client, server internalConn) {
if direction == "sc" {
server, client = client, server
}
data := make([]byte, sz)
if _, err := rand.Read(data); err != nil {
b.Fatal(err)
}
total := 0
wg := sync.NewWaitGroup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
wg.Add(2)
go func() {
if err := sendMsg(client, data); err != nil {
b.Fatal(err)
}
wg.Done()
}()
go func() {
if err := recvMsg(server, data); err != nil {
b.Fatal(err)
}
total += sz
wg.Done()
}()
wg.Wait()
}
b.ReportAllocs()
b.SetBytes(int64(total / b.N))
})
})
}
}
}
}
func sendMsg(c internalConn, buf []byte) error {
n, err := c.Write(buf)
if n != len(buf) || err != nil {
return err
}
return nil
}
func recvMsg(c internalConn, buf []byte) error {
for read := 0; read != len(buf); {
n, err := c.Read(buf)
read += n
if err != nil {
return err
}
}
return nil
}
func withConnectionPair(b *testing.B, connUri string, h func(client, server internalConn)) {
// Root of the service tree.
supervisor := suture.New("main", suture.Spec{
PassThroughPanics: true,
})
cert := mustGetCert(b)
deviceId := protocol.NewDeviceID(cert.Certificate[0])
tlsCfg := tlsutil.SecureDefaultTLS13()
tlsCfg.Certificates = []tls.Certificate{cert}
tlsCfg.NextProtos = []string{"bench"}
tlsCfg.ClientAuth = tls.RequestClientCert
tlsCfg.SessionTicketsDisabled = true
tlsCfg.InsecureSkipVerify = true
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
supervisor.ServeBackground(ctx)
cfg := config.Configuration{
Options: config.OptionsConfiguration{
RelaysEnabled: true,
},
}
wcfg := config.Wrap("", cfg, deviceId, events.NoopLogger)
uri, err := url.Parse(connUri)
if err != nil {
b.Fatal(err)
}
lf, err := getListenerFactory(cfg, uri)
if err != nil {
b.Fatal(err)
}
natSvc := nat.NewService(deviceId, wcfg)
conns := make(chan internalConn, 1)
listenSvc := lf.New(uri, wcfg, tlsCfg, conns, natSvc)
supervisor.Add(listenSvc)
var addr *url.URL
for {
addrs := listenSvc.LANAddresses()
if len(addrs) > 0 {
if !strings.HasSuffix(addrs[0].Host, ":0") {
addr = addrs[0]
break
}
}
time.Sleep(time.Millisecond)
}
df, err := getDialerFactory(cfg, addr)
if err != nil {
b.Fatal(err)
}
dialer := df.New(cfg.Options, tlsCfg)
// Relays might take some time to register the device, so dial multiple times
clientConn, err := dialer.Dial(ctx, deviceId, addr)
if err != nil {
for i := 0; i < 10 && err != nil; i++ {
clientConn, err = dialer.Dial(ctx, deviceId, addr)
time.Sleep(100 * time.Millisecond)
}
if err != nil {
b.Fatal(err)
}
}
data := []byte("hello")
// Quic does not start a stream until some data is sent through, so send something for the AcceptStream
// to fire on the other side.
if err := sendMsg(clientConn, data); err != nil {
b.Fatal(err)
}
serverConn := <-conns
if err := recvMsg(serverConn, data); err != nil {
b.Fatal(err)
}
h(clientConn, serverConn)
_ = clientConn.Close()
_ = serverConn.Close()
}
func mustGetCert(b *testing.B) tls.Certificate {
f1, err := ioutil.TempFile("", "")
if err != nil {
b.Fatal(err)
}
f1.Close()
f2, err := ioutil.TempFile("", "")
if err != nil {
b.Fatal(err)
}
f2.Close()
cert, err := tlsutil.NewCertificate(f1.Name(), f2.Name(), "bench", 10)
if err != nil {
b.Fatal(err)
}
_ = os.Remove(f1.Name())
_ = os.Remove(f2.Name())
return cert
}
+8 -1
View File
@@ -93,8 +93,15 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
type quicDialerFactory struct{}
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
// So the idea is that we should probably try dialing every 20 seconds.
// However it would still be nice if this was adjustable/proportional to ReconnectIntervalS
// But prevent something silly like 1/3 = 0 etc.
quicInterval := opts.ReconnectIntervalS / 3
if quicInterval < 10 {
quicInterval = 10
}
return &quicDialer{commonDialer{
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
reconnectInterval: time.Duration(quicInterval) * time.Second,
tlsCfg: tlsCfg,
}}
}
+61 -17
View File
@@ -48,6 +48,7 @@ type quicListener struct {
factory listenerFactory
address *url.URL
laddr net.Addr
mut sync.Mutex
}
@@ -82,18 +83,33 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string)
func (t *quicListener) serve(ctx context.Context) error {
network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp")
packetConn, err := net.ListenPacket(network, t.uri.Host)
udpAddr, err := net.ResolveUDPAddr(network, t.uri.Host)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
return err
}
defer func() { _ = packetConn.Close() }()
svc, conn := stun.New(t.cfg, t, packetConn)
defer func() { _ = conn.Close() }()
wrapped := &stunConnQUICWrapper{
udpConn, err := net.ListenUDP(network, udpAddr)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
return err
}
defer func() { _ = udpConn.Close() }()
svc, conn := stun.New(t.cfg, t, udpConn)
defer conn.Close()
quicWrapper := quicWrapper{
PacketConn: conn,
underlying: packetConn.(*net.UDPConn),
underlying: udpConn,
}
var wrapped net.PacketConn = &quicWrapper
if oobConn, ok := conn.(oobConn); ok {
l.Debugf("wrapping in oob conn")
wrapped = &oobConnWrapper{
quicWrapper, oobConn,
}
}
go svc.Serve(ctx)
@@ -106,12 +122,22 @@ func (t *quicListener) serve(ctx context.Context) error {
l.Infoln("Listen (BEP/quic):", err)
return err
}
t.notifyAddressesChanged(t)
defer listener.Close()
t.notifyAddressesChanged(t)
defer t.clearAddresses(t)
l.Infof("QUIC listener (%v) starting", packetConn.LocalAddr())
defer l.Infof("QUIC listener (%v) shutting down", packetConn.LocalAddr())
l.Infof("QUIC listener (%v) starting", udpConn.LocalAddr())
defer l.Infof("QUIC listener (%v) shutting down", udpConn.LocalAddr())
t.mut.Lock()
t.laddr = udpConn.LocalAddr()
t.mut.Unlock()
defer func() {
t.mut.Lock()
t.laddr = nil
t.mut.Unlock()
}()
acceptFailures := 0
const maxAcceptFailures = 10
@@ -164,8 +190,8 @@ func (t *quicListener) URI() *url.URL {
}
func (t *quicListener) WANAddresses() []*url.URL {
uris := []*url.URL{t.uri}
t.mut.Lock()
uris := []*url.URL{maybeReplacePort(t.uri, t.laddr)}
if t.address != nil {
uris = append(uris, t.address)
}
@@ -174,9 +200,12 @@ func (t *quicListener) WANAddresses() []*url.URL {
}
func (t *quicListener) LANAddresses() []*url.URL {
addrs := []*url.URL{t.uri}
network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp")
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(network, t.uri)...)
t.mut.Lock()
uri := maybeReplacePort(t.uri, t.laddr)
t.mut.Unlock()
addrs := []*url.URL{uri}
network := strings.ReplaceAll(uri.Scheme, "quic", "udp")
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(network, uri)...)
return addrs
}
@@ -219,17 +248,32 @@ func (quicListenerFactory) Enabled(cfg config.Configuration) bool {
return true
}
type stunConnQUICWrapper struct {
// quicWrapper provides methods used by quic
// https://github.com/lucas-clemente/quic-go/blob/master/packet_handler_map.go#L85
type quicWrapper struct {
net.PacketConn
underlying *net.UDPConn
}
// SetReadBuffer is required by QUIC < v0.20.0
func (s *stunConnQUICWrapper) SetReadBuffer(size int) error {
// SetReadBuffer is required by QUIC
func (s *quicWrapper) SetReadBuffer(size int) error {
return s.underlying.SetReadBuffer(size)
}
// SyscallConn is required by QUIC
func (s *stunConnQUICWrapper) SyscallConn() (syscall.RawConn, error) {
func (s *quicWrapper) SyscallConn() (syscall.RawConn, error) {
return s.underlying.SyscallConn()
}
// oobConn is used to assert that stun package returned a net.PacketConn that implements this interface.
// If it does, we then wrap quicWrapper in oobConnWrapper, to expose those methods to QUIC package.
type oobConn interface {
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
}
// See: https://pkg.go.dev/github.com/lucas-clemente/quic-go#OOBCapablePacketConn
type oobConnWrapper struct {
quicWrapper
oobConn
}
+17 -16
View File
@@ -44,38 +44,39 @@ type relayListener struct {
}
func (t *relayListener) serve(ctx context.Context) error {
clnt, err := client.NewClient(t.uri, t.tlsCfg.Certificates, nil, 10*time.Second)
clnt, err := client.NewClient(t.uri, t.tlsCfg.Certificates, 10*time.Second)
if err != nil {
l.Infoln("Listen (BEP/relay):", err)
return err
}
invitations := clnt.Invitations()
t.mut.Lock()
t.client = clnt
go clnt.Serve(ctx)
t.mut.Unlock()
// Start with nil, so that we send a addresses changed notification as soon as we connect somewhere.
var oldURI *url.URL
l.Infof("Relay listener (%v) starting", t)
defer l.Infof("Relay listener (%v) shutting down", t)
defer t.clearAddresses(t)
invitationCtx, cancel := context.WithCancel(ctx)
defer cancel()
go t.handleInvitations(invitationCtx, clnt)
return clnt.Serve(ctx)
}
func (t *relayListener) handleInvitations(ctx context.Context, clnt client.RelayClient) {
invitations := clnt.Invitations()
// Start with nil, so that we send a addresses changed notification as soon as we connect somewhere.
var oldURI *url.URL
for {
select {
case inv, ok := <-invitations:
if !ok {
if err := clnt.Error(); err != nil {
l.Infoln("Listen (BEP/relay):", err)
}
return err
}
case inv := <-invitations:
conn, err := client.JoinSession(ctx, inv)
if err != nil {
if errors.Cause(err) != context.Canceled {
if !errors.Is(err, context.Canceled) {
l.Infoln("Listen (BEP/relay): joining session:", err)
}
continue
@@ -119,7 +120,7 @@ func (t *relayListener) serve(ctx context.Context) error {
}
case <-ctx.Done():
return ctx.Err()
return
}
}
}
+43 -23
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/service.go --fake-name Service . Service
package connections
@@ -141,10 +142,10 @@ type service struct {
natService *nat.Service
evLogger events.Logger
listenersMut sync.RWMutex
listeners map[string]genericListener
listenerTokens map[string]suture.ServiceToken
listenerSupervisor *suture.Supervisor
deviceAddressesChanged chan struct{}
listenersMut sync.RWMutex
listeners map[string]genericListener
listenerTokens map[string]suture.ServiceToken
}
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger) Service {
@@ -165,22 +166,10 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
natService: nat.NewService(myID, cfg),
evLogger: evLogger,
listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener),
listenerTokens: make(map[string]suture.ServiceToken),
// A listener can fail twice, rapidly. Any more than that and it
// will be put on suspension for ten minutes. Restarts and changes
// due to config are done by removing and adding services, so are
// not subject to these limitations.
listenerSupervisor: suture.New("c.S.listenerSupervisor", suture.Spec{
EventHook: func(e suture.Event) {
l.Infoln(e)
},
FailureThreshold: 2,
FailureBackoff: 600 * time.Second,
PassThroughPanics: true,
}),
deviceAddressesChanged: make(chan struct{}, 1),
listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener),
listenerTokens: make(map[string]suture.ServiceToken),
}
cfg.Subscribe(service)
@@ -198,7 +187,6 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
service.Add(svcutil.AsService(service.connect, fmt.Sprintf("%s/connect", service)))
service.Add(svcutil.AsService(service.handle, fmt.Sprintf("%s/handle", service)))
service.Add(service.listenerSupervisor)
service.Add(service.natService)
svcutil.OnSupervisorDone(service.Supervisor, func() {
@@ -392,6 +380,7 @@ func (s *service) connect(ctx context.Context) error {
l.Debugln("Next connection loop in", sleep)
select {
case <-s.deviceAddressesChanged:
case <-time.After(sleep):
case <-ctx.Done():
return ctx.Err()
@@ -644,8 +633,18 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService)
listener.OnAddressesChanged(s.logListenAddressesChangedEvent)
// Retrying a listener many times in rapid succession is unlikely to help,
// thus back off quickly. A listener may soon be functional again, e.g. due
// to a network interface coming back online - retry every minute.
spec := svcutil.SpecWithInfoLogger(l)
spec.FailureThreshold = 2
spec.FailureBackoff = time.Minute
sup := suture.New(fmt.Sprintf("listenerSupervisor@%v", listener), spec)
sup.Add(listener)
s.listeners[uri.String()] = listener
s.listenerTokens[uri.String()] = s.listenerSupervisor.Add(listener)
s.listenerTokens[uri.String()] = s.Add(sup)
return true
}
@@ -675,6 +674,8 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
}
}
s.checkAndSignalConnectLoopOnUpdatedDevices(from, to)
s.listenersMut.Lock()
seen := make(map[string]struct{})
for _, addr := range to.Options.ListenAddresses() {
@@ -723,7 +724,7 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
for addr, listener := range s.listeners {
if _, ok := seen[addr]; !ok || listener.Factory().Valid(to) != nil {
l.Debugln("Stopping listener", addr)
s.listenerSupervisor.Remove(s.listenerTokens[addr])
s.Remove(s.listenerTokens[addr])
delete(s.listenerTokens, addr)
delete(s.listeners, addr)
}
@@ -733,6 +734,25 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
return true
}
func (s *service) checkAndSignalConnectLoopOnUpdatedDevices(from, to config.Configuration) {
oldDevices := make(map[protocol.DeviceID]config.DeviceConfiguration, len(from.Devices))
for _, dev := range from.Devices {
oldDevices[dev.DeviceID] = dev
}
for _, dev := range to.Devices {
oldDev, ok := oldDevices[dev.DeviceID]
if !ok || !util.EqualStrings(oldDev.Addresses, dev.Addresses) {
select {
case s.deviceAddressesChanged <- struct{}{}:
default:
// channel is blocked - a config update is already pending for the connection loop.
}
break
}
}
}
func (s *service) AllAddresses() []string {
s.listenersMut.RLock()
var addrs []string
+26 -11
View File
@@ -40,6 +40,7 @@ type tcpListener struct {
natService *nat.Service
mapping *nat.Mapping
laddr net.Addr
mut sync.RWMutex
}
@@ -60,25 +61,37 @@ func (t *tcpListener) serve(ctx context.Context) error {
l.Infoln("Listen (BEP/tcp):", err)
return err
}
t.notifyAddressesChanged(t)
registry.Register(t.uri.Scheme, tcaddr)
defer listener.Close()
// We might bind to :0, so use the port we've been given.
tcaddr = listener.Addr().(*net.TCPAddr)
t.notifyAddressesChanged(t)
defer t.clearAddresses(t)
registry.Register(t.uri.Scheme, tcaddr)
defer registry.Unregister(t.uri.Scheme, tcaddr)
l.Infof("TCP listener (%v) starting", listener.Addr())
defer l.Infof("TCP listener (%v) shutting down", listener.Addr())
l.Infof("TCP listener (%v) starting", tcaddr)
defer l.Infof("TCP listener (%v) shutting down", tcaddr)
mapping := t.natService.NewMapping(nat.TCP, tcaddr.IP, tcaddr.Port)
mapping.OnChanged(func(_ *nat.Mapping, _, _ []nat.Address) {
t.notifyAddressesChanged(t)
})
// Should be called after t.mapping is nil'ed out.
defer t.natService.RemoveMapping(mapping)
t.mut.Lock()
t.mapping = mapping
t.laddr = tcaddr
t.mut.Unlock()
defer func() {
t.mut.Lock()
t.mapping = nil
t.laddr = nil
t.mut.Unlock()
}()
acceptFailures := 0
const maxAcceptFailures = 10
@@ -94,9 +107,6 @@ func (t *tcpListener) serve(ctx context.Context) error {
if err == nil {
conn.Close()
}
t.mut.Lock()
t.mapping = nil
t.mut.Unlock()
return nil
default:
}
@@ -146,8 +156,10 @@ func (t *tcpListener) URI() *url.URL {
}
func (t *tcpListener) WANAddresses() []*url.URL {
uris := []*url.URL{t.uri}
t.mut.RLock()
uris := []*url.URL{
maybeReplacePort(t.uri, t.laddr),
}
if t.mapping != nil {
addrs := t.mapping.ExternalAddresses()
for _, addr := range addrs {
@@ -179,8 +191,11 @@ func (t *tcpListener) WANAddresses() []*url.URL {
}
func (t *tcpListener) LANAddresses() []*url.URL {
addrs := []*url.URL{t.uri}
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(t.uri.Scheme, t.uri)...)
t.mut.RLock()
uri := maybeReplacePort(t.uri, t.laddr)
t.mut.RUnlock()
addrs := []*url.URL{uri}
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(uri.Scheme, uri)...)
return addrs
}
+27
View File
@@ -117,3 +117,30 @@ func isV4Local(ip net.IP) bool {
}
return false
}
func maybeReplacePort(uri *url.URL, laddr net.Addr) *url.URL {
if laddr == nil {
return uri
}
host, portStr, err := net.SplitHostPort(uri.Host)
if err != nil {
return uri
}
port, err := strconv.Atoi(portStr)
if err != nil {
return uri
}
if port != 0 {
return uri
}
_, lportStr, err := net.SplitHostPort(laddr.String())
if err != nil {
return uri
}
uriCopy := *uri
uriCopy.Host = net.JoinHostPort(host, lportStr)
return &uriCopy
}
+1 -1
View File
@@ -36,7 +36,7 @@ const (
// KeyTypeFolderStatistic <folder ID as string> <some string> = some value
KeyTypeFolderStatistic byte = 4
// KeyTypeVirtualMtime <int32 folder ID> <file name> = dbMtime
// KeyTypeVirtualMtime <int32 folder ID> <file name> = mtimeMapping
KeyTypeVirtualMtime byte = 5
// KeyTypeFolderIdx <int32 id> = string value
+37 -3
View File
@@ -587,6 +587,18 @@ func (db *Lowlevel) dropFolderIndexIDs(folder []byte) error {
return t.Commit()
}
func (db *Lowlevel) dropIndexIDs() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
if err := t.deleteKeyPrefix([]byte{KeyTypeIndexID}); err != nil {
return err
}
return t.Commit()
}
func (db *Lowlevel) dropMtimes(folder []byte) error {
key, err := db.keyer.GenerateMtimesKey(nil, folder)
if err != nil {
@@ -663,7 +675,7 @@ func (db *Lowlevel) timeUntil(key string, every time.Duration) time.Duration {
return sleepTime
}
func (db *Lowlevel) gcIndirect(ctx context.Context) error {
func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
// The indirection GC uses bloom filters to track used block lists and
// versions. This means iterating over all items, adding their hashes to
// the filter, then iterating over the indirected items and removing
@@ -677,6 +689,26 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
db.gcMut.Lock()
defer db.gcMut.Unlock()
l.Debugln("Started database GC")
var discardedBlocks, matchedBlocks, discardedVersions, matchedVersions int
internalCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
// Only print something if the process takes more than "a moment".
select {
case <-internalCtx.Done():
case <-time.After(10 * time.Second):
l.Infoln("Database GC started - many Syncthing operations will be unresponsive until it's finished")
<-internalCtx.Done()
if err != nil || ctx.Err() != nil {
return
}
l.Infof("Database GC done (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
}
}()
t, err := db.newReadWriteTransaction()
if err != nil {
return err
@@ -734,7 +766,6 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
return err
}
defer it.Release()
matchedBlocks := 0
for it.Next() {
select {
case <-ctx.Done():
@@ -750,6 +781,7 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
if err := t.Delete(key); err != nil {
return err
}
discardedBlocks++
}
it.Release()
if err := it.Error(); err != nil {
@@ -763,7 +795,6 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
if err != nil {
return err
}
matchedVersions := 0
for it.Next() {
select {
case <-ctx.Done():
@@ -779,6 +810,7 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
if err := t.Delete(key); err != nil {
return err
}
discardedVersions++
}
it.Release()
if err := it.Error(); err != nil {
@@ -795,6 +827,8 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) error {
return err
}
l.Debugf("Finished GC, starting compaction (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
return db.Compact()
}
+54 -1
View File
@@ -20,7 +20,7 @@ import (
// do not put restrictions on downgrades (e.g. for repairs after a bugfix).
const (
dbVersion = 14
dbMigrationVersion = 16
dbMigrationVersion = 18
dbMinSyncthingVersion = "v1.9.0"
)
@@ -101,6 +101,8 @@ func (db *schemaUpdater) updateSchema() error {
{13, 13, "v1.7.0", db.updateSchemaTo13},
{14, 14, "v1.9.0", db.updateSchemaTo14},
{14, 16, "v1.9.0", db.checkRepairMigration},
{14, 17, "v1.9.0", db.migration17},
{14, 18, "v1.9.0", db.dropIndexIDsMigration},
}
for _, m := range migrations {
@@ -783,6 +785,57 @@ func (db *schemaUpdater) checkRepairMigration(_ int) error {
return nil
}
// migration17 finds all files that were pulled as invalid from an invalid
// global and make sure they get scanned/pulled again.
func (db *schemaUpdater) migration17(prev int) error {
if prev < 16 {
// Issue was introduced in migration to 16
return nil
}
t, err := db.newReadOnlyTransaction()
if err != nil {
return err
}
defer t.close()
for _, folderStr := range db.ListFolders() {
folder := []byte(folderStr)
meta, err := db.loadMetadataTracker(folderStr)
if err != nil {
return err
}
batch := NewFileInfoBatch(func(fs []protocol.FileInfo) error {
return db.updateLocalFiles(folder, fs, meta)
})
var innerErr error
err = t.withHave(folder, protocol.LocalDeviceID[:], nil, false, func(fi protocol.FileIntf) bool {
if fi.IsInvalid() && fi.FileLocalFlags() == 0 {
f := fi.(protocol.FileInfo)
f.SetMustRescan()
f.Version = protocol.Vector{}
batch.Append(f)
innerErr = batch.FlushIfFull()
return innerErr == nil
}
return true
})
if innerErr != nil {
return innerErr
}
if err != nil {
return err
}
if err := batch.Flush(); err != nil {
return err
}
}
return nil
}
func (db *schemaUpdater) dropIndexIDsMigration(_ int) error {
return db.dropIndexIDs()
}
func (db *schemaUpdater) rewriteGlobals(t readWriteTransaction) error {
it, err := t.NewPrefixIterator([]byte{KeyTypeGlobal})
if err != nil {
+1 -10
View File
@@ -451,21 +451,12 @@ func DropDeltaIndexIDs(db *Lowlevel) {
}
opStr := "DropDeltaIndexIDs"
l.Debugf(opStr)
dbi, err := db.NewPrefixIterator([]byte{KeyTypeIndexID})
err := db.dropIndexIDs()
if backend.IsClosed(err) {
return
} else if err != nil {
fatalError(err, opStr, db)
}
defer dbi.Release()
for dbi.Next() {
if err := db.Delete(dbi.Key()); err != nil && !backend.IsClosed(err) {
fatalError(err, opStr, db)
}
}
if err := dbi.Error(); err != nil && !backend.IsClosed(err) {
fatalError(err, opStr, db)
}
}
func normalizeFilenamesAndDropDuplicates(fs []protocol.FileInfo) []protocol.FileInfo {
-1
View File
@@ -472,7 +472,6 @@ func TestNeedWithInvalid(t *testing.T) {
remote0Have[0],
remote1Have[0],
remote0Have[2],
remote1Have[2],
}
replace(s, protocol.LocalDeviceID, localHave)
+11 -44
View File
@@ -1652,10 +1652,7 @@ func (m *FileVersion) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -1739,10 +1736,7 @@ func (m *VersionList) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2222,10 +2216,7 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2309,10 +2300,7 @@ func (m *BlockList) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2430,10 +2418,7 @@ func (m *IndirectionHashesOnly) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2650,10 +2635,7 @@ func (m *Counts) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2756,10 +2738,7 @@ func (m *CountsSet) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -2916,10 +2895,7 @@ func (m *FileVersionDeprecated) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -3003,10 +2979,7 @@ func (m *VersionListDeprecated) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -3141,10 +3114,7 @@ func (m *ObservedFolder) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
@@ -3291,10 +3261,7 @@ func (m *ObservedDevice) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStructs
}
if (iNdEx + skippy) > l {
+4 -2
View File
@@ -774,8 +774,10 @@ func (t readWriteTransaction) updateLocalNeed(keyBuf, folder, name []byte, add b
}
func Need(global FileVersion, haveLocal bool, localVersion protocol.Vector) bool {
// We never need a file without a valid version.
if global.Version.IsEmpty() {
// We never need an invalid file or a file without a valid version (just
// another way of expressing "invalid", really, until we fix that
// part...).
if global.IsInvalid() || global.Version.IsEmpty() {
return false
}
// We don't need a deleted file if we don't have it.
+70
View File
@@ -0,0 +1,70 @@
// 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/.
package db
import "github.com/syncthing/syncthing/lib/protocol"
// How many files to send in each Index/IndexUpdate message.
const (
MaxBatchSizeBytes = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed)
MaxBatchSizeFiles = 1000 // Either way, don't include more files than this
)
// FileInfoBatch is a utility to do file operations on the database in suitably
// sized batches.
type FileInfoBatch struct {
infos []protocol.FileInfo
size int
flushFn func([]protocol.FileInfo) error
}
func NewFileInfoBatch(fn func([]protocol.FileInfo) error) *FileInfoBatch {
return &FileInfoBatch{
infos: make([]protocol.FileInfo, 0, MaxBatchSizeFiles),
flushFn: fn,
}
}
func (b *FileInfoBatch) SetFlushFunc(fn func([]protocol.FileInfo) error) {
b.flushFn = fn
}
func (b *FileInfoBatch) Append(f protocol.FileInfo) {
b.infos = append(b.infos, f)
b.size += f.ProtoSize()
}
func (b *FileInfoBatch) Full() bool {
return len(b.infos) >= MaxBatchSizeFiles || b.size >= MaxBatchSizeBytes
}
func (b *FileInfoBatch) FlushIfFull() error {
if b.Full() {
return b.Flush()
}
return nil
}
func (b *FileInfoBatch) Flush() error {
if len(b.infos) == 0 {
return nil
}
if err := b.flushFn(b.infos); err != nil {
return err
}
b.Reset()
return nil
}
func (b *FileInfoBatch) Reset() {
b.infos = b.infos[:0]
b.size = 0
}
func (b *FileInfoBatch) Size() int {
return b.size
}
+1 -4
View File
@@ -298,10 +298,7 @@ func (m *Announce) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthLocal
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthLocal
}
if (iNdEx + skippy) > l {
+1
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/manager.go --fake-name Manager . Manager
package discover
+1
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/buffered_subscription.go --fake-name BufferedSubscription . BufferedSubscription
// Package events provides event subscription and polling functionality.
+8
View File
@@ -330,6 +330,14 @@ func (f *BasicFilesystem) SameFile(fi1, fi2 FileInfo) bool {
return os.SameFile(f1.osFileInfo(), f2.osFileInfo())
}
func (f *BasicFilesystem) underlying() (Filesystem, bool) {
return nil, false
}
func (f *BasicFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
// basicFile implements the fs.File interface on top of an os.File
type basicFile struct {
*os.File
+3 -2
View File
@@ -10,8 +10,9 @@ package fs
import "github.com/syncthing/notify"
// notify.InAttrib is not only required for permissions, but also mod. time changes
const (
subEventMask = notify.InCreate | notify.InMovedTo | notify.InDelete | notify.InDeleteSelf | notify.InModify | notify.InMovedFrom | notify.InMoveSelf
permEventMask = notify.InAttrib
subEventMask = notify.InCreate | notify.InMovedTo | notify.InDelete | notify.InDeleteSelf | notify.InModify | notify.InMovedFrom | notify.InMoveSelf | notify.InAttrib
permEventMask = 0
rmEventMask = notify.InDelete | notify.InDeleteSelf | notify.InMovedFrom | notify.InMoveSelf
)
+3 -2
View File
@@ -13,7 +13,8 @@ import "github.com/syncthing/notify"
const (
// Platform independent notify.Create is required, as kqueue does not have
// any event signalling file creation, but notify does generate those internally.
subEventMask = notify.NoteDelete | notify.NoteWrite | notify.NoteRename | notify.Create
permEventMask = notify.NoteAttrib | notify.NoteExtend
// NoteAttrib is not only required for permissions, but also mod. time changes
subEventMask = notify.NoteDelete | notify.NoteWrite | notify.NoteRename | notify.Create | notify.NoteAttrib | notify.NoteExtend
permEventMask = 0
rmEventMask = notify.NoteDelete | notify.NoteRename
)
+39 -7
View File
@@ -87,7 +87,7 @@ func TestWatchIgnore(t *testing.T) {
{name, NonRemove},
}
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), skipIgnoredDirs: true})
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), skipIgnoredDirs: true}, false)
}
func TestWatchInclude(t *testing.T) {
@@ -114,7 +114,7 @@ func TestWatchInclude(t *testing.T) {
{name, NonRemove},
}
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), include: filepath.Join(name, included)})
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), include: filepath.Join(name, included)}, false)
}
func TestWatchRename(t *testing.T) {
@@ -146,7 +146,7 @@ func TestWatchRename(t *testing.T) {
// set the "allow others" flag because we might get the create of
// "oldfile" initially
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
}
// TestWatchWinRoot checks that a watch at a drive letter does not panic due to
@@ -308,7 +308,7 @@ func TestWatchOverflow(t *testing.T) {
}
}
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
}
func TestWatchErrorLinuxInterpretation(t *testing.T) {
@@ -413,7 +413,39 @@ func TestWatchIssue4877(t *testing.T) {
testFs = origTestFs
}()
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{})
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
}
func TestWatchModTime(t *testing.T) {
name := "modtime"
file := createTestFile(name, "foo")
path := filepath.Join(name, file)
now := time.Now()
before := now.Add(-10 * time.Second)
if err := testFs.Chtimes(path, before, before); err != nil {
t.Fatal(err)
}
testCase := func() {
if err := testFs.Chtimes(path, now, now); err != nil {
t.Error(err)
}
}
expectedEvents := []Event{
{file, NonRemove},
}
var allowedEvents []Event
// Apparently an event for the parent is also sent on mac
if runtime.GOOS == "darwin" {
allowedEvents = []Event{
{name, NonRemove},
}
}
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, true)
}
// path relative to folder root, also creates parent dirs if necessary
@@ -442,7 +474,7 @@ func sleepMs(ms int) {
time.Sleep(time.Duration(ms) * time.Millisecond)
}
func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, fm fakeMatcher) {
func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, fm fakeMatcher, ignorePerms bool) {
if err := testFs.MkdirAll(name, 0755); err != nil {
panic(fmt.Sprintf("Failed to create directory %s: %s", name, err))
}
@@ -451,7 +483,7 @@ func testScenario(t *testing.T, name string, testCase func(), expectedEvents, al
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
eventChan, errChan, err := testFs.Watch(name, fm, ctx, false)
eventChan, errChan, err := testFs.Watch(name, fm, ctx, ignorePerms)
if err != nil {
panic(err)
}
+5 -5
View File
@@ -157,9 +157,9 @@ func (f *BasicFilesystem) Roots() ([]string, error) {
// pathseparator.
func (f *BasicFilesystem) unrootedChecked(absPath string, roots []string) (string, error) {
absPath = f.resolveWin83(absPath)
lowerAbsPath := UnicodeLowercase(absPath)
lowerAbsPath := UnicodeLowercaseNormalized(absPath)
for _, root := range roots {
lowerRoot := UnicodeLowercase(root)
lowerRoot := UnicodeLowercaseNormalized(root)
if lowerAbsPath+string(PathSeparator) == lowerRoot {
return ".", nil
}
@@ -171,7 +171,7 @@ func (f *BasicFilesystem) unrootedChecked(absPath string, roots []string) (strin
}
func rel(path, prefix string) string {
lowerRel := strings.TrimPrefix(strings.TrimPrefix(UnicodeLowercase(path), UnicodeLowercase(prefix)), string(PathSeparator))
lowerRel := strings.TrimPrefix(strings.TrimPrefix(UnicodeLowercaseNormalized(path), UnicodeLowercaseNormalized(prefix)), string(PathSeparator))
return path[len(path)-len(lowerRel):]
}
@@ -193,8 +193,8 @@ func (f *BasicFilesystem) resolveWin83(absPath string) string {
}
// Failed getting the long path. Return the part of the path which is
// already a long path.
lowerRoot := UnicodeLowercase(f.root)
for absPath = filepath.Dir(absPath); strings.HasPrefix(UnicodeLowercase(absPath), lowerRoot); absPath = filepath.Dir(absPath) {
lowerRoot := UnicodeLowercaseNormalized(f.root)
for absPath = filepath.Dir(absPath); strings.HasPrefix(UnicodeLowercaseNormalized(absPath), lowerRoot); absPath = filepath.Dir(absPath) {
if !isMaybeWin83(absPath) {
return absPath
}
+15 -3
View File
@@ -15,6 +15,7 @@ import (
"time"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/text/unicode/norm"
)
const (
@@ -339,6 +340,14 @@ func (f *caseFilesystem) Unhide(name string) error {
return f.Filesystem.Unhide(name)
}
func (f *caseFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *caseFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeCase
}
func (f *caseFilesystem) checkCase(name string) error {
var err error
if name, err = Canonicalize(name); err != nil {
@@ -367,7 +376,10 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
if err != nil {
return err
}
if realName != name {
// We normalize the normalization (hah!) of the strings before
// comparing, as we don't want to treat a normalization difference as a
// case conflict.
if norm.NFC.String(realName) != norm.NFC.String(name) {
return &ErrCaseConflict{name, realName}
}
return nil
@@ -416,7 +428,7 @@ func (r *defaultRealCaser) realCase(name string) (string, error) {
lastLower := ""
for _, n := range dirNames {
node.children[n] = struct{}{}
lower := UnicodeLowercase(n)
lower := UnicodeLowercaseNormalized(n)
if lower != lastLower {
node.lowerToReal[lower] = n
lastLower = n
@@ -429,7 +441,7 @@ func (r *defaultRealCaser) realCase(name string) (string, error) {
// Try to find a direct or case match
if _, ok := node.children[comp]; !ok {
comp, ok = node.lowerToReal[UnicodeLowercase(comp)]
comp, ok = node.lowerToReal[UnicodeLowercaseNormalized(comp)]
if !ok {
return "", ErrNotExist
}
+12 -3
View File
@@ -161,7 +161,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
b.Fatal(err)
}
b.Run("rawfs", func(b *testing.B) {
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, fsys, paths, 0, "")
fakefs.reportMetricsPerOp(b)
@@ -174,7 +177,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
Filesystem: fsys,
realCaser: newDefaultRealCaser(fsys),
}
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, 0, "")
fakefs.reportMetricsPerOp(b)
@@ -197,7 +203,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
Filesystem: fsys,
realCaser: newDefaultRealCaser(fsys),
}
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, otherOpEvery, otherOpPath)
fakefs.reportMetricsPerOp(b)
+2 -1
View File
@@ -18,7 +18,8 @@ import (
// reason for existence is the Windows version, which allows creating
// symlinks when non-elevated.
func DebugSymlinkForTestsOnly(oldFs, newFs Filesystem, oldname, newname string) error {
if caseFs, ok := unwrapFilesystem(newFs).(*caseFilesystem); ok {
if fs, ok := unwrapFilesystem(newFs, filesystemWrapperTypeCase); ok {
caseFs := fs.(*caseFilesystem)
if err := caseFs.checkCase(newname); err != nil {
return err
}
+8
View File
@@ -52,3 +52,11 @@ func (fs *errorFilesystem) SameFile(fi1, fi2 FileInfo) bool { return false }
func (fs *errorFilesystem) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
return nil, nil, fs.err
}
func (fs *errorFilesystem) underlying() (Filesystem, bool) {
return nil, false
}
func (fs *errorFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeError
}
+67 -59
View File
@@ -27,7 +27,7 @@ import (
// see readShortAt()
const randomBlockShift = 14 // 128k
// fakefs is a fake filesystem for testing and benchmarking. It has the
// fakeFS is a fake filesystem for testing and benchmarking. It has the
// following properties:
//
// - File metadata is kept in RAM. Specifically, we remember which files and
@@ -37,7 +37,7 @@ const randomBlockShift = 14 // 128k
// - File contents are generated pseudorandomly with just the file name as
// seed. Writes are discarded, other than having the effect of increasing
// the file size. If you only write data that you've read from a file with
// the same name on a different fakefs, you'll never know the difference...
// the same name on a different fakeFS, you'll never know the difference...
//
// - We totally ignore permissions - pretend you are root.
//
@@ -51,10 +51,10 @@ const randomBlockShift = 14 // 128k
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
//
// - Two fakefs:s pointing at the same root path see the same files.
// - Two fakeFS:s pointing at the same root path see the same files.
//
type fakefs struct {
counters fakefsCounters
type fakeFS struct {
counters fakeFSCounters
uri string
mut sync.Mutex
root *fakeEntry
@@ -63,7 +63,7 @@ type fakefs struct {
latency time.Duration
}
type fakefsCounters struct {
type fakeFSCounters struct {
Chmod int64
Lchown int64
Chtimes int64
@@ -81,13 +81,13 @@ type fakefsCounters struct {
}
var (
fakefsMut sync.Mutex
fakefsFs = make(map[string]*fakefs)
fakeFSMut sync.Mutex
fakeFSCache = make(map[string]*fakeFS)
)
func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
fakefsMut.Lock()
defer fakefsMut.Unlock()
func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
fakeFSMut.Lock()
defer fakeFSMut.Unlock()
root := rootURI
var params url.Values
@@ -97,12 +97,12 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
params = uri.Query()
}
if fs, ok := fakefsFs[rootURI]; ok {
if fs, ok := fakeFSCache[rootURI]; ok {
// Already have an fs at this path
return fs
}
fs := &fakefs{
fs := &fakeFS{
uri: "fake://" + rootURI,
root: &fakeEntry{
name: "/",
@@ -157,7 +157,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
// the filesystem initially.
fs.latency, _ = time.ParseDuration(params.Get("latency"))
fakefsFs[root] = fs
fakeFSCache[root] = fs
return fs
}
@@ -183,10 +183,10 @@ type fakeEntry struct {
content []byte
}
func (fs *fakefs) entryForName(name string) *fakeEntry {
func (fs *fakeFS) entryForName(name string) *fakeEntry {
// bug: lookup doesn't work through symlinks.
if fs.insens {
name = UnicodeLowercase(name)
name = UnicodeLowercaseNormalized(name)
}
name = filepath.ToSlash(name)
@@ -210,7 +210,7 @@ func (fs *fakefs) entryForName(name string) *fakeEntry {
return entry
}
func (fs *fakefs) Chmod(name string, mode FileMode) error {
func (fs *fakeFS) Chmod(name string, mode FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Chmod++
@@ -223,7 +223,7 @@ func (fs *fakefs) Chmod(name string, mode FileMode) error {
return nil
}
func (fs *fakefs) Lchown(name string, uid, gid int) error {
func (fs *fakeFS) Lchown(name string, uid, gid int) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Lchown++
@@ -237,7 +237,7 @@ func (fs *fakefs) Lchown(name string, uid, gid int) error {
return nil
}
func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
func (fs *fakeFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Chtimes++
@@ -250,7 +250,7 @@ func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
return nil
}
func (fs *fakefs) create(name string) (*fakeEntry, error) {
func (fs *fakeFS) create(name string) (*fakeEntry, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Create++
@@ -285,7 +285,7 @@ func (fs *fakefs) create(name string) (*fakeEntry, error) {
}
if fs.insens {
base = UnicodeLowercase(base)
base = UnicodeLowercaseNormalized(base)
}
if fs.withContent {
@@ -296,7 +296,7 @@ func (fs *fakefs) create(name string) (*fakeEntry, error) {
return new, nil
}
func (fs *fakefs) Create(name string) (File, error) {
func (fs *fakeFS) Create(name string) (File, error) {
entry, err := fs.create(name)
if err != nil {
return nil, err
@@ -307,7 +307,7 @@ func (fs *fakefs) Create(name string) (File, error) {
return &fakeFile{fakeEntry: entry}, nil
}
func (fs *fakefs) CreateSymlink(target, name string) error {
func (fs *fakeFS) CreateSymlink(target, name string) error {
entry, err := fs.create(name)
if err != nil {
return err
@@ -317,7 +317,7 @@ func (fs *fakefs) CreateSymlink(target, name string) error {
return nil
}
func (fs *fakefs) DirNames(name string) ([]string, error) {
func (fs *fakeFS) DirNames(name string) ([]string, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.DirNames++
@@ -336,7 +336,7 @@ func (fs *fakefs) DirNames(name string) ([]string, error) {
return names, nil
}
func (fs *fakefs) Lstat(name string) (FileInfo, error) {
func (fs *fakeFS) Lstat(name string) (FileInfo, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Lstat++
@@ -355,7 +355,7 @@ func (fs *fakefs) Lstat(name string) (FileInfo, error) {
return info, nil
}
func (fs *fakefs) Mkdir(name string, perm FileMode) error {
func (fs *fakeFS) Mkdir(name string, perm FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Mkdir++
@@ -373,7 +373,7 @@ func (fs *fakefs) Mkdir(name string, perm FileMode) error {
return os.ErrExist
}
if fs.insens {
key = UnicodeLowercase(key)
key = UnicodeLowercaseNormalized(key)
}
if _, ok := entry.children[key]; ok {
return os.ErrExist
@@ -389,7 +389,7 @@ func (fs *fakefs) Mkdir(name string, perm FileMode) error {
return nil
}
func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
func (fs *fakeFS) MkdirAll(name string, perm FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.MkdirAll++
@@ -402,7 +402,7 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
for _, comp := range comps {
key := comp
if fs.insens {
key = UnicodeLowercase(key)
key = UnicodeLowercaseNormalized(key)
}
next, ok := entry.children[key]
@@ -426,7 +426,7 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
return nil
}
func (fs *fakefs) Open(name string) (File, error) {
func (fs *fakeFS) Open(name string) (File, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Open++
@@ -443,7 +443,7 @@ func (fs *fakefs) Open(name string) (File, error) {
return &fakeFile{fakeEntry: entry}, nil
}
func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
func (fs *fakeFS) OpenFile(name string, flags int, mode FileMode) (File, error) {
if flags&os.O_CREATE == 0 {
return fs.Open(name)
}
@@ -465,7 +465,7 @@ func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error)
}
if fs.insens {
key = UnicodeLowercase(key)
key = UnicodeLowercaseNormalized(key)
}
if flags&os.O_EXCL != 0 {
if _, ok := entry.children[key]; ok {
@@ -486,7 +486,7 @@ func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error)
return &fakeFile{fakeEntry: newEntry}, nil
}
func (fs *fakefs) ReadSymlink(name string) (string, error) {
func (fs *fakeFS) ReadSymlink(name string) (string, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.ReadSymlink++
@@ -501,14 +501,14 @@ func (fs *fakefs) ReadSymlink(name string) (string, error) {
return entry.dest, nil
}
func (fs *fakefs) Remove(name string) error {
func (fs *fakeFS) Remove(name string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Remove++
time.Sleep(fs.latency)
if fs.insens {
name = UnicodeLowercase(name)
name = UnicodeLowercaseNormalized(name)
}
entry := fs.entryForName(name)
@@ -524,19 +524,19 @@ func (fs *fakefs) Remove(name string) error {
return nil
}
func (fs *fakefs) RemoveAll(name string) error {
func (fs *fakeFS) RemoveAll(name string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.RemoveAll++
time.Sleep(fs.latency)
if fs.insens {
name = UnicodeLowercase(name)
name = UnicodeLowercaseNormalized(name)
}
entry := fs.entryForName(filepath.Dir(name))
if entry == nil {
return nil // all tested real systems exibit this behaviour
return nil // all tested real systems exhibit this behaviour
}
// RemoveAll is easy when the file system uses garbage collection under
@@ -545,7 +545,7 @@ func (fs *fakefs) RemoveAll(name string) error {
return nil
}
func (fs *fakefs) Rename(oldname, newname string) error {
func (fs *fakeFS) Rename(oldname, newname string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Rename++
@@ -555,8 +555,8 @@ func (fs *fakefs) Rename(oldname, newname string) error {
newKey := filepath.Base(newname)
if fs.insens {
oldKey = UnicodeLowercase(oldKey)
newKey = UnicodeLowercase(newKey)
oldKey = UnicodeLowercaseNormalized(oldKey)
newKey = UnicodeLowercaseNormalized(newKey)
}
p0 := fs.entryForName(filepath.Dir(oldname))
@@ -595,63 +595,63 @@ func (fs *fakefs) Rename(oldname, newname string) error {
return nil
}
func (fs *fakefs) Stat(name string) (FileInfo, error) {
func (fs *fakeFS) Stat(name string) (FileInfo, error) {
return fs.Lstat(name)
}
func (fs *fakefs) SymlinksSupported() bool {
func (fs *fakeFS) SymlinksSupported() bool {
return false
}
func (fs *fakefs) Walk(name string, walkFn WalkFunc) error {
func (fs *fakeFS) Walk(name string, walkFn WalkFunc) error {
return errors.New("not implemented")
}
func (fs *fakefs) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
func (fs *fakeFS) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
return nil, nil, ErrWatchNotSupported
}
func (fs *fakefs) Hide(name string) error {
func (fs *fakeFS) Hide(name string) error {
return nil
}
func (fs *fakefs) Unhide(name string) error {
func (fs *fakeFS) Unhide(name string) error {
return nil
}
func (fs *fakefs) Glob(pattern string) ([]string, error) {
func (fs *fakeFS) Glob(pattern string) ([]string, error) {
// gnnh we don't seem to actually require this in practice
return nil, errors.New("not implemented")
}
func (fs *fakefs) Roots() ([]string, error) {
func (fs *fakeFS) Roots() ([]string, error) {
return []string{"/"}, nil
}
func (fs *fakefs) Usage(name string) (Usage, error) {
func (fs *fakeFS) Usage(name string) (Usage, error) {
return Usage{}, errors.New("not implemented")
}
func (fs *fakefs) Type() FilesystemType {
func (fs *fakeFS) Type() FilesystemType {
return FilesystemTypeFake
}
func (fs *fakefs) URI() string {
func (fs *fakeFS) URI() string {
return fs.uri
}
func (fs *fakefs) Options() []Option {
func (fs *fakeFS) Options() []Option {
return nil
}
func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
func (fs *fakeFS) SameFile(fi1, fi2 FileInfo) bool {
// BUG: real systems base file sameness on path, inodes, etc
// we try our best, but FileInfo just doesn't have enough data
// so there be false positives, especially on Windows
// where ModTime is not that precise
var ok bool
if fs.insens {
ok = UnicodeLowercase(fi1.Name()) == UnicodeLowercase(fi2.Name())
ok = UnicodeLowercaseNormalized(fi1.Name()) == UnicodeLowercaseNormalized(fi2.Name())
} else {
ok = fi1.Name() == fi2.Name()
}
@@ -659,17 +659,25 @@ func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
}
func (fs *fakefs) resetCounters() {
func (fs *fakeFS) underlying() (Filesystem, bool) {
return nil, false
}
func (fs *fakeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
func (fs *fakeFS) resetCounters() {
fs.mut.Lock()
fs.counters = fakefsCounters{}
fs.counters = fakeFSCounters{}
fs.mut.Unlock()
}
func (fs *fakefs) reportMetricsPerOp(b *testing.B) {
func (fs *fakeFS) reportMetricsPerOp(b *testing.B) {
fs.reportMetricsPer(b, 1, "op")
}
func (fs *fakefs) reportMetricsPer(b *testing.B, divisor float64, unit string) {
func (fs *fakeFS) reportMetricsPer(b *testing.B, divisor float64, unit string) {
fs.mut.Lock()
defer fs.mut.Unlock()
b.ReportMetric(float64(fs.counters.Lstat)/divisor/float64(b.N), "Lstat/"+unit)
+5 -5
View File
@@ -21,7 +21,7 @@ import (
)
func TestFakeFS(t *testing.T) {
// Test some basic aspects of the fakefs
// Test some basic aspects of the fakeFS
fs := newFakeFilesystem("/foo/bar/baz")
@@ -131,7 +131,7 @@ func TestFakeFS(t *testing.T) {
}
func testFakeFSRead(t *testing.T, fs Filesystem) {
// Test some basic aspects of the fakefs
// Test some basic aspects of the fakeFS
// Create
fd, _ := fs.Create("test")
defer fd.Close()
@@ -201,7 +201,7 @@ func TestFakeFSCaseSensitive(t *testing.T) {
{"FileName", testFakeFSFileName},
}
var filesystems = []testFS{
{"fakefs", newFakeFilesystem("/foo")},
{"fakeFS", newFakeFilesystem("/foo")},
}
testDir, sensitive := createTestDir(t)
@@ -237,7 +237,7 @@ func TestFakeFSCaseInsensitive(t *testing.T) {
}
var filesystems = []testFS{
{"fakefs", newFakeFilesystem("/foobar?insens=true")},
{"fakeFS", newFakeFilesystem("/foobar?insens=true")},
}
testDir, sensitive := createTestDir(t)
@@ -891,7 +891,7 @@ func testFakeFSCreateInsens(t *testing.T, fs Filesystem) {
t.Errorf("name of created file \"fOo\" is %s", fd2.Name())
}
// one would expect DirNames to show the last variant, but in fact it shows
// one would expect DirNames to show the last wrapperType, but in fact it shows
// the original one
assertDir(t, fs, "/", []string{"FOO"})
}
+24 -11
View File
@@ -16,6 +16,17 @@ import (
"time"
)
type filesystemWrapperType int32
const (
filesystemWrapperTypeNone filesystemWrapperType = iota
filesystemWrapperTypeMtime
filesystemWrapperTypeCase
filesystemWrapperTypeError
filesystemWrapperTypeWalk
filesystemWrapperTypeLog
)
// The Filesystem interface abstracts access to the file system.
type Filesystem interface {
Chmod(name string, mode FileMode) error
@@ -49,6 +60,10 @@ type Filesystem interface {
URI() string
Options() []Option
SameFile(fi1, fi2 FileInfo) bool
// Used for unwrapping things
underlying() (Filesystem, bool)
wrapperType() filesystemWrapperType
}
// The File interface abstracts access to a regular file, being a somewhat
@@ -284,18 +299,16 @@ func wrapFilesystem(fs Filesystem, wrapFn func(Filesystem) Filesystem) Filesyste
return fs
}
// unwrapFilesystem removes "wrapping" filesystems to expose the underlying filesystem.
func unwrapFilesystem(fs Filesystem) Filesystem {
// unwrapFilesystem removes "wrapping" filesystems to expose the filesystem of the requested wrapperType, if it exists.
func unwrapFilesystem(fs Filesystem, wrapperType filesystemWrapperType) (Filesystem, bool) {
var ok bool
for {
switch sfs := fs.(type) {
case *logFilesystem:
fs = sfs.Filesystem
case *walkFilesystem:
fs = sfs.Filesystem
case *mtimeFS:
fs = sfs.Filesystem
default:
return sfs
if fs.wrapperType() == wrapperType {
return fs, true
}
fs, ok = fs.underlying()
if !ok {
return nil, false
}
}
}
+7 -3
View File
@@ -10,12 +10,16 @@ import (
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/text/unicode/norm"
)
func UnicodeLowercase(s string) string {
// UnicodeLowercaseNormalized returns the Unicode lower case variant of s,
// having also normalized it to normalization form C.
func UnicodeLowercaseNormalized(s string) string {
i := firstCaseChange(s)
if i == -1 {
return s
return norm.NFC.String(s)
}
var rs strings.Builder
@@ -28,7 +32,7 @@ func UnicodeLowercase(s string) string {
for _, r := range s[i:] {
rs.WriteRune(unicode.ToLower(unicode.ToUpper(r)))
}
return rs.String()
return norm.NFC.String(rs.String())
}
// Byte index of the first rune r s.t. lower(upper(r)) != r.
+8 -5
View File
@@ -44,13 +44,16 @@ var caseCases = [][2]string{
{"チャーハン", "チャーハン"},
// Some special Unicode characters, however, are folded by OSes.
{"\u212A", "k"},
// Folding renormalizes to NFC
{"A\xCC\x88", "\xC3\xA4"}, // ä
{"a\xCC\x88", "\xC3\xA4"}, // ä
}
func TestUnicodeLowercase(t *testing.T) {
func TestUnicodeLowercaseNormalized(t *testing.T) {
for _, tc := range caseCases {
res := UnicodeLowercase(tc[0])
res := UnicodeLowercaseNormalized(tc[0])
if res != tc[1] {
t.Errorf("UnicodeLowercase(%q) => %q, expected %q", tc[0], res, tc[1])
t.Errorf("UnicodeLowercaseNormalized(%q) => %q, expected %q", tc[0], res, tc[1])
}
}
}
@@ -60,7 +63,7 @@ func BenchmarkUnicodeLowercaseMaybeChange(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, s := range caseCases {
UnicodeLowercase(s[0])
UnicodeLowercaseNormalized(s[0])
}
}
}
@@ -70,7 +73,7 @@ func BenchmarkUnicodeLowercaseNoChange(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, s := range caseCases {
UnicodeLowercase(s[1])
UnicodeLowercaseNormalized(s[1])
}
}
}
+8
View File
@@ -163,3 +163,11 @@ func (fs *logFilesystem) Usage(name string) (Usage, error) {
l.Debugln(getCaller(), fs.Type(), fs.URI(), "Usage", name, usage, err)
return usage, err
}
func (fs *logFilesystem) underlying() (Filesystem, bool) {
return fs.Filesystem, true
}
func (fs *logFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeLog
}
+56 -37
View File
@@ -7,6 +7,7 @@
package fs
import (
"errors"
"time"
)
@@ -69,14 +70,14 @@ func (f *mtimeFS) Stat(name string) (FileInfo, error) {
return nil, err
}
real, virtual, err := f.load(name)
mtimeMapping, err := f.load(name)
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
@@ -89,14 +90,14 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) {
return nil, err
}
real, virtual, err := f.load(name)
mtimeMapping, err := f.load(name)
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
@@ -106,15 +107,15 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) {
func (f *mtimeFS) Walk(root string, walkFn WalkFunc) error {
return f.Filesystem.Walk(root, func(path string, info FileInfo, err error) error {
if info != nil {
real, virtual, loadErr := f.load(path)
mtimeMapping, loadErr := f.load(path)
if loadErr != nil && err == nil {
// The iterator gets to deal with the error
err = loadErr
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
}
@@ -146,12 +147,17 @@ func (f *mtimeFS) OpenFile(name string, flags int, mode FileMode) (File, error)
return mtimeFile{fd, f}, nil
}
// "real" is the on disk timestamp
// "virtual" is what want the timestamp to be
func (f *mtimeFS) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *mtimeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeMtime
}
func (f *mtimeFS) save(name string, real, virtual time.Time) {
if f.caseInsensitive {
name = UnicodeLowercase(name)
name = UnicodeLowercaseNormalized(name)
}
if real.Equal(virtual) {
@@ -161,32 +167,31 @@ func (f *mtimeFS) save(name string, real, virtual time.Time) {
return
}
mtime := dbMtime{
real: real,
virtual: virtual,
mtime := MtimeMapping{
Real: real,
Virtual: virtual,
}
bs, _ := mtime.Marshal() // Can't fail
f.db.PutBytes(name, bs)
}
func (f *mtimeFS) load(name string) (real, virtual time.Time, err error) {
func (f *mtimeFS) load(name string) (MtimeMapping, error) {
if f.caseInsensitive {
name = UnicodeLowercase(name)
name = UnicodeLowercaseNormalized(name)
}
data, exists, err := f.db.Bytes(name)
if err != nil {
return time.Time{}, time.Time{}, err
return MtimeMapping{}, err
} else if !exists {
return time.Time{}, time.Time{}, nil
return MtimeMapping{}, nil
}
var mtime dbMtime
var mtime MtimeMapping
if err := mtime.Unmarshal(data); err != nil {
return time.Time{}, time.Time{}, err
return MtimeMapping{}, err
}
return mtime.real, mtime.virtual, nil
return mtime, nil
}
// The mtimeFileInfo is an os.FileInfo that lies about the ModTime().
@@ -211,43 +216,57 @@ func (f mtimeFile) Stat() (FileInfo, error) {
return nil, err
}
real, virtual, err := f.fs.load(f.Name())
mtimeMapping, err := f.fs.load(f.Name())
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
return info, nil
}
// Used by copyRange to unwrap to the real file and access SyscallConn
func (f mtimeFile) unwrap() File {
return f.File
}
// The dbMtime is our database representation
type dbMtime struct {
real time.Time
virtual time.Time
// MtimeMapping represents the mapping as stored in the database
type MtimeMapping struct {
// "Real" is the on disk timestamp
Real time.Time `json:"real"`
// "Virtual" is what want the timestamp to be
Virtual time.Time `json:"virtual"`
}
func (t *dbMtime) Marshal() ([]byte, error) {
bs0, _ := t.real.MarshalBinary()
bs1, _ := t.virtual.MarshalBinary()
func (t *MtimeMapping) Marshal() ([]byte, error) {
bs0, _ := t.Real.MarshalBinary()
bs1, _ := t.Virtual.MarshalBinary()
return append(bs0, bs1...), nil
}
func (t *dbMtime) Unmarshal(bs []byte) error {
if err := t.real.UnmarshalBinary(bs[:len(bs)/2]); err != nil {
func (t *MtimeMapping) Unmarshal(bs []byte) error {
if err := t.Real.UnmarshalBinary(bs[:len(bs)/2]); err != nil {
return err
}
if err := t.virtual.UnmarshalBinary(bs[len(bs)/2:]); err != nil {
if err := t.Virtual.UnmarshalBinary(bs[len(bs)/2:]); err != nil {
return err
}
return nil
}
func GetMtimeMapping(fs Filesystem, file string) (MtimeMapping, error) {
fs, ok := unwrapFilesystem(fs, filesystemWrapperTypeMtime)
if !ok {
return MtimeMapping{}, errors.New("failed to unwrap")
}
mtimeFs, ok := fs.(*mtimeFS)
if !ok {
return MtimeMapping{}, errors.New("unwrapping failed")
}
return mtimeFs.load(file)
}
+8
View File
@@ -149,3 +149,11 @@ func (f *walkFilesystem) Walk(root string, walkFn WalkFunc) error {
}
return f.walk(root, info, walkFn, ancestors)
}
func (f *walkFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *walkFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeWalk
}
+1
View File
@@ -1,6 +1,7 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/logger.go --fake-name Recorder . Recorder
// Package logger implements a standardized logger with callback functionality
+3 -3
View File
@@ -154,17 +154,17 @@ func (f *fakeConnection) sendIndexUpdate() {
f.model.IndexUpdate(f.id, f.folder, toSend)
}
func addFakeConn(m *testModel, dev protocol.DeviceID) *fakeConnection {
func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConnection {
fc := newFakeConnection(dev, m)
m.AddConnection(fc, protocol.Hello{})
m.ClusterConfig(dev, protocol.ClusterConfig{
Folders: []protocol.Folder{
{
ID: "default",
ID: folderID,
Devices: []protocol.Device{
{ID: myID},
{ID: device1},
{ID: dev},
},
},
},
+17 -17
View File
@@ -473,7 +473,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
f.setState(FolderScanning)
f.clearScanErrors(subDirs)
batch := newFileInfoBatch(func(fs []protocol.FileInfo) error {
batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
if err := f.getHealthErrorWithoutIgnores(); err != nil {
l.Debugf("Stopping scan of folder %s due to: %s", f.Description(), err)
return err
@@ -500,7 +500,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
return err
}
if err := batch.flush(); err != nil {
if err := batch.Flush(); err != nil {
return err
}
@@ -519,7 +519,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
return err
}
if err := batch.flush(); err != nil {
if err := batch.Flush(); err != nil {
return err
}
@@ -529,7 +529,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
type batchAppendFunc func(protocol.FileInfo, *db.Snapshot) bool
func (f *folder) scanSubdirsBatchAppendFunc(batch *fileInfoBatch) batchAppendFunc {
func (f *folder) scanSubdirsBatchAppendFunc(batch *db.FileInfoBatch) batchAppendFunc {
// Resolve items which are identical with the global state.
switch f.Type {
case config.FolderTypeReceiveOnly:
@@ -550,7 +550,7 @@ func (f *folder) scanSubdirsBatchAppendFunc(batch *fileInfoBatch) batchAppendFun
l.Debugf("%v scanning: Marking item as not locally changed", f, fi)
fi.LocalFlags &^= protocol.FlagLocalReceiveOnly
}
batch.append(fi)
batch.Append(fi)
return true
}
case config.FolderTypeReceiveEncrypted:
@@ -567,18 +567,18 @@ func (f *folder) scanSubdirsBatchAppendFunc(batch *fileInfoBatch) batchAppendFun
// Any local change must not be sent as index entry to
// remotes and show up as an error in the UI.
fi.LocalFlags = protocol.FlagLocalReceiveOnly
batch.append(fi)
batch.Append(fi)
return true
}
default:
return func(fi protocol.FileInfo, _ *db.Snapshot) bool {
batch.append(fi)
batch.Append(fi)
return true
}
}
}
func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *fileInfoBatch, batchAppend batchAppendFunc) (int, error) {
func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *db.FileInfoBatch, batchAppend batchAppendFunc) (int, error) {
changes := 0
snap, err := f.dbSnapshot()
if err != nil {
@@ -621,7 +621,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *fileInfoBatch
continue
}
if err := batch.flushIfFull(); err != nil {
if err := batch.FlushIfFull(); err != nil {
// Prevent a race between the scan aborting due to context
// cancellation and releasing the snapshot in defer here.
scanCancel()
@@ -648,7 +648,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *fileInfoBatch
return changes, nil
}
func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *fileInfoBatch, batchAppend batchAppendFunc) (int, error) {
func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileInfoBatch, batchAppend batchAppendFunc) (int, error) {
var toIgnore []db.FileInfoTruncated
ignoredParent := ""
changes := 0
@@ -670,7 +670,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *fileInfoB
file := fi.(db.FileInfoTruncated)
if err := batch.flushIfFull(); err != nil {
if err := batch.FlushIfFull(); err != nil {
iterError = err
return false
}
@@ -682,7 +682,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *fileInfoB
if batchAppend(nf, snap) {
changes++
}
if err := batch.flushIfFull(); err != nil {
if err := batch.FlushIfFull(); err != nil {
iterError = err
return false
}
@@ -775,7 +775,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *fileInfoB
if batchAppend(nf, snap) {
changes++
}
if iterError = batch.flushIfFull(); iterError != nil {
if iterError = batch.FlushIfFull(); iterError != nil {
break
}
}
@@ -1206,7 +1206,7 @@ func (f *folder) handleForcedRescans() error {
return nil
}
batch := newFileInfoBatch(func(fs []protocol.FileInfo) error {
batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
f.fset.Update(protocol.LocalDeviceID, fs)
return nil
})
@@ -1218,7 +1218,7 @@ func (f *folder) handleForcedRescans() error {
defer snap.Release()
for _, path := range paths {
if err := batch.flushIfFull(); err != nil {
if err := batch.FlushIfFull(); err != nil {
return err
}
@@ -1227,10 +1227,10 @@ func (f *folder) handleForcedRescans() error {
continue
}
fi.SetMustRescan()
batch.append(fi)
batch.Append(fi)
}
if err = batch.flush(); err != nil {
if err = batch.Flush(); err != nil {
return err
}
+4 -4
View File
@@ -41,7 +41,7 @@ func (f *receiveEncryptedFolder) revert() error {
f.setState(FolderScanning)
defer f.setState(FolderIdle)
batch := newFileInfoBatch(func(fs []protocol.FileInfo) error {
batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
f.updateLocalsFromScanning(fs)
return nil
})
@@ -54,7 +54,7 @@ func (f *receiveEncryptedFolder) revert() error {
var iterErr error
var dirs []string
snap.WithHaveTruncated(protocol.LocalDeviceID, func(intf protocol.FileIntf) bool {
if iterErr = batch.flushIfFull(); iterErr != nil {
if iterErr = batch.FlushIfFull(); iterErr != nil {
return false
}
@@ -81,7 +81,7 @@ func (f *receiveEncryptedFolder) revert() error {
// item should still not be sent in index updates. However being
// deleted, it will not show up as an unexpected file in the UI
// anymore.
batch.append(fi)
batch.Append(fi)
return true
})
@@ -91,7 +91,7 @@ func (f *receiveEncryptedFolder) revert() error {
if iterErr != nil {
return iterErr
}
return batch.flush()
return batch.Flush()
}
func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapshot) {
+9 -18
View File
@@ -82,8 +82,10 @@ func (f *receiveOnlyFolder) revert() error {
scanChan: scanChan,
}
batch := make([]protocol.FileInfo, 0, maxBatchSizeFiles)
batchSizeBytes := 0
batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error {
f.updateLocalsFromScanning(files)
return nil
})
snap, err := f.dbSnapshot()
if err != nil {
return err
@@ -124,21 +126,12 @@ func (f *receiveOnlyFolder) revert() error {
fi.Version = protocol.Vector{}
}
batch = append(batch, fi)
batchSizeBytes += fi.ProtoSize()
batch.Append(fi)
_ = batch.FlushIfFull()
if len(batch) >= maxBatchSizeFiles || batchSizeBytes >= maxBatchSizeBytes {
f.updateLocalsFromScanning(batch)
batch = batch[:0]
batchSizeBytes = 0
}
return true
})
if len(batch) > 0 {
f.updateLocalsFromScanning(batch)
}
batch = batch[:0]
batchSizeBytes = 0
_ = batch.Flush()
// Handle any queued directories
deleted, err := delQueue.flush(snap)
@@ -147,7 +140,7 @@ func (f *receiveOnlyFolder) revert() error {
}
now := time.Now()
for _, dir := range deleted {
batch = append(batch, protocol.FileInfo{
batch.Append(protocol.FileInfo{
Name: dir,
Type: protocol.FileInfoTypeDirectory,
ModifiedS: now.Unix(),
@@ -156,9 +149,7 @@ func (f *receiveOnlyFolder) revert() error {
Version: protocol.Vector{},
})
}
if len(batch) > 0 {
f.updateLocalsFromScanning(batch)
}
_ = batch.Flush()
// We will likely have changed our local index, but that won't trigger a
// pull by itself. Make sure we schedule one so that we start
+6 -6
View File
@@ -43,7 +43,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
// Send and index update for the known stuff
m.Index(device1, "ro", knownFiles)
must(t, m.Index(device1, "ro", knownFiles))
f.updateLocalsFromScanning(knownFiles)
size := globalSize(t, m, "ro")
@@ -119,7 +119,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
// Send and index update for the known stuff
m.Index(device1, "ro", knownFiles)
must(t, m.Index(device1, "ro", knownFiles))
f.updateLocalsFromScanning(knownFiles)
// Scan the folder.
@@ -208,7 +208,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
// Send an index update for the known stuff
m.Index(device1, "ro", knownFiles)
must(t, m.Index(device1, "ro", knownFiles))
f.updateLocalsFromScanning(knownFiles)
// Scan the folder.
@@ -277,7 +277,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
// Send an index update for the known stuff
m.Index(device1, "ro", knownFiles)
must(t, m.Index(device1, "ro", knownFiles))
f.updateLocalsFromScanning(knownFiles)
// Scan the folder.
@@ -341,7 +341,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
// Send an index update for the known stuff
m.Index(device1, "ro", knownFiles)
must(t, m.Index(device1, "ro", knownFiles))
f.updateLocalsFromScanning(knownFiles)
// Scan the folder.
@@ -396,7 +396,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
return true
})
snap.Release()
m.IndexUpdate(device1, "ro", files)
must(t, m.IndexUpdate(device1, "ro", files))
// Ensure the pull to resolve conflicts (content identical) happened
must(t, f.doInSync(func() error {
+13 -19
View File
@@ -37,7 +37,7 @@ func (f *sendOnlyFolder) PullErrors() []FileError {
// pull checks need for files that only differ by metadata (no changes on disk)
func (f *sendOnlyFolder) pull() (bool, error) {
batch := newFileInfoBatch(func(files []protocol.FileInfo) error {
batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error {
f.updateLocalsFromPulling(files)
return nil
})
@@ -48,13 +48,13 @@ func (f *sendOnlyFolder) pull() (bool, error) {
}
defer snap.Release()
snap.WithNeed(protocol.LocalDeviceID, func(intf protocol.FileIntf) bool {
batch.flushIfFull()
batch.FlushIfFull()
file := intf.(protocol.FileInfo)
if f.ignores.ShouldIgnore(intf.FileName()) {
file.SetIgnored()
batch.append(file)
batch.Append(file)
l.Debugln(f, "Handling ignored file", file)
return true
}
@@ -63,7 +63,7 @@ func (f *sendOnlyFolder) pull() (bool, error) {
if !ok {
if intf.IsInvalid() {
// Global invalid file just exists for need accounting
batch.append(file)
batch.Append(file)
} else if intf.IsDeleted() {
l.Debugln("Should never get a deleted file as needed when we don't have it")
f.evLogger.Log(events.Failure, "got deleted file that doesn't exist locally as needed when pulling on send-only")
@@ -75,13 +75,13 @@ func (f *sendOnlyFolder) pull() (bool, error) {
return true
}
batch.append(file)
batch.Append(file)
l.Debugln(f, "Merging versions of identical file", file)
return true
})
batch.flush()
batch.Flush()
return true, nil
}
@@ -96,8 +96,10 @@ func (f *sendOnlyFolder) override() error {
f.setState(FolderScanning)
defer f.setState(FolderIdle)
batch := make([]protocol.FileInfo, 0, maxBatchSizeFiles)
batchSizeBytes := 0
batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error {
f.updateLocalsFromScanning(files)
return nil
})
snap, err := f.dbSnapshot()
if err != nil {
return err
@@ -105,11 +107,7 @@ func (f *sendOnlyFolder) override() error {
defer snap.Release()
snap.WithNeed(protocol.LocalDeviceID, func(fi protocol.FileIntf) bool {
need := fi.(protocol.FileInfo)
if len(batch) == maxBatchSizeFiles || batchSizeBytes > maxBatchSizeBytes {
f.updateLocalsFromScanning(batch)
batch = batch[:0]
batchSizeBytes = 0
}
_ = batch.FlushIfFull()
have, ok := snap.Get(protocol.LocalDeviceID, need.Name)
// Don't override files that are in a bad state (ignored,
@@ -126,12 +124,8 @@ func (f *sendOnlyFolder) override() error {
need = have
}
need.Sequence = 0
batch = append(batch, need)
batchSizeBytes += need.ProtoSize()
batch.Append(need)
return true
})
if len(batch) > 0 {
f.updateLocalsFromScanning(batch)
}
return nil
return batch.Flush()
}
+8 -16
View File
@@ -357,11 +357,6 @@ func (f *sendReceiveFolder) processNeeded(snap *db.Snapshot, dbUpdateChan chan<-
changed--
}
case file.IsInvalid():
// Global invalid file just exists for need accounting
l.Debugln(f, "Handling global invalid item", file)
dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate}
case file.IsDeleted():
if file.IsDirectory() {
// Perform directory deletions at the end, as we may have
@@ -1668,15 +1663,12 @@ func (f *sendReceiveFolder) Jobs(page, perpage int) ([]string, []string, int) {
func (f *sendReceiveFolder) dbUpdaterRoutine(dbUpdateChan <-chan dbUpdateJob) {
const maxBatchTime = 2 * time.Second
batch := newFileInfoBatch(nil)
tick := time.NewTicker(maxBatchTime)
defer tick.Stop()
changedDirs := make(map[string]struct{})
found := false
var lastFile protocol.FileInfo
batch.flushFn = func(files []protocol.FileInfo) error {
tick := time.NewTicker(maxBatchTime)
defer tick.Stop()
batch := db.NewFileInfoBatch(func(files []protocol.FileInfo) error {
// sync directories
for dir := range changedDirs {
delete(changedDirs, dir)
@@ -1703,7 +1695,7 @@ func (f *sendReceiveFolder) dbUpdaterRoutine(dbUpdateChan <-chan dbUpdateJob) {
}
return nil
}
})
recvEnc := f.Type == config.FolderTypeReceiveEncrypted
loop:
@@ -1736,16 +1728,16 @@ loop:
job.file.Sequence = 0
batch.append(job.file)
batch.Append(job.file)
batch.flushIfFull()
batch.FlushIfFull()
case <-tick.C:
batch.flush()
batch.Flush()
}
}
batch.flush()
batch.Flush()
}
// pullScannerRoutine aggregates paths to be scanned after pulling. The scan is
+1 -1
View File
@@ -1297,7 +1297,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
if !ok {
t.Fatal("file missing")
}
m.Index(device1, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}})
must(t, m.Index(device1, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}}))
scanChan := make(chan string)
+1
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/folderSummaryService.go --fake-name FolderSummaryService . FolderSummaryService
package model
+26 -12
View File
@@ -118,15 +118,15 @@ func (s *indexSender) pause() {
// returns the highest sent sequence number.
func (s *indexSender) sendIndexTo(ctx context.Context) error {
initial := s.prevSequence == 0
batch := newFileInfoBatch(nil)
batch.flushFn = func(fs []protocol.FileInfo) error {
l.Debugf("%v: Sending %d files (<%d bytes)", s, len(batch.infos), batch.size)
batch := db.NewFileInfoBatch(nil)
batch.SetFlushFunc(func(fs []protocol.FileInfo) error {
l.Debugf("%v: Sending %d files (<%d bytes)", s, len(fs), batch.Size())
if initial {
initial = false
return s.conn.Index(ctx, s.folder, fs)
}
return s.conn.IndexUpdate(ctx, s.folder, fs)
}
})
var err error
var f protocol.FileInfo
@@ -140,8 +140,8 @@ func (s *indexSender) sendIndexTo(ctx context.Context) error {
// This is to make sure that renames (which is an add followed by a delete) land in the same batch.
// Even if the batch is full, we allow a last delete to slip in, we do this by making sure that
// the batch ends with a non-delete, or that the last item in the batch is already a delete
if batch.full() && (!fi.IsDeleted() || previousWasDelete) {
if err = batch.flush(); err != nil {
if batch.Full() && (!fi.IsDeleted() || previousWasDelete) {
if err = batch.Flush(); err != nil {
return false
}
}
@@ -181,14 +181,14 @@ func (s *indexSender) sendIndexTo(ctx context.Context) error {
previousWasDelete = f.IsDeleted()
batch.append(f)
batch.Append(f)
return true
})
if err != nil {
return err
}
err = batch.flush()
err = batch.Flush()
// True if there was nothing to be sent
if f.Sequence == 0 {
@@ -247,6 +247,7 @@ func newIndexSenderRegistry(conn protocol.Connection, closed chan struct{}, sup
func (r *indexSenderRegistry) add(folder config.FolderConfiguration, fset *db.FileSet, startInfo *indexSenderStartInfo) {
r.mut.Lock()
r.addLocked(folder, fset, startInfo)
l.Debugf("Started index sender for device %v and folder %v", r.deviceID.Short(), folder.ID)
r.mut.Unlock()
}
@@ -336,15 +337,17 @@ func (r *indexSenderRegistry) addLocked(folder config.FolderConfiguration, fset
// addPending stores the given info to start an index sender once resume is called
// for this folder.
// If an index sender is already running, it will be stopped.
func (r *indexSenderRegistry) addPending(folder config.FolderConfiguration, startInfo *indexSenderStartInfo) {
func (r *indexSenderRegistry) addPending(folder string, startInfo *indexSenderStartInfo) {
r.mut.Lock()
defer r.mut.Unlock()
if is, ok := r.indexSenders[folder.ID]; ok {
if is, ok := r.indexSenders[folder]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder.ID)
delete(r.indexSenders, folder)
l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.deviceID.Short(), folder)
}
r.startInfos[folder.ID] = startInfo
r.startInfos[folder] = startInfo
l.Debugf("Pending index sender for device %v and folder %v", r.deviceID.Short(), folder)
}
// remove stops a running index sender or removes one pending to be started.
@@ -358,6 +361,7 @@ func (r *indexSenderRegistry) remove(folder string) {
delete(r.indexSenders, folder)
}
delete(r.startInfos, folder)
l.Debugf("Removed index sender for device %v and folder %v", r.deviceID.Short(), folder)
}
// removeAllExcept stops all running index senders and removes those pending to be started,
@@ -371,11 +375,13 @@ func (r *indexSenderRegistry) removeAllExcept(except map[string]struct{}) {
if _, ok := except[folder]; !ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder)
l.Debugf("Removed index sender for device %v and folder %v (removeAllExcept)", r.deviceID.Short(), folder)
}
}
for folder := range r.startInfos {
if _, ok := except[folder]; !ok {
delete(r.startInfos, folder)
l.Debugf("Removed pending index sender for device %v and folder %v (removeAllExcept)", r.deviceID.Short(), folder)
}
}
}
@@ -388,6 +394,9 @@ func (r *indexSenderRegistry) pause(folder string) {
if is, ok := r.indexSenders[folder]; ok {
is.pause()
l.Debugf("Paused index sender for device %v and folder %v", r.deviceID.Short(), folder)
} else {
l.Debugf("No index sender for device %v and folder %v to pause", r.deviceID.Short(), folder)
}
}
@@ -403,11 +412,16 @@ func (r *indexSenderRegistry) resume(folder config.FolderConfiguration, fset *db
if isOk {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder.ID)
l.Debugf("Removed index sender for device %v and folder %v in resume", r.deviceID.Short(), folder.ID)
}
r.addLocked(folder, fset, info)
delete(r.startInfos, folder.ID)
l.Debugf("Started index sender for device %v and folder %v in resume", r.deviceID.Short(), folder.ID)
} else if isOk {
is.resume(fset)
l.Debugf("Resume index sender for device %v and folder %v", r.deviceID.Short(), folder.ID)
} else {
l.Debugf("Not resuming index sender for device %v and folder %v as none is paused and there is no start info", r.deviceID.Short(), folder.ID)
}
}
+82
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/model"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/stats"
@@ -249,6 +250,20 @@ type Model struct {
getHelloReturnsOnCall map[int]struct {
result1 protocol.HelloIntf
}
GetMtimeMappingStub func(string, string) (fs.MtimeMapping, error)
getMtimeMappingMutex sync.RWMutex
getMtimeMappingArgsForCall []struct {
arg1 string
arg2 string
}
getMtimeMappingReturns struct {
result1 fs.MtimeMapping
result2 error
}
getMtimeMappingReturnsOnCall map[int]struct {
result1 fs.MtimeMapping
result2 error
}
GlobalDirectoryTreeStub func(string, string, int, bool) ([]*model.TreeEntry, error)
globalDirectoryTreeMutex sync.RWMutex
globalDirectoryTreeArgsForCall []struct {
@@ -1691,6 +1706,71 @@ func (fake *Model) GetHelloReturnsOnCall(i int, result1 protocol.HelloIntf) {
}{result1}
}
func (fake *Model) GetMtimeMapping(arg1 string, arg2 string) (fs.MtimeMapping, error) {
fake.getMtimeMappingMutex.Lock()
ret, specificReturn := fake.getMtimeMappingReturnsOnCall[len(fake.getMtimeMappingArgsForCall)]
fake.getMtimeMappingArgsForCall = append(fake.getMtimeMappingArgsForCall, struct {
arg1 string
arg2 string
}{arg1, arg2})
stub := fake.GetMtimeMappingStub
fakeReturns := fake.getMtimeMappingReturns
fake.recordInvocation("GetMtimeMapping", []interface{}{arg1, arg2})
fake.getMtimeMappingMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *Model) GetMtimeMappingCallCount() int {
fake.getMtimeMappingMutex.RLock()
defer fake.getMtimeMappingMutex.RUnlock()
return len(fake.getMtimeMappingArgsForCall)
}
func (fake *Model) GetMtimeMappingCalls(stub func(string, string) (fs.MtimeMapping, error)) {
fake.getMtimeMappingMutex.Lock()
defer fake.getMtimeMappingMutex.Unlock()
fake.GetMtimeMappingStub = stub
}
func (fake *Model) GetMtimeMappingArgsForCall(i int) (string, string) {
fake.getMtimeMappingMutex.RLock()
defer fake.getMtimeMappingMutex.RUnlock()
argsForCall := fake.getMtimeMappingArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *Model) GetMtimeMappingReturns(result1 fs.MtimeMapping, result2 error) {
fake.getMtimeMappingMutex.Lock()
defer fake.getMtimeMappingMutex.Unlock()
fake.GetMtimeMappingStub = nil
fake.getMtimeMappingReturns = struct {
result1 fs.MtimeMapping
result2 error
}{result1, result2}
}
func (fake *Model) GetMtimeMappingReturnsOnCall(i int, result1 fs.MtimeMapping, result2 error) {
fake.getMtimeMappingMutex.Lock()
defer fake.getMtimeMappingMutex.Unlock()
fake.GetMtimeMappingStub = nil
if fake.getMtimeMappingReturnsOnCall == nil {
fake.getMtimeMappingReturnsOnCall = make(map[int]struct {
result1 fs.MtimeMapping
result2 error
})
}
fake.getMtimeMappingReturnsOnCall[i] = struct {
result1 fs.MtimeMapping
result2 error
}{result1, result2}
}
func (fake *Model) GlobalDirectoryTree(arg1 string, arg2 string, arg3 int, arg4 bool) ([]*model.TreeEntry, error) {
fake.globalDirectoryTreeMutex.Lock()
ret, specificReturn := fake.globalDirectoryTreeReturnsOnCall[len(fake.globalDirectoryTreeArgsForCall)]
@@ -3186,6 +3266,8 @@ func (fake *Model) Invocations() map[string][][]interface{} {
defer fake.getFolderVersionsMutex.RUnlock()
fake.getHelloMutex.RLock()
defer fake.getHelloMutex.RUnlock()
fake.getMtimeMappingMutex.RLock()
defer fake.getMtimeMappingMutex.RUnlock()
fake.globalDirectoryTreeMutex.RLock()
defer fake.globalDirectoryTreeMutex.RUnlock()
fake.indexMutex.RLock()
+76 -73
View File
@@ -4,6 +4,7 @@
// 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/.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
//go:generate counterfeiter -o mocks/model.go --fake-name Model . Model
package model
@@ -15,6 +16,7 @@ import (
"fmt"
"io"
"net"
"os"
"path/filepath"
"reflect"
"runtime"
@@ -41,12 +43,6 @@ import (
"github.com/syncthing/syncthing/lib/versioner"
)
// How many files to send in each Index/IndexUpdate message.
const (
maxBatchSizeBytes = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed)
maxBatchSizeFiles = 1000 // Either way, don't include more files than this
)
type service interface {
suture.Service
BringToFront(string)
@@ -100,6 +96,7 @@ type Model interface {
CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool, error)
CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool, error)
GetMtimeMapping(folder string, file string) (fs.MtimeMapping, error)
Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) ([]Availability, error)
Completion(device protocol.DeviceID, folder string) (FolderCompletion, error)
@@ -184,18 +181,21 @@ var (
errNetworkNotAllowed = errors.New("network not allowed")
errNoVersioner = errors.New("folder has no versioner")
// errors about why a connection is closed
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")
errEncryptionInvConfigRemote = errors.New("remote has encrypted data and encrypts that data for us - this is impossible")
errEncryptionNotEncryptedLocal = errors.New("folder is announced as encrypted, but not configured thus")
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")
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")
errReplacingConnection = errors.New("replacing connection")
errStopped = errors.New("Syncthing is being stopped")
errEncryptionInvConfigLocal = errors.New("can't encrypt outgoing data because local data is encrypted (folder-type receive-encrypted)")
errEncryptionInvConfigRemote = errors.New("remote has encrypted data and encrypts that data for us - this is impossible")
errEncryptionNotEncryptedLocal = errors.New("remote expects to exchange encrypted data, but is configured for plain data")
errEncryptionPlainForReceiveEncrypted = errors.New("remote expects to exchange plain data, but is configured to be encrypted")
errEncryptionPlainForRemoteEncrypted = errors.New("remote expects to exchange plain data, but local data is encrypted (folder-type receive-encrypted)")
errEncryptionNotEncryptedUntrusted = errors.New("device is untrusted, but configured to receive plain data")
errEncryptionPassword = errors.New("different encryption passwords used")
errEncryptionTokenRead = errors.New("failed to read encryption token")
errEncryptionTokenWrite = errors.New("failed to write encryption token")
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"
)
@@ -1341,7 +1341,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
if err := m.db.AddOrUpdatePendingFolder(folder.ID, of, deviceID); err != nil {
l.Warnf("Failed to persist pending folder entry to database: %v", err)
}
indexSenders.addPending(cfg, ccDeviceInfos[folder.ID])
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
updatedPending = append(updatedPending, updatedPendingFolder{
FolderID: folder.ID,
FolderLabel: folder.Label,
@@ -1365,7 +1365,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
}
if cfg.Paused {
indexSenders.addPending(cfg, ccDeviceInfos[folder.ID])
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
continue
}
@@ -1381,9 +1381,12 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
if sameError {
l.Debugln(msg)
} else {
if rerr, ok := err.(*redactedError); ok {
err = rerr.redacted
}
m.evLogger.Log(events.Failure, err.Error())
l.Warnln(msg)
}
m.evLogger.Log(events.Failure, err.Error())
return tempIndexFolders, paused, err
}
if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
@@ -1407,7 +1410,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
// Shouldn't happen because !cfg.Paused, but might happen
// if the folder is about to be unpaused, but not yet.
l.Debugln("ccH: no fset", folder.ID)
indexSenders.addPending(cfg, ccDeviceInfos[folder.ID])
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
continue
}
@@ -1468,7 +1471,11 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
}
if !(hasTokenRemote || hasTokenLocal) {
return errEncryptionNotEncryptedRemote
if isEncryptedRemote {
return errEncryptionPlainForReceiveEncrypted
} else {
return errEncryptionPlainForRemoteEncrypted
}
}
if !(isEncryptedRemote || isEncryptedLocal) {
@@ -1506,7 +1513,13 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
var err error
token, err = readEncryptionToken(fcfg)
if err != nil && !fs.IsNotExist(err) {
return err
if rerr, ok := redactPathError(err); ok {
return rerr
}
return &redactedError{
error: err,
redacted: errEncryptionTokenRead,
}
}
if err == nil {
m.fmut.Lock()
@@ -1514,7 +1527,14 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
m.fmut.Unlock()
} else {
if err := writeEncryptionToken(ccToken, fcfg); err != nil {
return err
if rerr, ok := redactPathError(err); ok {
return rerr
} else {
return &redactedError{
error: err,
redacted: errEncryptionTokenWrite,
}
}
}
m.fmut.Lock()
m.folderEncryptionPasswordTokens[fcfg.ID] = ccToken
@@ -1921,7 +1941,7 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, blockNo
return nil, protocol.ErrGeneric
}
if len(hash) > 0 && !scanner.Validate(res.data[:n], hash, weakHash) {
if folderCfg.Type != config.FolderTypeReceiveEncrypted && len(hash) > 0 && !scanner.Validate(res.data[:n], hash, weakHash) {
m.recheckFile(deviceID, folder, name, offset, hash, weakHash)
l.Debugf("%v REQ(in) failed validating data: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size)
return nil, protocol.ErrNoSuchFile
@@ -2025,12 +2045,12 @@ func (m *model) CurrentFolderFile(folder string, file string) (protocol.FileInfo
func (m *model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) {
m.fmut.RLock()
fs, ok := m.folderFiles[folder]
ffs, ok := m.folderFiles[folder]
m.fmut.RUnlock()
if !ok {
return protocol.FileInfo{}, false, ErrFolderMissing
}
snap, err := fs.Snapshot()
snap, err := ffs.Snapshot()
if err != nil {
return protocol.FileInfo{}, false, err
}
@@ -2039,6 +2059,16 @@ func (m *model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo
return f, ok, nil
}
func (m *model) GetMtimeMapping(folder string, file string) (fs.MtimeMapping, error) {
m.fmut.RLock()
ffs, ok := m.folderFiles[folder]
m.fmut.RUnlock()
if !ok {
return fs.MtimeMapping{}, ErrFolderMissing
}
return fs.GetMtimeMapping(ffs.MtimeFS(), file)
}
// Connection returns the current connection for device, and a boolean whether a connection was found.
func (m *model) Connection(deviceID protocol.DeviceID) (protocol.Connection, bool) {
m.pmut.RLock()
@@ -3135,51 +3165,6 @@ func (s folderDeviceSet) hasDevice(dev protocol.DeviceID) bool {
return false
}
type fileInfoBatch struct {
infos []protocol.FileInfo
size int
flushFn func([]protocol.FileInfo) error
}
func newFileInfoBatch(fn func([]protocol.FileInfo) error) *fileInfoBatch {
return &fileInfoBatch{
infos: make([]protocol.FileInfo, 0, maxBatchSizeFiles),
flushFn: fn,
}
}
func (b *fileInfoBatch) append(f protocol.FileInfo) {
b.infos = append(b.infos, f)
b.size += f.ProtoSize()
}
func (b *fileInfoBatch) full() bool {
return len(b.infos) >= maxBatchSizeFiles || b.size >= maxBatchSizeBytes
}
func (b *fileInfoBatch) flushIfFull() error {
if b.full() {
return b.flush()
}
return nil
}
func (b *fileInfoBatch) flush() error {
if len(b.infos) == 0 {
return nil
}
if err := b.flushFn(b.infos); err != nil {
return err
}
b.reset()
return nil
}
func (b *fileInfoBatch) reset() {
b.infos = b.infos[:0]
b.size = 0
}
// syncMutexMap is a type safe wrapper for a sync.Map that holds mutexes
type syncMutexMap struct {
inner stdsync.Map
@@ -3258,3 +3243,21 @@ type updatedPendingFolder struct {
DeviceID protocol.DeviceID `json:"deviceID"`
ReceiveEncrypted bool `json:"receiveEncrypted"`
}
// redactPathError checks if the error is actually a os.PathError, and if yes
// returns a redactedError with the path removed.
func redactPathError(err error) (error, bool) {
perr, ok := err.(*os.PathError)
if !ok {
return nil, false
}
return &redactedError{
error: err,
redacted: fmt.Errorf("%v: %w", perr.Op, perr.Err),
}, true
}
type redactedError struct {
error
redacted error
}
+28 -28
View File
@@ -224,11 +224,11 @@ func benchmarkIndex(b *testing.B, nfiles int) {
defer cleanupModel(m)
files := genFiles(nfiles)
m.Index(device1, "default", files)
must(b, m.Index(device1, "default", files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Index(device1, "default", files)
must(b, m.Index(device1, "default", files))
}
b.ReportAllocs()
}
@@ -252,11 +252,11 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
files := genFiles(nfiles)
ufiles := genFiles(nufiles)
m.Index(device1, "default", files)
must(b, m.Index(device1, "default", files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.IndexUpdate(device1, "default", ufiles)
must(b, m.IndexUpdate(device1, "default", ufiles))
}
b.ReportAllocs()
}
@@ -273,7 +273,7 @@ func BenchmarkRequestOut(b *testing.B) {
fc.addFile(f.Name, 0644, protocol.FileInfoTypeFile, []byte("some data to return"))
}
m.AddConnection(fc, protocol.Hello{})
m.Index(device1, "default", files)
must(b, m.Index(device1, "default", files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -1799,7 +1799,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
return string(bytes)
}
m.Index(device1, "default", testdata)
must(t, m.Index(device1, "default", testdata))
result, _ := m.GlobalDirectoryTree("default", "", -1, false)
@@ -2006,7 +2006,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) {
m.ScanFolder("default")
files := genDeepFiles(n1, n2)
m.Index(device1, "default", files)
must(b, m.Index(device1, "default", files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -2293,8 +2293,8 @@ func TestIssue3496(t *testing.T) {
m.ScanFolder("default")
addFakeConn(m, device1)
addFakeConn(m, device2)
addFakeConn(m, device1, "default")
addFakeConn(m, device2, "default")
// Reach into the model and grab the current file list...
@@ -2327,7 +2327,7 @@ func TestIssue3496(t *testing.T) {
Version: protocol.Vector{Counters: []protocol.Counter{{ID: device1.Short(), Value: 42}}},
})
m.IndexUpdate(device1, "default", localFiles)
must(t, m.IndexUpdate(device1, "default", localFiles))
// Check that the completion percentage for us makes sense
@@ -2400,8 +2400,8 @@ func TestNoRequestsFromPausedDevices(t *testing.T) {
t.Errorf("should not be available, no connections")
}
addFakeConn(m, device1)
addFakeConn(m, device2)
addFakeConn(m, device1, "default")
addFakeConn(m, device2, "default")
// !!! This is not what I'd expect to happen, as we don't even know if the peer has the original index !!!
@@ -2440,8 +2440,8 @@ func TestNoRequestsFromPausedDevices(t *testing.T) {
// Test that remote paused folders are not used.
addFakeConn(m, device1)
addFakeConn(m, device2)
addFakeConn(m, device1, "default")
addFakeConn(m, device2, "default")
m.ClusterConfig(device1, cc)
ccp := cc
@@ -2654,7 +2654,7 @@ func TestRemoveDirWithContent(t *testing.T) {
file.Deleted = true
file.Version = file.Version.Update(device1.Short()).Update(device1.Short())
m.IndexUpdate(device1, "default", []protocol.FileInfo{dir, file})
must(t, m.IndexUpdate(device1, "default", []protocol.FileInfo{dir, file}))
// Is there something we could trigger on instead of just waiting?
timeout := time.NewTimer(5 * time.Second)
@@ -3784,7 +3784,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) {
}
// A remote must have the file, otherwise the deletion below is
// automatically resolved as not a ro-changed item.
m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{file})
must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{file}))
must(t, ffs.Remove(name))
m.ScanFolders()
@@ -3895,9 +3895,9 @@ func TestIssue6961(t *testing.T) {
version := protocol.Vector{}.Update(device1.Short())
// Remote, valid and existing file
m.Index(device1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}})
must(t, m.Index(device1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}}))
// Remote, invalid (receive-only) and existing file
m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}})
must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}}))
// Create a local file
if fd, err := tfs.OpenFile(name, fs.OptCreate, 0666); err != nil {
t.Fatal(err)
@@ -3923,7 +3923,7 @@ func TestIssue6961(t *testing.T) {
m.ScanFolders()
// Drop ther remote index, add some other file.
m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}})
must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}}))
// Pause and unpause folder to create new db.FileSet and thus recalculate everything
pauseFolder(t, wcfg, fcfg.ID, true)
@@ -3947,7 +3947,7 @@ func TestCompletionEmptyGlobal(t *testing.T) {
m.fmut.Unlock()
files[0].Deleted = true
files[0].Version = files[0].Version.Update(device1.Short())
m.IndexUpdate(device1, fcfg.ID, files)
must(t, m.IndexUpdate(device1, fcfg.ID, files))
comp := m.testCompletion(protocol.LocalDeviceID, fcfg.ID)
if comp.CompletionPct != 95 {
t.Error("Expected completion of 95%, got", comp.CompletionPct)
@@ -3966,26 +3966,26 @@ func TestNeedMetaAfterIndexReset(t *testing.T) {
// Start with two remotes having one file, then both deleting it, then
// only one adding it again.
m.Index(device1, fcfg.ID, files)
m.Index(device2, fcfg.ID, files)
must(t, m.Index(device1, fcfg.ID, files))
must(t, m.Index(device2, fcfg.ID, files))
seq++
files[0].SetDeleted(device2.Short())
files[0].Sequence = seq
m.IndexUpdate(device2, fcfg.ID, files)
m.IndexUpdate(device1, fcfg.ID, files)
must(t, m.IndexUpdate(device2, fcfg.ID, files))
must(t, m.IndexUpdate(device1, fcfg.ID, files))
seq++
files[0].Deleted = false
files[0].Size = 20
files[0].Version = files[0].Version.Update(device1.Short())
files[0].Sequence = seq
m.IndexUpdate(device1, fcfg.ID, files)
must(t, m.IndexUpdate(device1, fcfg.ID, files))
if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 {
t.Error("Expected one needed item for device2, got", comp.NeedItems)
}
// Pretend we had an index reset on device 1
m.Index(device1, fcfg.ID, files)
must(t, m.Index(device1, fcfg.ID, files))
if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 {
t.Error("Expected one needed item for device2, got", comp.NeedItems)
}
@@ -4068,14 +4068,14 @@ func TestCcCheckEncryption(t *testing.T) {
tokenLocal: nil,
isEncryptedRemote: true,
isEncryptedLocal: false,
expectedErr: errEncryptionNotEncryptedRemote,
expectedErr: errEncryptionPlainForRemoteEncrypted,
},
{
tokenRemote: nil,
tokenLocal: nil,
isEncryptedRemote: false,
isEncryptedLocal: true,
expectedErr: errEncryptionNotEncryptedRemote,
expectedErr: errEncryptionPlainForReceiveEncrypted,
},
{
tokenRemote: nil,
+108 -23
View File
@@ -24,6 +24,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
)
func TestRequestSimple(t *testing.T) {
@@ -57,7 +58,11 @@ func TestRequestSimple(t *testing.T) {
contents := []byte("test file contents\n")
fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
fc.sendIndexUpdate()
<-done
select {
case <-done:
case <-time.After(10 * time.Second):
t.Fatal("timed out")
}
// Verify the contents
if err := equalContents(filepath.Join(tfs.URI(), "testfile"), contents); err != nil {
@@ -305,7 +310,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
folderIgnoresAlwaysReload(t, m, fcfg)
fc := addFakeConn(m, device1)
fc := addFakeConn(m, device1, fcfg.ID)
fc.folder = "default"
if err := m.SetIgnores("default", []string{"*ignored*"}); err != nil {
@@ -1037,7 +1042,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) {
folderIgnoresAlwaysReload(t, m, fcfg)
m.ScanFolders()
fc := addFakeConn(m, device1)
fc := addFakeConn(m, device1, fcfg.ID)
fc.folder = "default"
fc.mut.Lock()
fc.mut.Unlock()
@@ -1295,7 +1300,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
stopped: make(chan struct{}),
}
defer cleanupModel(m)
fc := addFakeConn(m, device1)
fc := addFakeConn(m, device1, fcfg.ID)
done := make(chan struct{})
defer close(done) // Must be the last thing to be deferred, thus first to run.
indexChan := make(chan []protocol.FileInfo, 1)
@@ -1335,7 +1340,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
}
}
func TestRequestReceiveEncryptedLocalNoSend(t *testing.T) {
func TestRequestReceiveEncrypted(t *testing.T) {
if testing.Short() {
t.Skip("skipping on short testing - scrypt is too slow")
}
@@ -1360,10 +1365,11 @@ func TestRequestReceiveEncryptedLocalNoSend(t *testing.T) {
m.fmut.RUnlock()
fset.Update(protocol.LocalDeviceID, files)
indexChan := make(chan []protocol.FileInfo, 1)
indexChan := make(chan []protocol.FileInfo, 10)
done := make(chan struct{})
defer close(done)
fc := newFakeConnection(device1, m)
fc.folder = fcfg.ID
fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
select {
case indexChan <- fs:
@@ -1398,23 +1404,52 @@ func TestRequestReceiveEncryptedLocalNoSend(t *testing.T) {
case <-time.After(5 * time.Second):
t.Fatal("timed out before receiving index")
}
// Detects deletion, as we never really created the file on disk
// Shouldn't send anything because receive-encrypted
must(t, m.ScanFolder(fcfg.ID))
// One real file to be sent
name := "foo"
data := make([]byte, 2000)
rand.Read(data)
fc.addFile(name, 0664, protocol.FileInfoTypeFile, data)
fc.sendIndexUpdate()
select {
case fs := <-indexChan:
if len(fs) != 1 {
t.Error("Expected index with one file, got", fs)
}
if got := fs[0].Name; got != name {
t.Errorf("Expected file %v, got %v", got, files[0].Name)
}
case <-time.After(5 * time.Second):
t.Fatal("timed out before receiving index")
}
// 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)
}
func TestRequestIssue7474(t *testing.T) {
// Repro for https://github.com/syncthing/syncthing/issues/7474
// Devices A, B and C. B connected to A and C, but not A to C.
// A has valid file, B ignores it.
// In the test C is local, and B is the fake connection.
func TestRequestGlobalInvalidToValid(t *testing.T) {
done := make(chan struct{})
defer close(done)
m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
waiter, err := m.cfg.Modify(func(cfg *config.Configuration) {
cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2"))
fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
cfg.SetFolder(fcfg)
})
must(t, err)
waiter.Wait()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
indexChan := make(chan []protocol.FileInfo)
indexChan := make(chan []protocol.FileInfo, 1)
fc.setIndexFn(func(ctx context.Context, folder string, fs []protocol.FileInfo) error {
select {
case indexChan <- fs:
@@ -1426,18 +1461,68 @@ func TestRequestIssue7474(t *testing.T) {
name := "foo"
fc.addFileWithLocalFlags(name, protocol.FileInfoTypeFile, protocol.FlagLocalIgnored)
// Setup device with valid file, do not send index yet
contents := []byte("test file contents\n")
fc.addFile(name, 0644, protocol.FileInfoTypeFile, contents)
// Third device ignoring the same file
fc.mut.Lock()
file := fc.files[0]
fc.mut.Unlock()
file.SetIgnored()
m.IndexUpdate(device2, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)})
// Wait for the ignored file to be received and possible pulled
timeout := time.After(10 * time.Second)
globalUpdated := false
for {
select {
case <-timeout:
t.Fatalf("timed out (globalUpdated == %v)", globalUpdated)
default:
time.Sleep(10 * time.Millisecond)
}
if !globalUpdated {
_, ok, err := m.CurrentGlobalFile(fcfg.ID, name)
if err != nil {
t.Fatal(err)
}
if !ok {
continue
}
globalUpdated = true
}
snap, err := m.DBSnapshot(fcfg.ID)
if err != nil {
t.Fatal(err)
}
need := snap.NeedSize(protocol.LocalDeviceID)
snap.Release()
if need.Files == 0 {
break
}
}
// Send the valid file
fc.sendIndexUpdate()
select {
case <-time.After(10 * time.Second):
t.Fatal("timed out before receiving index")
case fs := <-indexChan:
if len(fs) != 1 {
t.Fatalf("Expected one file in index, got %v", len(fs))
}
if !fs[0].IsInvalid() {
t.Error("Expected invalid file")
gotInvalid := false
for {
select {
case <-timeout:
t.Fatal("timed out before receiving index")
case fs := <-indexChan:
if len(fs) != 1 {
t.Fatalf("Expected one file in index, got %v", len(fs))
}
if !fs[0].IsInvalid() {
return
}
if gotInvalid {
t.Fatal("Received two invalid index updates")
}
t.Log("got index with invalid file")
gotInvalid = true
}
}
}
+1 -1
View File
@@ -126,7 +126,7 @@ func setupModelWithConnectionFromWrapper(t testing.TB, w config.Wrapper) (*testM
t.Helper()
m := setupModel(t, w)
fc := addFakeConn(m, device1)
fc := addFakeConn(m, device1, "default")
fc.folder = "default"
_ = m.ScanFolder("default")
+17 -68
View File
@@ -2629,10 +2629,7 @@ func (m *Hello) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -2720,10 +2717,7 @@ func (m *Header) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -2807,10 +2801,7 @@ func (m *ClusterConfig) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -3058,10 +3049,7 @@ func (m *Folder) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -3371,10 +3359,7 @@ func (m *Device) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -3490,10 +3475,7 @@ func (m *Index) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -3609,10 +3591,7 @@ func (m *IndexUpdate) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4126,10 +4105,7 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4270,10 +4246,7 @@ func (m *BlockInfo) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4357,10 +4330,7 @@ func (m *Vector) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4448,10 +4418,7 @@ func (m *Counter) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4714,10 +4681,7 @@ func (m *Request) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4839,10 +4803,7 @@ func (m *Response) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -4958,10 +4919,7 @@ func (m *DownloadProgress) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -5190,10 +5148,7 @@ func (m *FileDownloadProgressUpdate) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -5243,10 +5198,7 @@ func (m *Ping) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
@@ -5328,10 +5280,7 @@ func (m *Close) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthBep
}
if (iNdEx + skippy) > l {
+2 -8
View File
@@ -297,10 +297,7 @@ func (m *TestOldDeviceID) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) > l {
@@ -383,10 +380,7 @@ func (m *TestNewDeviceID) Unmarshal(dAtA []byte) error {
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) < 0 {
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthDeviceidTest
}
if (iNdEx + skippy) > l {
+2
View File
@@ -1,5 +1,7 @@
// Copyright (C) 2014 The Protocol Authors.
//go:generate -command counterfeiter go run github.com/maxbrunsfeld/counterfeiter/v6
// Prevents import loop, for internal testing
//go:generate counterfeiter -o mocked_connection_info_test.go --fake-name mockedConnectionInfo . ConnectionInfo
//go:generate go run ../../script/prune_mocks.go -t mocked_connection_info_test.go
+5 -21
View File
@@ -35,21 +35,21 @@ type RelayClient interface {
URI() *url.URL
}
func NewClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) (RelayClient, error) {
func NewClient(uri *url.URL, certs []tls.Certificate, timeout time.Duration) (RelayClient, error) {
factory, ok := supportedSchemes[uri.Scheme]
if !ok {
return nil, fmt.Errorf("unsupported scheme: %s", uri.Scheme)
}
invitations := make(chan protocol.SessionInvitation)
return factory(uri, certs, invitations, timeout), nil
}
type commonClient struct {
svcutil.ServiceWithError
invitations chan protocol.SessionInvitation
closeInvitationsOnFinish bool
mut sync.RWMutex
invitations chan protocol.SessionInvitation
mut sync.RWMutex
}
func newCommonClient(invitations chan protocol.SessionInvitation, serve func(context.Context) error, creator string) commonClient {
@@ -57,26 +57,10 @@ func newCommonClient(invitations chan protocol.SessionInvitation, serve func(con
invitations: invitations,
mut: sync.NewRWMutex(),
}
newServe := func(ctx context.Context) error {
defer c.cleanup()
return serve(ctx)
}
c.ServiceWithError = svcutil.AsService(newServe, creator)
if c.invitations == nil {
c.closeInvitationsOnFinish = true
c.invitations = make(chan protocol.SessionInvitation)
}
c.ServiceWithError = svcutil.AsService(serve, creator)
return c
}
func (c *commonClient) cleanup() {
c.mut.Lock()
if c.closeInvitationsOnFinish {
close(c.invitations)
}
c.mut.Unlock()
}
func (c *commonClient) Invitations() chan protocol.SessionInvitation {
c.mut.RLock()
defer c.mut.RUnlock()
+2 -1
View File
@@ -93,7 +93,8 @@ func (c *dynamicClient) serve(ctx context.Context) error {
c.client = client
c.mut.Unlock()
c.client.Serve(ctx)
err = c.client.Serve(ctx)
l.Debugf("Disconnected from %s://%s: %v", c.client.URI().Scheme, c.client.URI().Host, err)
c.mut.Lock()
c.client = nil
+9 -5
View File
@@ -114,16 +114,20 @@ func JoinSession(ctx context.Context, invitation protocol.SessionInvitation) (ne
func TestRelay(ctx context.Context, uri *url.URL, certs []tls.Certificate, sleep, timeout time.Duration, times int) error {
id := syncthingprotocol.NewDeviceID(certs[0].Certificate[0])
invs := make(chan protocol.SessionInvitation, 1)
c, err := NewClient(uri, certs, invs, timeout)
c, err := NewClient(uri, certs, timeout)
if err != nil {
close(invs)
return fmt.Errorf("creating client: %w", err)
}
ctx, cancel := context.WithCancel(context.Background())
go c.Serve(ctx)
go func() {
c.Serve(ctx)
close(invs)
for {
select {
case <-c.Invitations():
case <-ctx.Done():
return
}
}
}()
defer cancel()
+14 -10
View File
@@ -48,7 +48,7 @@ func newStaticClient(uri *url.URL, certs []tls.Certificate, invitations chan pro
func (c *staticClient) serve(ctx context.Context) error {
if err := c.connect(ctx); err != nil {
l.Infof("Could not connect to relay %s: %s", c.uri, err)
l.Debugf("Could not connect to relay %s: %s", c.uri, err)
return err
}
@@ -56,17 +56,16 @@ func (c *staticClient) serve(ctx context.Context) error {
defer c.disconnect()
if err := c.join(); err != nil {
l.Infof("Could not join relay %s: %s", c.uri, err)
l.Debugf("Could not join relay %s: %s", c.uri, err)
return err
}
if err := c.conn.SetDeadline(time.Time{}); err != nil {
l.Infoln("Relay set deadline:", err)
l.Debugln("Relay set deadline:", err)
return err
}
l.Infof("Joined relay %s://%s", c.uri.Scheme, c.uri.Host)
defer l.Infof("Disconnected from relay %s://%s", c.uri.Scheme, c.uri.Host)
c.mut.Lock()
c.connected = true
@@ -88,7 +87,7 @@ func (c *staticClient) serve(ctx context.Context) error {
switch msg := message.(type) {
case protocol.Ping:
if err := protocol.WriteMessage(c.conn, protocol.Pong{}); err != nil {
l.Infoln("Relay write:", err)
l.Debugln("Relay write:", err)
return err
}
l.Debugln(c, "sent pong")
@@ -98,23 +97,28 @@ func (c *staticClient) serve(ctx context.Context) error {
if len(ip) == 0 || ip.IsUnspecified() {
msg.Address = remoteIPBytes(c.conn)
}
c.invitations <- msg
select {
case c.invitations <- msg:
case <-ctx.Done():
l.Debugln(c, "stopping")
return ctx.Err()
}
case protocol.RelayFull:
l.Infof("Disconnected from relay %s due to it becoming full.", c.uri)
l.Debugf("Disconnected from relay %s due to it becoming full.", c.uri)
return errors.New("relay full")
default:
l.Infoln("Relay: protocol error: unexpected message %v", msg)
l.Debugln("Relay: protocol error: unexpected message %v", msg)
return fmt.Errorf("protocol error: unexpected message %v", msg)
}
case <-ctx.Done():
l.Debugln(c, "stopping")
return nil
return ctx.Err()
case err := <-errorsc:
l.Infof("Disconnecting from relay %s due to error: %s", c.uri, err)
l.Debugf("Disconnecting from relay %s due to error: %s", c.uri, err)
return err
case <-timeout.C:
+1 -1
View File
@@ -1,6 +1,6 @@
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
//go:generate -command genxdr go run ../../../repos/xdr/cmd/genxdr/main.go
//go:generate -command genxdr go run github.com/calmh/xdr/cmd/genxdr
//go:generate genxdr -o packets_xdr.go packets.go
package protocol
+22 -19
View File
@@ -109,7 +109,7 @@ type walker struct {
// Walk returns the list of files found in the local folder by scanning the
// file system. Files are blockwise hashed.
func (w *walker) walk(ctx context.Context) chan ScanResult {
l.Debugln("Walk", w.Subs, w.Matcher)
l.Debugln(w, "Walk", w.Subs, w.Matcher)
toHashChan := make(chan protocol.FileInfo)
finishedChan := make(chan ScanResult)
@@ -162,13 +162,13 @@ func (w *walker) walk(ctx context.Context) chan ScanResult {
for {
select {
case <-done:
l.Debugln("Walk progress done", w.Folder, w.Subs, w.Matcher)
l.Debugln(w, "Walk progress done", w.Folder, w.Subs, w.Matcher)
ticker.Stop()
return
case <-ticker.C:
current := progress.Total()
rate := progress.Rate()
l.Debugf("Walk %s %s current progress %d/%d at %.01f MiB/s (%d%%)", w.Folder, w.Subs, current, total, rate/1024/1024, current*100/total)
l.Debugf("%v: Walk %s %s current progress %d/%d at %.01f MiB/s (%d%%)", w, w.Folder, w.Subs, current, total, rate/1024/1024, current*100/total)
w.EventLogger.Log(events.FolderScanProgress, map[string]interface{}{
"folder": w.Folder,
"current": current,
@@ -184,7 +184,7 @@ func (w *walker) walk(ctx context.Context) chan ScanResult {
loop:
for _, file := range filesToHash {
l.Debugln("real to hash:", file.Name)
l.Debugln(w, "real to hash:", file.Name)
select {
case realToHashChan <- file:
case <-ctx.Done():
@@ -198,7 +198,7 @@ func (w *walker) walk(ctx context.Context) chan ScanResult {
}
func (w *walker) walkWithoutHashing(ctx context.Context) chan ScanResult {
l.Debugln("Walk without hashing", w.Subs, w.Matcher)
l.Debugln(w, "Walk without hashing", w.Subs, w.Matcher)
toHashChan := make(chan protocol.FileInfo)
finishedChan := make(chan ScanResult)
@@ -224,7 +224,7 @@ func (w *walker) scan(ctx context.Context, toHashChan chan<- protocol.FileInfo,
} else {
for _, sub := range w.Subs {
if err := osutil.TraversesSymlink(w.Filesystem, filepath.Dir(sub)); err != nil {
l.Debugf("Skip walking %v as it is below a symlink", sub)
l.Debugf("%v: Skip walking %v as it is below a symlink", w, sub)
continue
}
w.Filesystem.Walk(sub, hashFiles)
@@ -258,21 +258,21 @@ func (w *walker) walkAndHashFiles(ctx context.Context, toHashChan chan<- protoco
}
if fs.IsTemporary(path) {
l.Debugln("temporary:", path, "err:", err)
l.Debugln(w, "temporary:", path, "err:", err)
if err == nil && info.IsRegular() && info.ModTime().Add(w.TempLifetime).Before(now) {
w.Filesystem.Remove(path)
l.Debugln("removing temporary:", path, info.ModTime())
l.Debugln(w, "removing temporary:", path, info.ModTime())
}
return nil
}
if fs.IsInternal(path) {
l.Debugln("ignored (internal):", path)
l.Debugln(w, "ignored (internal):", path)
return skip
}
if w.Matcher.Match(path).IsIgnored() {
l.Debugln("ignored (patterns):", path)
l.Debugln(w, "ignored (patterns):", path)
// Only descend if matcher says so and the current file is not a symlink.
if err != nil || w.Matcher.SkipIgnoredDirs() || info.IsSymlink() {
return skip
@@ -285,7 +285,11 @@ func (w *walker) walkAndHashFiles(ctx context.Context, toHashChan chan<- protoco
}
if err != nil {
handleError(ctx, "scan", path, err, finishedChan)
// No need reporting errors for files that don't exist (e.g. scan
// due to filesystem watcher)
if !fs.IsNotExist(err) {
handleError(ctx, "scan", path, err, finishedChan)
}
return skip
}
@@ -384,6 +388,7 @@ func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileIn
if hasCurFile {
if curFile.IsEquivalentOptional(f, w.ModTimeWindow, w.IgnorePerms, true, w.LocalFlags) {
l.Debugln(w, "unchanged:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
return nil
}
if curFile.ShouldConflict() {
@@ -394,10 +399,10 @@ func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileIn
// conflict.
f.Version = f.Version.DropOthers(w.ShortID)
}
l.Debugln("rescan:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
l.Debugln(w, "rescan:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
}
l.Debugln("to hash:", relPath, f)
l.Debugln(w, "to hash:", relPath, f)
select {
case toHashChan <- f:
@@ -417,6 +422,7 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo,
if hasCurFile {
if curFile.IsEquivalentOptional(f, w.ModTimeWindow, w.IgnorePerms, true, w.LocalFlags) {
l.Debugln(w, "unchanged:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
return nil
}
if curFile.ShouldConflict() {
@@ -429,7 +435,7 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo,
}
}
l.Debugln("dir:", relPath, f)
l.Debugln(w, "dir:", relPath, f)
select {
case finishedChan <- ScanResult{File: f}:
@@ -461,6 +467,7 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, info fs.FileIn
if hasCurFile {
if curFile.IsEquivalentOptional(f, w.ModTimeWindow, w.IgnorePerms, true, w.LocalFlags) {
l.Debugln(w, "unchanged:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
return nil
}
if curFile.ShouldConflict() {
@@ -473,7 +480,7 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, info fs.FileIn
}
}
l.Debugln("symlink changedb:", relPath, f)
l.Debugln(w, "symlink changedb:", relPath, f)
select {
case finishedChan <- ScanResult{File: f}:
@@ -557,10 +564,6 @@ func (w *walker) updateFileInfo(file, curFile protocol.FileInfo) protocol.FileIn
}
func handleError(ctx context.Context, context, path string, err error, finishedChan chan<- ScanResult) {
// Ignore missing items, as deletions are not handled by the scanner.
if fs.IsNotExist(err) {
return
}
select {
case finishedChan <- ScanResult{
Err: fmt.Errorf("%s: %w", context, err),
+56
View File
@@ -251,6 +251,62 @@ func TestNormalization(t *testing.T) {
}
}
func TestNormalizationDarwinCaseFS(t *testing.T) {
// This tests that normalization works on Darwin, through a CaseFS.
if runtime.GOOS != "darwin" {
t.Skip("Normalization test not possible on non-Darwin")
return
}
testFs := fs.NewCaseFilesystem(testFs)
testFs.RemoveAll("normalization")
defer testFs.RemoveAll("normalization")
testFs.MkdirAll("normalization", 0755)
const (
inNFC = "\xC3\x84"
inNFD = "\x41\xCC\x88"
)
// Create dir in NFC
if err := testFs.Mkdir(filepath.Join("normalization", "dir-"+inNFC), 0755); err != nil {
t.Fatal(err)
}
// Create file in NFC
fd, err := testFs.Create(filepath.Join("normalization", "dir-"+inNFC, "file-"+inNFC))
if err != nil {
t.Fatal(err)
}
fd.Close()
// Walk, which should normalize and return
walkDir(testFs, "normalization", nil, nil, 0)
tmp := walkDir(testFs, "normalization", nil, nil, 0)
if len(tmp) != 3 {
t.Error("Expected one file and one dir scanned")
}
// Verify we see the normalized entries in the result
foundFile := false
foundDir := false
for _, f := range tmp {
if f.Name == filepath.Join("normalization", "dir-"+inNFD) {
foundDir = true
continue
}
if f.Name == filepath.Join("normalization", "dir-"+inNFD, "file-"+inNFD) {
foundFile = true
continue
}
}
if !foundFile || !foundDir {
t.Error("Didn't find expected normalization form")
}
}
func TestIssue1507(t *testing.T) {
w := &walker{}
w.Matcher = ignore.New(w.Filesystem)
+30 -12
View File
@@ -38,17 +38,35 @@ const (
NATSymmetricUDPFirewall = stun.NATSymmetricUDPFirewall
)
type writeTrackingPacketConn struct {
type writeTrackingUdpConn struct {
lastWrite int64 // atomic, must remain 64-bit aligned
net.PacketConn
// Needs to be UDPConn not PacketConn, as pfilter checks for WriteMsgUDP/ReadMsgUDP
// and even if we embed UDPConn here, in place of a PacketConn, seems the interface
// check fails.
*net.UDPConn
}
func (c *writeTrackingPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
func (c *writeTrackingUdpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
atomic.StoreInt64(&c.lastWrite, time.Now().Unix())
return c.PacketConn.WriteTo(p, addr)
return c.UDPConn.WriteTo(p, addr)
}
func (c *writeTrackingPacketConn) getLastWrite() time.Time {
func (c *writeTrackingUdpConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
atomic.StoreInt64(&c.lastWrite, time.Now().Unix())
return c.UDPConn.WriteMsgUDP(b, oob, addr)
}
func (c *writeTrackingUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
atomic.StoreInt64(&c.lastWrite, time.Now().Unix())
return c.UDPConn.WriteToUDP(b, addr)
}
func (c *writeTrackingUdpConn) Write(b []byte) (int, error) {
atomic.StoreInt64(&c.lastWrite, time.Now().Unix())
return c.UDPConn.Write(b)
}
func (c *writeTrackingUdpConn) getLastWrite() time.Time {
unix := atomic.LoadInt64(&c.lastWrite)
return time.Unix(unix, 0)
}
@@ -65,18 +83,18 @@ type Service struct {
stunConn net.PacketConn
client *stun.Client
writeTrackingPacketConn *writeTrackingPacketConn
writeTrackingUdpConn *writeTrackingUdpConn
natType NATType
addr *Host
}
func New(cfg config.Wrapper, subscriber Subscriber, conn net.PacketConn) (*Service, net.PacketConn) {
func New(cfg config.Wrapper, subscriber Subscriber, conn *net.UDPConn) (*Service, net.PacketConn) {
// Wrap the original connection to track writes on it
writeTrackingPacketConn := &writeTrackingPacketConn{lastWrite: 0, PacketConn: conn}
writeTrackingUdpConn := &writeTrackingUdpConn{lastWrite: 0, UDPConn: conn}
// Wrap it in a filter and split it up, so that stun packets arrive on stun conn, others arrive on the data conn
filterConn := pfilter.NewPacketFilter(writeTrackingPacketConn)
filterConn := pfilter.NewPacketFilter(writeTrackingUdpConn)
otherDataConn := filterConn.NewConn(otherDataPriority, nil)
stunConn := filterConn.NewConn(stunFilterPriority, &stunFilter{
ids: make(map[string]time.Time),
@@ -97,7 +115,7 @@ func New(cfg config.Wrapper, subscriber Subscriber, conn net.PacketConn) (*Servi
stunConn: stunConn,
client: client,
writeTrackingPacketConn: writeTrackingPacketConn,
writeTrackingUdpConn: writeTrackingUdpConn,
natType: NATUnknown,
addr: nil,
@@ -241,7 +259,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host)
}
// Adjust the keepalives to fire only nextSleep after last write.
lastWrite := s.writeTrackingPacketConn.getLastWrite()
lastWrite := s.writeTrackingUdpConn.getLastWrite()
minSleep := time.Duration(s.cfg.Options().StunKeepaliveMinS) * time.Second
if nextSleep < minSleep {
nextSleep = minSleep
@@ -270,7 +288,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host)
}
// Check if any writes happened while we were sleeping, if they did, sleep again
lastWrite = s.writeTrackingPacketConn.getLastWrite()
lastWrite = s.writeTrackingUdpConn.getLastWrite()
if gap := time.Since(lastWrite); gap < nextSleep {
l.Debugf("%s stun last write gap less than next sleep: %s < %s. Will try later", s, gap, nextSleep)
goto tryLater
+33 -1
View File
@@ -160,7 +160,7 @@ func SpecWithDebugLogger(l logger.Logger) suture.Spec {
}
func SpecWithInfoLogger(l logger.Logger) suture.Spec {
return spec(func(e suture.Event) { l.Infoln(e) })
return spec(infoEventHook(l))
}
func spec(eventHook suture.EventHook) suture.Spec {
@@ -171,3 +171,35 @@ func spec(eventHook suture.EventHook) suture.Spec {
DontPropagateTermination: false,
}
}
// infoEventHook prints service failures and failures to stop services at level
// info. All other events and identical, consecutive failures are logged at
// debug only.
func infoEventHook(l logger.Logger) suture.EventHook {
var prevTerminate suture.EventServiceTerminate
return func(ei suture.Event) {
switch e := ei.(type) {
case suture.EventStopTimeout:
l.Infof("%s: Service %s failed to terminate in a timely manner", e.SupervisorName, e.ServiceName)
case suture.EventServicePanic:
l.Warnln("Caught a service panic, which shouldn't happen")
l.Infoln(e)
case suture.EventServiceTerminate:
msg := fmt.Sprintf("%s: service %s failed: %s", e.SupervisorName, e.ServiceName, e.Err)
if e.ServiceName == prevTerminate.ServiceName && e.Err == prevTerminate.Err {
l.Debugln(msg)
} else {
l.Infoln(msg)
}
prevTerminate = e
l.Debugln(e) // Contains some backoff statistics
case suture.EventBackoff:
l.Debugf("%s: exiting the backoff state.", e.SupervisorName)
case suture.EventResume:
l.Debugf("%s: too many service failures - entering the backoff state.", e.SupervisorName)
default:
l.Warnln("Unknown suture supervisor event type", e.Type())
l.Infoln(e)
}
}
}
+11 -4
View File
@@ -110,7 +110,8 @@ func (a *App) Start() error {
a.stopped = make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
a.mainServiceCancel = cancel
go a.run(ctx)
errChan := a.mainService.ServeBackground(ctx)
go a.wait(errChan)
if err := a.startup(); err != nil {
a.stopWithErr(svcutil.ExitError, err)
@@ -255,7 +256,13 @@ func (a *App) startup() error {
// The TLS configuration is used for both the listening socket and outgoing
// connections.
tlsCfg := tlsutil.SecureDefault()
var tlsCfg *tls.Config
if a.cfg.Options().InsecureAllowOldTLSVersions {
l.Infoln("TLS 1.2 is allowed on sync connections. This is less than optimally secure.")
tlsCfg = tlsutil.SecureDefaultWithTLS12()
} else {
tlsCfg = tlsutil.SecureDefaultTLS13()
}
tlsCfg.Certificates = []tls.Certificate{a.cert}
tlsCfg.NextProtos = []string{bepProtocolName}
tlsCfg.ClientAuth = tls.RequestClientCert
@@ -328,8 +335,8 @@ func (a *App) startup() error {
return nil
}
func (a *App) run(ctx context.Context) {
err := a.mainService.Serve(ctx)
func (a *App) wait(errChan <-chan error) {
err := <-errChan
a.handleMainServiceError(err)
done := make(chan struct{})

Some files were not shown because too many files have changed in this diff Show More