mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-24 21:58:22 -05:00
Compare commits
13 Commits
v1.2.0-rc.
...
v1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20eab36a33 | ||
|
|
afde0727fe | ||
|
|
bf744ded31 | ||
|
|
0d86166890 | ||
|
|
cea5962417 | ||
|
|
02752af862 | ||
|
|
6b1d7ac727 | ||
|
|
abd363e8bb | ||
|
|
bff1a5f5e4 | ||
|
|
38302270d4 | ||
|
|
1b4fe39a89 | ||
|
|
6b74cdc613 | ||
|
|
13a746e0fb |
1
AUTHORS
1
AUTHORS
@@ -144,6 +144,7 @@ Michael Ploujnikov (plouj) <ploujj@gmail.com>
|
||||
Michael Tilli (pyfisch) <pyfisch@gmail.com>
|
||||
Mike Boone <mike@boonedocks.net>
|
||||
MikeLund <MikeLund@users.noreply.github.com>
|
||||
Mingxuan Lin <gdlmx@users.noreply.github.com>
|
||||
Nate Morrison (nrm21) <natemorrison@gmail.com>
|
||||
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
|
||||
Nico Stapelbroek <3368018+nstapelbroek@users.noreply.github.com>
|
||||
|
||||
2
build.go
2
build.go
@@ -348,7 +348,7 @@ func test(pkgs ...string) {
|
||||
}
|
||||
|
||||
if coverage {
|
||||
args = append(args, "-covermode", "atomic", "-coverprofile", "coverage.txt")
|
||||
args = append(args, "-covermode", "atomic", "-coverprofile", "coverage.txt", "-coverpkg", strings.Join(pkgs, ","))
|
||||
}
|
||||
|
||||
runPrint(goCmd, append(args, pkgs...)...)
|
||||
|
||||
@@ -928,7 +928,17 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
code := exit.waitForExit()
|
||||
|
||||
mainService.Stop()
|
||||
ldb.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
ldb.Close()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Second):
|
||||
l.Warnln("Database failed to stop within 10s")
|
||||
}
|
||||
|
||||
l.Infoln("Exiting")
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ SuccessExitStatus=3 4
|
||||
RestartForceExitStatus=3 4
|
||||
|
||||
# Hardening
|
||||
ProtectSystem=full
|
||||
PrivateTmp=true
|
||||
SystemCallArchitectures=native
|
||||
MemoryDenyWriteExecute=true
|
||||
NoNewPrivileges=true
|
||||
|
||||
13
go.mod
13
go.mod
@@ -2,7 +2,7 @@ module github.com/syncthing/syncthing
|
||||
|
||||
require (
|
||||
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190525131515-730b0de4d4de
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
|
||||
github.com/AudriusButkevicius/recli v0.0.5
|
||||
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
|
||||
github.com/calmh/du v1.0.1
|
||||
@@ -16,7 +16,6 @@ require (
|
||||
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d
|
||||
github.com/gogo/protobuf v1.2.1
|
||||
github.com/golang/groupcache v0.0.0-20171101203131-84a468cf14b4
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 // indirect
|
||||
github.com/jackpal/gateway v0.0.0-20161225004348-5795ac81146e
|
||||
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
@@ -25,6 +24,8 @@ require (
|
||||
github.com/maruel/panicparse v1.2.1
|
||||
github.com/mattn/go-isatty v0.0.7
|
||||
github.com/minio/sha256-simd v0.0.0-20190117184323-cc1980cb0338
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/oschwald/geoip2-golang v1.3.0
|
||||
github.com/oschwald/maxminddb-golang v0.0.0-20170901134056-26fe5ace1c70 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20170816195418-3db12ebb2a59 // indirect
|
||||
@@ -33,15 +34,17 @@ require (
|
||||
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9
|
||||
github.com/sasha-s/go-deadlock v0.2.0
|
||||
github.com/syncthing/notify v0.0.0-20181107104724-4e389ea6c0d8
|
||||
github.com/syndtr/goleveldb v0.0.0-20171214120811-34011bf325bc
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965
|
||||
github.com/thejerf/suture v3.0.2+incompatible
|
||||
github.com/urfave/cli v1.20.0
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
|
||||
golang.org/x/sys v0.0.0-20190613124609-5ed2794edfdc // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/time v0.0.0-20170927054726-6dc17368e09b
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/ldap.v2 v2.5.1
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
||||
44
go.sum
44
go.sum
@@ -2,9 +2,10 @@ github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362 h1:l
|
||||
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362/go.mod h1:CEaBhA5lh1spxbPOELh5wNLKGsVQoahjUhVrJViVK8s=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190525131515-730b0de4d4de h1:w1VG0ehgPh2ucQGO7wL9TBmHLzMo4dduYwyp2lhs8+A=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190525131515-730b0de4d4de/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6 h1:Apvc4kyfdrOxG+F5dn8osz+45kwGJa6CySQn0tB38SU=
|
||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6/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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
@@ -21,7 +22,6 @@ github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d h1:As4937T5NVbJ/DmZ
|
||||
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d/go.mod h1:3FK1bMar37f7jqVY7q/63k3OMX1c47pGCufzt3X0sYE=
|
||||
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713 h1:UNOqI3EKhvbqV8f1Vm3NIwkrhq388sGCeAH2Op7w0rc=
|
||||
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chmduquesne/rollinghash v0.0.0-20180912150627-a60f8e7142b5 h1:Wg96Dh0MLTanEaPO0OkGtUIaa2jOnShAIOVUIzRHUxo=
|
||||
@@ -31,7 +31,6 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
@@ -40,7 +39,6 @@ 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/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d h1:IngNQgbqr5ZOU0exk395Szrvkzes9Ilk1fmJfkw7d+M=
|
||||
github.com/gobwas/glob v0.0.0-20170212200151-51eb1ee00b6d/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
@@ -55,8 +53,8 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/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=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jackpal/gateway v0.0.0-20161225004348-5795ac81146e h1:lS8IitpqG4RkZbEDlZg5Z7FvBdWLVjSVfsPGOKafEkI=
|
||||
@@ -76,8 +74,6 @@ 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.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lucas-clemente/quic-go v0.11.1 h1:zasajC848Dqq/+WqfqBCkmPw+YHNe1MBts/z7y7nXf4=
|
||||
github.com/lucas-clemente/quic-go v0.11.1/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/lucas-clemente/quic-go v0.11.2 h1:Mop0ac3zALaBR3wGs6j8OYe/tcFvFsxTUFMkE/7yUOI=
|
||||
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||
@@ -96,12 +92,15 @@ github.com/minio/sha256-simd v0.0.0-20190117184323-cc1980cb0338/go.mod h1:2FMWW+
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/oschwald/geoip2-golang v1.3.0 h1:D+Hsdos1NARPbzZ2aInUHZL+dApIzo8E0ErJVsWcku8=
|
||||
github.com/oschwald/geoip2-golang v1.3.0/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE=
|
||||
github.com/oschwald/maxminddb-golang v0.0.0-20170901134056-26fe5ace1c70 h1:XGLYUmodtNzThosQ8GkMvj9TiIB/uWsP8NfxKSa3aDc=
|
||||
@@ -114,40 +113,32 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=
|
||||
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9 h1:jmLW6izPBVlIbk4d+XgK9+sChGbVKxxOPmd9eqRHCjw=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syncthing/notify v0.0.0-20181107104724-4e389ea6c0d8 h1:ewsMW/a4xDpqHyIteoD29ayMn6GdkFZc2T0PX2K6PAg=
|
||||
github.com/syncthing/notify v0.0.0-20181107104724-4e389ea6c0d8/go.mod h1:Sn4ChoS7e4FxjCN1XHPVBT43AgnRLbuaB8pEc1Zcdjg=
|
||||
github.com/syndtr/goleveldb v0.0.0-20171214120811-34011bf325bc h1:yhWARKbbDg8UBRi/M5bVcVOBg2viFKcNJEAtHMYbRBo=
|
||||
github.com/syndtr/goleveldb v0.0.0-20171214120811-34011bf325bc/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
|
||||
github.com/thejerf/suture v3.0.2+incompatible h1:GtMydYcnK4zBJ0KL6Lx9vLzl6Oozb65wh252FTBxrvM=
|
||||
github.com/thejerf/suture v3.0.2+incompatible/go.mod h1:ibKwrVj+Uzf3XZdAiNWUouPaAbSoemxOHLmJmwheEMc=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
@@ -158,10 +149,13 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -169,7 +163,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -177,6 +170,9 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpbl
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190613124609-5ed2794edfdc h1:x+/QxSNkVFAC+v4pL1f6mZr1z+qgi+FoR8ccXZPVC10=
|
||||
golang.org/x/sys v0.0.0-20190613124609-5ed2794edfdc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
@@ -199,3 +195,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -372,3 +372,32 @@ ul.three-columns li, ul.two-columns li {
|
||||
.fancytree-ext-table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@media (max-width: 419px) {
|
||||
/* the selectors are build to target only the content of folder and device
|
||||
panels as it would "destroy" e.g. out of sync or recent changes listings */
|
||||
div[id^='device-'].panel-collapse table,
|
||||
div[id^='folder-'].panel-collapse table,
|
||||
div[id^='device-'].panel-collapse tbody,
|
||||
div[id^='folder-'].panel-collapse tbody,
|
||||
div[id^='device-'].panel-collapse tr,
|
||||
div[id^='folder-'].panel-collapse tr {
|
||||
display: block;
|
||||
}
|
||||
div[id^='device-'].panel-collapse th,
|
||||
div[id^='folder-'].panel-collapse th,
|
||||
div[id^='device-'].panel-collapse td,
|
||||
div[id^='folder-'].panel-collapse td {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* all buttons, except panel headings, get bottom margin, as they won't fit
|
||||
beside each other anymore */
|
||||
.btn:not(.panel-heading),
|
||||
/* this "+"-selector is needed to override some bootstrap defaults */
|
||||
.btn:not(.panel-heading) + .btn:not(.panel-heading) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
"Are you sure you want to remove folder {%label%}?": "Are you sure you want to remove folder {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "Are you sure you want to restore {{count}} files?",
|
||||
"Auto Accept": "Auto Accept",
|
||||
"Automatic Crash Reporting": "Automatic Crash Reporting",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatic upgrade now offers the choice between stable releases and release candidates.",
|
||||
"Automatic upgrades": "Automatic upgrades",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
|
||||
@@ -71,6 +72,7 @@
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Devices",
|
||||
"Disable Crash Reporting": "Disable Crash Reporting",
|
||||
"Disabled": "Disabled",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Disabled periodic scanning and disabled watching for changes",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Disabled periodic scanning and enabled watching for changes",
|
||||
@@ -92,6 +94,7 @@
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Editing": "Editing",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Enable Crash Reporting": "Enable Crash Reporting",
|
||||
"Enable NAT traversal": "Enable NAT traversal",
|
||||
"Enable Relaying": "Enable Relaying",
|
||||
"Enabled": "Enabled",
|
||||
@@ -141,6 +144,7 @@
|
||||
"Global State": "Global State",
|
||||
"Help": "Help",
|
||||
"Home page": "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.",
|
||||
"Ignore": "Ignore",
|
||||
"Ignore Patterns": "Ignore Patterns",
|
||||
"Ignore Permissions": "Ignore Permissions",
|
||||
@@ -299,6 +303,7 @@
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as MPL v2.0.",
|
||||
"Syncthing is restarting.": "Syncthing is restarting.",
|
||||
"Syncthing is upgrading.": "Syncthing is upgrading.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.",
|
||||
"Take me back": "Take me back",
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
"Log": "Журнал",
|
||||
"Log tailing paused. Click here to continue.": "Вывод журнала приостановлен. Чтобы продолжить, нажмите здесь.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Вывод журнала приостановлен. Чтобы продолжить, прокрутите журнал до конца.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Вывод журнала приостановлен. Чтобы продолжить, прокрутите до журнал конца.",
|
||||
"Logs": "Журналы",
|
||||
"Major Upgrade": "Обновление основной версии",
|
||||
"Mass actions": "Массовые действия",
|
||||
@@ -338,7 +338,7 @@
|
||||
"Type": "Тип",
|
||||
"Unavailable": "Недоступно",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Недоступно или отключено администратором",
|
||||
"Undecided (will prompt)": "Запрос каждый раз",
|
||||
"Undecided (will prompt)": "Не определено (запрашивать каждый раз)",
|
||||
"Unignore": "Не игнорировать",
|
||||
"Unknown": "Неизвестно",
|
||||
"Unshared": "Необщедоступно",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<p translate>Copyright © 2014-2019 the following Contributors:</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, André Colomb, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Evgeny Kuznetsov, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, Nitroretro, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, dependabot-preview[bot], dependabot[bot], derekriemer, desbma, georgespatton, janost, jaseg, klemens, marco-m, otbutz, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, André Colomb, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Evgeny Kuznetsov, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Kalle Laine, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, Nitroretro, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, dependabot-preview[bot], dependabot[bot], derekriemer, desbma, georgespatton, janost, jaseg, klemens, marco-m, otbutz, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -726,6 +726,11 @@ angular.module('syncthing.core')
|
||||
|
||||
var refreshGlobalChanges = debounce(function () {
|
||||
$http.get(urlbase + "/events/disk?limit=25").success(function (data) {
|
||||
if (!data) {
|
||||
// For reasons unknown this is called with data being the empty
|
||||
// string on shutdown, causing an error on .reverse().
|
||||
return;
|
||||
}
|
||||
data = data.reverse();
|
||||
$scope.globalChangeEvents = data;
|
||||
console.log("refreshGlobalChanges", data);
|
||||
|
||||
@@ -39,6 +39,7 @@ type Lowlevel struct {
|
||||
deviceIdx *smallIndex
|
||||
closed bool
|
||||
closeMut *sync.RWMutex
|
||||
iterWG sync.WaitGroup
|
||||
}
|
||||
|
||||
// Open attempts to open the database at the given location, and runs
|
||||
@@ -99,42 +100,76 @@ func (db *Lowlevel) Committed() int64 {
|
||||
}
|
||||
|
||||
func (db *Lowlevel) Put(key, val []byte, wo *opt.WriteOptions) error {
|
||||
db.closeMut.RLock()
|
||||
defer db.closeMut.RUnlock()
|
||||
if db.closed {
|
||||
return leveldb.ErrClosed
|
||||
}
|
||||
atomic.AddInt64(&db.committed, 1)
|
||||
return db.DB.Put(key, val, wo)
|
||||
}
|
||||
|
||||
func (db *Lowlevel) Write(batch *leveldb.Batch, wo *opt.WriteOptions) error {
|
||||
db.closeMut.RLock()
|
||||
defer db.closeMut.RUnlock()
|
||||
if db.closed {
|
||||
return leveldb.ErrClosed
|
||||
}
|
||||
return db.DB.Write(batch, wo)
|
||||
}
|
||||
|
||||
func (db *Lowlevel) Delete(key []byte, wo *opt.WriteOptions) error {
|
||||
db.closeMut.RLock()
|
||||
defer db.closeMut.RUnlock()
|
||||
if db.closed {
|
||||
return leveldb.ErrClosed
|
||||
}
|
||||
atomic.AddInt64(&db.committed, 1)
|
||||
return db.DB.Delete(key, wo)
|
||||
}
|
||||
|
||||
func (db *Lowlevel) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
||||
return db.newIterator(func() iterator.Iterator { return db.DB.NewIterator(slice, ro) })
|
||||
}
|
||||
|
||||
// newIterator returns an iterator created with the given constructor only if db
|
||||
// is not yet closed. If it is closed, a closedIter is returned instead.
|
||||
func (db *Lowlevel) newIterator(constr func() iterator.Iterator) iterator.Iterator {
|
||||
db.closeMut.RLock()
|
||||
defer db.closeMut.RUnlock()
|
||||
if db.closed {
|
||||
return &closedIter{}
|
||||
}
|
||||
return db.DB.NewIterator(slice, ro)
|
||||
db.iterWG.Add(1)
|
||||
return &iter{
|
||||
Iterator: constr(),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Lowlevel) GetSnapshot() snapshot {
|
||||
snap, err := db.DB.GetSnapshot()
|
||||
s, err := db.DB.GetSnapshot()
|
||||
if err != nil {
|
||||
if err == leveldb.ErrClosed {
|
||||
return &closedSnap{}
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
return snap
|
||||
return &snap{
|
||||
Snapshot: s,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Lowlevel) Close() {
|
||||
db.closeMut.Lock()
|
||||
defer db.closeMut.Unlock()
|
||||
if db.closed {
|
||||
db.closeMut.Unlock()
|
||||
return
|
||||
}
|
||||
db.closed = true
|
||||
db.closeMut.Unlock()
|
||||
db.iterWG.Wait()
|
||||
db.DB.Close()
|
||||
}
|
||||
|
||||
@@ -146,6 +181,7 @@ func NewLowlevel(db *leveldb.DB, location string) *Lowlevel {
|
||||
folderIdx: newSmallIndex(db, []byte{KeyTypeFolderIdx}),
|
||||
deviceIdx: newSmallIndex(db, []byte{KeyTypeDeviceIdx}),
|
||||
closeMut: &sync.RWMutex{},
|
||||
iterWG: sync.WaitGroup{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,3 +256,51 @@ func (s *closedSnap) NewIterator(*util.Range, *opt.ReadOptions) iterator.Iterato
|
||||
return &closedIter{}
|
||||
}
|
||||
func (s *closedSnap) Release() {}
|
||||
|
||||
type snap struct {
|
||||
*leveldb.Snapshot
|
||||
db *Lowlevel
|
||||
}
|
||||
|
||||
func (s *snap) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
||||
return s.db.newIterator(func() iterator.Iterator { return s.Snapshot.NewIterator(slice, ro) })
|
||||
}
|
||||
|
||||
// iter implements iterator.Iterator which allows tracking active iterators
|
||||
// and aborts if the underlying database is being closed.
|
||||
type iter struct {
|
||||
iterator.Iterator
|
||||
db *Lowlevel
|
||||
}
|
||||
|
||||
func (it *iter) Release() {
|
||||
it.db.iterWG.Done()
|
||||
it.Iterator.Release()
|
||||
}
|
||||
|
||||
func (it *iter) Next() bool {
|
||||
return it.execIfNotClosed(it.Iterator.Next)
|
||||
}
|
||||
func (it *iter) Prev() bool {
|
||||
return it.execIfNotClosed(it.Iterator.Prev)
|
||||
}
|
||||
func (it *iter) First() bool {
|
||||
return it.execIfNotClosed(it.Iterator.First)
|
||||
}
|
||||
func (it *iter) Last() bool {
|
||||
return it.execIfNotClosed(it.Iterator.Last)
|
||||
}
|
||||
func (it *iter) Seek(key []byte) bool {
|
||||
return it.execIfNotClosed(func() bool {
|
||||
return it.Iterator.Seek(key)
|
||||
})
|
||||
}
|
||||
|
||||
func (it *iter) execIfNotClosed(fn func() bool) bool {
|
||||
it.db.closeMut.RLock()
|
||||
defer it.db.closeMut.RUnlock()
|
||||
if it.db.closed {
|
||||
return false
|
||||
}
|
||||
return fn()
|
||||
}
|
||||
|
||||
@@ -326,7 +326,17 @@ func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyC
|
||||
changed++
|
||||
|
||||
case runtime.GOOS == "windows" && fs.WindowsInvalidFilename(file.Name):
|
||||
f.newPullError(file.Name, fs.ErrInvalidFilename)
|
||||
if file.IsDeleted() {
|
||||
// Just pretend we deleted it, no reason to create an error
|
||||
// about a deleted file that we can't have anyway.
|
||||
// Reason we need it in the first place is, that it was
|
||||
// ignored at some point.
|
||||
dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteFile}
|
||||
changed++
|
||||
} else {
|
||||
// We can't pull an invalid file.
|
||||
f.newPullError(file.Name, fs.ErrInvalidFilename)
|
||||
}
|
||||
|
||||
case file.IsDeleted():
|
||||
if file.IsDirectory() {
|
||||
|
||||
@@ -59,8 +59,6 @@ type service interface {
|
||||
GetStatistics() stats.FolderStatistics
|
||||
|
||||
getState() (folderState, time.Time, error)
|
||||
setState(state folderState)
|
||||
setError(err error)
|
||||
}
|
||||
|
||||
type Availability struct {
|
||||
@@ -2072,15 +2070,6 @@ func (m *model) ScanFolders() map[string]error {
|
||||
errorsMut.Lock()
|
||||
errors[folder] = err
|
||||
errorsMut.Unlock()
|
||||
|
||||
// Potentially sets the error twice, once in the scanner just
|
||||
// by doing a check, and once here, if the error returned is
|
||||
// the same one as returned by CheckHealth, though
|
||||
// duplicate set is handled by setError.
|
||||
m.fmut.RLock()
|
||||
srv := m.folderRunners[folder]
|
||||
m.fmut.RUnlock()
|
||||
srv.setError(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
@@ -2852,9 +2852,10 @@ func TestVersionRestore(t *testing.T) {
|
||||
defer cleanupModel(m)
|
||||
m.ScanFolder("default")
|
||||
|
||||
sentinel, err := time.ParseInLocation(versioner.TimeFormat, "20200101-010101", time.Local)
|
||||
must(t, err)
|
||||
sentinelTag := sentinel.Format(versioner.TimeFormat)
|
||||
sentinel, err := time.ParseInLocation(versioner.TimeFormat, "20180101-010101", time.Local)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range []string{
|
||||
// Versions directory
|
||||
@@ -2866,7 +2867,6 @@ func TestVersionRestore(t *testing.T) {
|
||||
".stversions/dir/file~20171210-040406.txt",
|
||||
".stversions/very/very/deep/one~20171210-040406.txt", // lives deep down, no directory exists.
|
||||
".stversions/dir/existing~20171210-040406.txt", // exists, should expect to be archived.
|
||||
".stversions/dir/file.txt~20171210-040405", // old tag format, supported
|
||||
".stversions/dir/cat", // untagged which was used by trashcan, supported
|
||||
|
||||
// "file.txt" will be restored
|
||||
@@ -2897,7 +2897,7 @@ func TestVersionRestore(t *testing.T) {
|
||||
"file.txt": 1,
|
||||
"existing": 1,
|
||||
"something": 1,
|
||||
"dir/file.txt": 4,
|
||||
"dir/file.txt": 3,
|
||||
"dir/existing.txt": 1,
|
||||
"very/very/deep/one.txt": 1,
|
||||
"dir/cat": 1,
|
||||
@@ -2942,6 +2942,8 @@ func TestVersionRestore(t *testing.T) {
|
||||
"very/very/deep/one.txt": makeTime("20171210-040406"),
|
||||
}
|
||||
|
||||
beforeRestore := time.Now().Truncate(time.Second)
|
||||
|
||||
ferr, err := m.RestoreFolderVersions("default", restore)
|
||||
must(t, err)
|
||||
|
||||
@@ -2977,51 +2979,48 @@ func TestVersionRestore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Simple versioner uses modtime for timestamp generation, so we can check
|
||||
// if existing stuff was correctly archived as we restored.
|
||||
// Simple versioner uses now for timestamp generation, so we can check
|
||||
// if existing stuff was correctly archived as we restored (oppose to deleteD), and version time as after beforeRestore
|
||||
expectArchived := map[string]struct{}{
|
||||
"existing": {},
|
||||
"dir/file.txt": {},
|
||||
"dir/existing.txt": {},
|
||||
}
|
||||
|
||||
// Even if they are at the archived path, content should have the non
|
||||
// archived name.
|
||||
for file := range expectArchived {
|
||||
allFileVersions, err := m.GetFolderVersions("default")
|
||||
must(t, err)
|
||||
for file, versions := range allFileVersions {
|
||||
key := file
|
||||
if runtime.GOOS == "windows" {
|
||||
file = filepath.FromSlash(file)
|
||||
}
|
||||
taggedName := versioner.TagFilename(file, sentinelTag)
|
||||
taggedArchivedName := filepath.Join(".stversions", taggedName)
|
||||
for _, version := range versions {
|
||||
if version.VersionTime.Equal(beforeRestore) || version.VersionTime.After(beforeRestore) {
|
||||
fd, err := filesystem.Open(".stversions/" + versioner.TagFilename(file, version.VersionTime.Format(versioner.TimeFormat)))
|
||||
must(t, err)
|
||||
defer fd.Close()
|
||||
|
||||
fd, err := filesystem.Open(taggedArchivedName)
|
||||
must(t, err)
|
||||
defer fd.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !bytes.Equal(content, []byte(file)) {
|
||||
t.Errorf("%s: %s != %s", file, string(content), file)
|
||||
content, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// Even if they are at the archived path, content should have the non
|
||||
// archived name.
|
||||
if !bytes.Equal(content, []byte(file)) {
|
||||
t.Errorf("%s (%s): %s != %s", file, fd.Name(), string(content), file)
|
||||
}
|
||||
_, ok := expectArchived[key]
|
||||
if !ok {
|
||||
t.Error("unexpected archived file with future timestamp", file, version.VersionTime)
|
||||
}
|
||||
delete(expectArchived, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for other unexpected things that are tagged.
|
||||
filesystem.Walk(".", func(path string, f fs.FileInfo, err error) error {
|
||||
if !f.IsRegular() {
|
||||
return nil
|
||||
}
|
||||
if strings.Contains(path, sentinelTag) {
|
||||
path = osutil.NormalizedFilename(path)
|
||||
name, _ := versioner.UntagFilename(path)
|
||||
name = strings.TrimPrefix(name, ".stversions/")
|
||||
if _, ok := expectArchived[name]; !ok {
|
||||
t.Errorf("unexpected file with sentinel tag: %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(expectArchived) != 0 {
|
||||
t.Fatal("missed some archived files", expectArchived)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPausedFolders(t *testing.T) {
|
||||
|
||||
@@ -362,9 +362,13 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
}
|
||||
|
||||
done = make(chan struct{})
|
||||
expected := map[string]struct{}{ign: {}, ignExisting: {}}
|
||||
// The indexes will normally arrive in one update, but it is possible
|
||||
// that they arrive in separate ones.
|
||||
secondIndex := false
|
||||
fc.mut.Lock()
|
||||
fc.indexFn = func(folder string, fs []protocol.FileInfo) {
|
||||
expected := map[string]struct{}{ign: {}, ignExisting: {}}
|
||||
secondIndex = true
|
||||
for _, f := range fs {
|
||||
if _, ok := expected[f.Name]; !ok {
|
||||
t.Fatalf("Unexpected file %v was updated in index", f.Name)
|
||||
@@ -387,10 +391,11 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
}
|
||||
delete(expected, f.Name)
|
||||
}
|
||||
for name := range expected {
|
||||
t.Errorf("File %v wasn't updated in index", name)
|
||||
if len(expected) == 0 {
|
||||
close(done)
|
||||
} else if secondIndex {
|
||||
t.Fatal("Didn't receive index updates for all existing files, missing", expected)
|
||||
}
|
||||
close(done)
|
||||
}
|
||||
// Make sure pulling doesn't interfere, as index updates are racy and
|
||||
// thus we cannot distinguish between scan and pull results.
|
||||
|
||||
@@ -14,6 +14,7 @@ type TestModel struct {
|
||||
weakHash uint32
|
||||
fromTemporary bool
|
||||
indexFn func(DeviceID, string, []FileInfo)
|
||||
ccFn func(DeviceID, ClusterConfig)
|
||||
closedCh chan struct{}
|
||||
closedErr error
|
||||
}
|
||||
@@ -52,6 +53,9 @@ func (t *TestModel) Closed(conn Connection, err error) {
|
||||
}
|
||||
|
||||
func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) {
|
||||
if t.ccFn != nil {
|
||||
t.ccFn(deviceID, config)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestModel) DownloadProgress(DeviceID, string, []FileDownloadProgressUpdate) {
|
||||
|
||||
@@ -882,7 +882,11 @@ func (c *rawConnection) Close(err error) {
|
||||
}
|
||||
})
|
||||
|
||||
c.internalClose(err)
|
||||
// Close might be called from a method that is called from within
|
||||
// dispatcherLoop, resulting in a deadlock.
|
||||
// The sending above must happen before spawning the routine, to prevent
|
||||
// the underlying connection from terminating before sending the close msg.
|
||||
go c.internalClose(err)
|
||||
}
|
||||
|
||||
// internalClose is called if there is an unexpected error during normal operation.
|
||||
|
||||
@@ -813,3 +813,22 @@ func TestClusterConfigAfterClose(t *testing.T) {
|
||||
t.Fatal("timed out before Cluster Config returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDispatcherToCloseDeadlock(t *testing.T) {
|
||||
// Verify that we don't deadlock when calling Close() from within one of
|
||||
// the model callbacks (ClusterConfig).
|
||||
m := newTestModel()
|
||||
c := NewConnection(c0ID, &testutils.BlockingRW{}, &testutils.NoopRW{}, m, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
|
||||
m.ccFn = func(devID DeviceID, cc ClusterConfig) {
|
||||
c.Close(errManual)
|
||||
}
|
||||
c.Start()
|
||||
|
||||
c.inbox <- &ClusterConfig{}
|
||||
|
||||
select {
|
||||
case <-c.dispatcherLoopStopped:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timed out before dispatcher loop terminated")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
package versioner
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -50,35 +48,12 @@ func (v Simple) Archive(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
file := filepath.Base(filePath)
|
||||
dir := filepath.Dir(filePath)
|
||||
|
||||
// Glob according to the new file~timestamp.ext pattern.
|
||||
pattern := filepath.Join(dir, TagFilename(file, TimeGlob))
|
||||
newVersions, err := v.versionsFs.Glob(pattern)
|
||||
if err != nil {
|
||||
l.Warnln("globbing:", err, "for", pattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Also according to the old file.ext~timestamp pattern.
|
||||
pattern = filepath.Join(dir, file+"~"+TimeGlob)
|
||||
oldVersions, err := v.versionsFs.Glob(pattern)
|
||||
if err != nil {
|
||||
l.Warnln("globbing:", err, "for", pattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use all the found filenames.
|
||||
versions := util.UniqueTrimmedStrings(append(oldVersions, newVersions...))
|
||||
|
||||
// Amend with mtime, sort on mtime, delete the oldest first. Mtime,
|
||||
// nowadays at least, is the time when the archiving happened.
|
||||
versionsWithMtimes := versionsToVersionsWithMtime(v.versionsFs, versions)
|
||||
if len(versionsWithMtimes) > v.keep {
|
||||
for _, toRemove := range versionsWithMtimes[:len(versionsWithMtimes)-v.keep] {
|
||||
// Versions are sorted by timestamp in the file name, oldest first.
|
||||
versions := findAllVersions(v.versionsFs, filePath)
|
||||
if len(versions) > v.keep {
|
||||
for _, toRemove := range versions[:len(versions)-v.keep] {
|
||||
l.Debugln("cleaning out", toRemove)
|
||||
err = v.versionsFs.Remove(toRemove.name)
|
||||
err = v.versionsFs.Remove(toRemove)
|
||||
if err != nil {
|
||||
l.Warnln("removing old version:", err)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
package versioner
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/syncthing/syncthing/lib/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -103,7 +101,7 @@ func (v *Staggered) clean() {
|
||||
return
|
||||
}
|
||||
|
||||
versionsPerFile := make(map[string][]versionWithMtime)
|
||||
versionsPerFile := make(map[string][]string)
|
||||
dirTracker := make(emptyDirTracker)
|
||||
|
||||
walkFn := func(path string, f fs.FileInfo, err error) error {
|
||||
@@ -124,10 +122,7 @@ func (v *Staggered) clean() {
|
||||
return nil
|
||||
}
|
||||
|
||||
versionsPerFile[name] = append(versionsPerFile[name], versionWithMtime{
|
||||
name: name,
|
||||
mtime: f.ModTime(),
|
||||
})
|
||||
versionsPerFile[name] = append(versionsPerFile[name], path)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -146,7 +141,7 @@ func (v *Staggered) clean() {
|
||||
l.Debugln("Cleaner: Finished cleaning", v.versionsFs)
|
||||
}
|
||||
|
||||
func (v *Staggered) expire(versions []versionWithMtime) {
|
||||
func (v *Staggered) expire(versions []string) {
|
||||
l.Debugln("Versioner: Expiring versions", versions)
|
||||
for _, file := range v.toRemove(versions, time.Now()) {
|
||||
if fi, err := v.versionsFs.Lstat(file); err != nil {
|
||||
@@ -163,24 +158,26 @@ func (v *Staggered) expire(versions []versionWithMtime) {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Staggered) toRemove(versions []versionWithMtime, now time.Time) []string {
|
||||
func (v *Staggered) toRemove(versions []string, now time.Time) []string {
|
||||
var prevAge int64
|
||||
firstFile := true
|
||||
var remove []string
|
||||
|
||||
// The list of versions may or may not be properly sorted. Let's take
|
||||
// off and nuke from orbit, it's the only way to be sure.
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return versions[i].mtime.Before(versions[j].mtime)
|
||||
})
|
||||
// The list of versions may or may not be properly sorted.
|
||||
sort.Strings(versions)
|
||||
|
||||
for _, version := range versions {
|
||||
age := int64(now.Sub(version.mtime).Seconds())
|
||||
versionTime, err := time.ParseInLocation(TimeFormat, ExtractTag(version), time.Local)
|
||||
if err != nil {
|
||||
l.Debugf("Versioner: file name %q is invalid: %v", version, err)
|
||||
continue
|
||||
}
|
||||
age := int64(now.Sub(versionTime).Seconds())
|
||||
|
||||
// If the file is older than the max age of the last interval, remove it
|
||||
if lastIntv := v.interval[len(v.interval)-1]; lastIntv.end > 0 && age > lastIntv.end {
|
||||
l.Debugln("Versioner: File over maximum age -> delete ", version.name)
|
||||
remove = append(remove, version.name)
|
||||
l.Debugln("Versioner: File over maximum age -> delete ", version)
|
||||
remove = append(remove, version)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -200,8 +197,8 @@ func (v *Staggered) toRemove(versions []versionWithMtime, now time.Time) []strin
|
||||
}
|
||||
|
||||
if prevAge-age < usedInterval.step {
|
||||
l.Debugln("too many files in step -> delete", version.name)
|
||||
remove = append(remove, version.name)
|
||||
l.Debugln("too many files in step -> delete", version)
|
||||
remove = append(remove, version)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -222,31 +219,7 @@ func (v *Staggered) Archive(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
file := filepath.Base(filePath)
|
||||
inFolderPath := filepath.Dir(filePath)
|
||||
|
||||
// Glob according to the new file~timestamp.ext pattern.
|
||||
pattern := filepath.Join(inFolderPath, TagFilename(file, TimeGlob))
|
||||
newVersions, err := v.versionsFs.Glob(pattern)
|
||||
if err != nil {
|
||||
l.Warnln("globbing:", err, "for", pattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Also according to the old file.ext~timestamp pattern.
|
||||
pattern = filepath.Join(inFolderPath, file+"~"+TimeGlob)
|
||||
oldVersions, err := v.versionsFs.Glob(pattern)
|
||||
if err != nil {
|
||||
l.Warnln("globbing:", err, "for", pattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use all the found filenames.
|
||||
versions := append(oldVersions, newVersions...)
|
||||
versions = util.UniqueTrimmedStrings(versions)
|
||||
|
||||
versionsWithMtimes := versionsToVersionsWithMtime(v.versionsFs, versions)
|
||||
v.expire(versionsWithMtimes)
|
||||
v.expire(findAllVersions(v.versionsFs, filePath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,25 +26,25 @@ func TestStaggeredVersioningVersionCount(t *testing.T) {
|
||||
*/
|
||||
|
||||
now := parseTime("20160415-140000")
|
||||
versionsWithMtime := []versionWithMtime{
|
||||
versionsWithMtime := []string{
|
||||
// 14:00:00 is "now"
|
||||
{"test~20160415-140000", parseTime("20160415-140000")}, // 0 seconds ago
|
||||
{"test~20160415-135959", parseTime("20160415-135959")}, // 1 second ago
|
||||
{"test~20160415-135958", parseTime("20160415-135958")}, // 2 seconds ago
|
||||
{"test~20160415-135900", parseTime("20160415-135900")}, // 1 minute ago
|
||||
{"test~20160415-135859", parseTime("20160415-135859")}, // 1 minute 1 second ago
|
||||
{"test~20160415-135830", parseTime("20160415-135830")}, // 1 minute 30 seconds ago
|
||||
{"test~20160415-135829", parseTime("20160415-135829")}, // 1 minute 31 seconds ago
|
||||
{"test~20160415-135700", parseTime("20160415-135700")}, // 3 minutes ago
|
||||
{"test~20160415-135630", parseTime("20160415-135630")}, // 3 minutes 30 seconds ago
|
||||
{"test~20160415-133000", parseTime("20160415-133000")}, // 30 minutes ago
|
||||
{"test~20160415-132900", parseTime("20160415-132900")}, // 31 minutes ago
|
||||
{"test~20160415-132500", parseTime("20160415-132500")}, // 35 minutes ago
|
||||
{"test~20160415-132000", parseTime("20160415-132000")}, // 40 minutes ago
|
||||
{"test~20160415-130000", parseTime("20160415-130000")}, // 60 minutes ago
|
||||
{"test~20160415-124000", parseTime("20160415-124000")}, // 80 minutes ago
|
||||
{"test~20160415-122000", parseTime("20160415-122000")}, // 100 minutes ago
|
||||
{"test~20160415-110000", parseTime("20160415-110000")}, // 120 minutes ago
|
||||
"test~20160415-140000", // 0 seconds ago
|
||||
"test~20160415-135959", // 1 second ago
|
||||
"test~20160415-135958", // 2 seconds ago
|
||||
"test~20160415-135900", // 1 minute ago
|
||||
"test~20160415-135859", // 1 minute 1 second ago
|
||||
"test~20160415-135830", // 1 minute 30 seconds ago
|
||||
"test~20160415-135829", // 1 minute 31 seconds ago
|
||||
"test~20160415-135700", // 3 minutes ago
|
||||
"test~20160415-135630", // 3 minutes 30 seconds ago
|
||||
"test~20160415-133000", // 30 minutes ago
|
||||
"test~20160415-132900", // 31 minutes ago
|
||||
"test~20160415-132500", // 35 minutes ago
|
||||
"test~20160415-132000", // 40 minutes ago
|
||||
"test~20160415-130000", // 60 minutes ago
|
||||
"test~20160415-124000", // 80 minutes ago
|
||||
"test~20160415-122000", // 100 minutes ago
|
||||
"test~20160415-110000", // 120 minutes ago
|
||||
}
|
||||
|
||||
delete := []string{
|
||||
|
||||
@@ -133,9 +133,15 @@ func (t *Trashcan) Restore(filepath string, versionTime time.Time) error {
|
||||
|
||||
taggedName := ""
|
||||
tagger := func(name, tag string) string {
|
||||
// We can't use TagFilename here, as restoreFii would discover that as a valid version and restore that instead.
|
||||
// We also abuse the fact that tagger gets called twice, once for tagging the restoration version, which
|
||||
// should just return the plain name, and second time by archive which archives existing file in the folder.
|
||||
// We can't use TagFilename here, as restoreFile would discover that as a valid version and restore that instead.
|
||||
if taggedName != "" {
|
||||
return taggedName
|
||||
}
|
||||
|
||||
taggedName = fs.TempName(name)
|
||||
return taggedName
|
||||
return name
|
||||
}
|
||||
|
||||
err := restoreFile(t.versionsFs, t.folderFs, filepath, versionTime, tagger)
|
||||
|
||||
@@ -108,18 +108,39 @@ func TestTrashcanArchiveRestoreSwitcharoo(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
versionInfo, err := versionsFs.Stat("file")
|
||||
// Check versions
|
||||
versions, err := versioner.GetVersions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fileVersions := versions["file"]
|
||||
if len(fileVersions) != 1 {
|
||||
t.Fatalf("unexpected number of versions: %d != 1", len(fileVersions))
|
||||
}
|
||||
|
||||
fileVersion := fileVersions[0]
|
||||
|
||||
if !fileVersion.ModTime.Equal(fileVersion.VersionTime) {
|
||||
t.Error("time mismatch")
|
||||
}
|
||||
|
||||
if content := readFile(t, versionsFs, "file"); content != "A" {
|
||||
t.Errorf("expected A got %s", content)
|
||||
}
|
||||
|
||||
writeFile(t, folderFs, "file", "B")
|
||||
|
||||
if err := versioner.Restore("file", versionInfo.ModTime().Truncate(time.Second)); err != nil {
|
||||
versionInfo, err := versionsFs.Stat("file")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !versionInfo.ModTime().Truncate(time.Second).Equal(fileVersion.ModTime) {
|
||||
t.Error("time mismatch")
|
||||
}
|
||||
|
||||
if err := versioner.Restore("file", fileVersion.VersionTime); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/util"
|
||||
)
|
||||
|
||||
var errDirectory = fmt.Errorf("cannot restore on top of a directory")
|
||||
@@ -87,15 +88,16 @@ func retrieveVersions(fileSystem fs.Filesystem) (map[string][]FileVersion, error
|
||||
return nil
|
||||
}
|
||||
|
||||
modTime := f.ModTime().Truncate(time.Second)
|
||||
|
||||
path = osutil.NormalizedFilename(path)
|
||||
|
||||
name, tag := UntagFilename(path)
|
||||
// Something invalid, assume it's an untagged file
|
||||
// Something invalid, assume it's an untagged file (trashcan versioner stuff)
|
||||
if name == "" || tag == "" {
|
||||
versionTime := f.ModTime().Truncate(time.Second)
|
||||
files[path] = append(files[path], FileVersion{
|
||||
VersionTime: versionTime,
|
||||
ModTime: versionTime,
|
||||
VersionTime: modTime,
|
||||
ModTime: modTime,
|
||||
Size: f.Size(),
|
||||
})
|
||||
return nil
|
||||
@@ -107,15 +109,11 @@ func retrieveVersions(fileSystem fs.Filesystem) (map[string][]FileVersion, error
|
||||
return nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
files[name] = append(files[name], FileVersion{
|
||||
// This looks backwards, but mtime of the file is when we archived it, making that the version time
|
||||
// The mod time of the file before archiving is embedded in the file name.
|
||||
VersionTime: f.ModTime().Truncate(time.Second),
|
||||
ModTime: versionTime.Truncate(time.Second),
|
||||
Size: f.Size(),
|
||||
})
|
||||
}
|
||||
files[name] = append(files[name], FileVersion{
|
||||
VersionTime: versionTime,
|
||||
ModTime: modTime,
|
||||
Size: f.Size(),
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
@@ -156,30 +154,38 @@ func archiveFile(srcFs, dstFs fs.Filesystem, filePath string, tagger fileTagger)
|
||||
}
|
||||
}
|
||||
|
||||
l.Debugln("archiving", filePath)
|
||||
|
||||
file := filepath.Base(filePath)
|
||||
inFolderPath := filepath.Dir(filePath)
|
||||
|
||||
err = dstFs.MkdirAll(inFolderPath, 0755)
|
||||
if err != nil && !fs.IsExist(err) {
|
||||
l.Debugln("archiving", filePath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
ver := tagger(file, info.ModTime().Format(TimeFormat))
|
||||
now := time.Now()
|
||||
|
||||
ver := tagger(file, now.Format(TimeFormat))
|
||||
dst := filepath.Join(inFolderPath, ver)
|
||||
l.Debugln("moving to", dst)
|
||||
l.Debugln("archiving", filePath, "moving to", dst)
|
||||
err = osutil.RenameOrCopy(srcFs, dstFs, filePath, dst)
|
||||
|
||||
// Set the mtime to the time the file was deleted. This can be used by the
|
||||
// cleanout routine. If this fails things won't work optimally but there's
|
||||
// not much we can do about it so we ignore the error.
|
||||
_ = dstFs.Chtimes(dst, time.Now(), time.Now())
|
||||
mtime := info.ModTime()
|
||||
// If it's a trashcan versioner type thing, then it does not have version time in the name
|
||||
// so use mtime for that.
|
||||
if ver == file {
|
||||
mtime = now
|
||||
}
|
||||
|
||||
_ = dstFs.Chtimes(dst, mtime, mtime)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func restoreFile(src, dst fs.Filesystem, filePath string, versionTime time.Time, tagger fileTagger) error {
|
||||
tag := versionTime.In(time.Local).Truncate(time.Second).Format(TimeFormat)
|
||||
taggedFilePath := tagger(filePath, tag)
|
||||
|
||||
// If the something already exists where we are restoring to, archive existing file for versioning
|
||||
// remove if it's a symlink, or fail if it's a directory
|
||||
if info, err := dst.Lstat(filePath); err == nil {
|
||||
@@ -203,28 +209,27 @@ func restoreFile(src, dst fs.Filesystem, filePath string, versionTime time.Time,
|
||||
}
|
||||
|
||||
filePath = osutil.NativeFilename(filePath)
|
||||
tag := versionTime.In(time.Local).Truncate(time.Second).Format(TimeFormat)
|
||||
|
||||
taggedFilename := TagFilename(filePath, tag)
|
||||
oldTaggedFilename := filePath + tag
|
||||
untaggedFileName := filePath
|
||||
|
||||
// Check that the thing we've been asked to restore is actually a file
|
||||
// and that it exists.
|
||||
// Try and find a file that has the correct mtime
|
||||
sourceFile := ""
|
||||
for _, candidate := range []string{taggedFilename, oldTaggedFilename, untaggedFileName} {
|
||||
if info, err := src.Lstat(candidate); fs.IsNotExist(err) || !info.IsRegular() {
|
||||
continue
|
||||
} else if err != nil {
|
||||
// All other errors are fatal
|
||||
return err
|
||||
} else if candidate == untaggedFileName && !info.ModTime().Truncate(time.Second).Equal(versionTime) {
|
||||
// No error, and untagged file, but mtime does not match, skip
|
||||
continue
|
||||
sourceMtime := time.Time{}
|
||||
if info, err := src.Lstat(taggedFilePath); err == nil && info.IsRegular() {
|
||||
sourceFile = taggedFilePath
|
||||
sourceMtime = info.ModTime()
|
||||
} else if err == nil {
|
||||
l.Debugln("restore:", taggedFilePath, "not regular")
|
||||
} else {
|
||||
l.Debugln("restore:", taggedFilePath, err.Error())
|
||||
}
|
||||
|
||||
// Check for untagged file
|
||||
if sourceFile == "" {
|
||||
info, err := src.Lstat(filePath)
|
||||
if err == nil && info.IsRegular() && info.ModTime().Truncate(time.Second).Equal(versionTime) {
|
||||
sourceFile = filePath
|
||||
sourceMtime = info.ModTime()
|
||||
}
|
||||
|
||||
sourceFile = candidate
|
||||
break
|
||||
}
|
||||
|
||||
if sourceFile == "" {
|
||||
@@ -240,7 +245,9 @@ func restoreFile(src, dst fs.Filesystem, filePath string, versionTime time.Time,
|
||||
}
|
||||
|
||||
_ = dst.MkdirAll(filepath.Dir(filePath), 0755)
|
||||
return osutil.RenameOrCopy(src, dst, sourceFile, filePath)
|
||||
err := osutil.RenameOrCopy(src, dst, sourceFile, filePath)
|
||||
_ = dst.Chtimes(filePath, sourceMtime, sourceMtime)
|
||||
return err
|
||||
}
|
||||
|
||||
func fsFromParams(folderFs fs.Filesystem, params map[string]string) (versionsFs fs.Filesystem) {
|
||||
@@ -260,33 +267,23 @@ func fsFromParams(folderFs fs.Filesystem, params map[string]string) (versionsFs
|
||||
_ = fsType.UnmarshalText([]byte(params["fsType"]))
|
||||
versionsFs = fs.NewFilesystem(fsType, params["fsPath"])
|
||||
}
|
||||
l.Debugln("%s (%s) folder using %s (%s) versioner dir", folderFs.URI(), folderFs.Type(), versionsFs.URI(), versionsFs.Type())
|
||||
l.Debugf("%s (%s) folder using %s (%s) versioner dir", folderFs.URI(), folderFs.Type(), versionsFs.URI(), versionsFs.Type())
|
||||
return
|
||||
}
|
||||
|
||||
type versionWithMtime struct {
|
||||
name string
|
||||
mtime time.Time
|
||||
}
|
||||
func findAllVersions(fs fs.Filesystem, filePath string) []string {
|
||||
inFolderPath := filepath.Dir(filePath)
|
||||
file := filepath.Base(filePath)
|
||||
|
||||
func versionsToVersionsWithMtime(fs fs.Filesystem, versions []string) []versionWithMtime {
|
||||
versionsWithMtimes := make([]versionWithMtime, 0, len(versions))
|
||||
|
||||
for _, version := range versions {
|
||||
if stat, err := fs.Stat(version); err != nil {
|
||||
// Welp, assume it's gone?
|
||||
continue
|
||||
} else {
|
||||
versionsWithMtimes = append(versionsWithMtimes, versionWithMtime{
|
||||
name: version,
|
||||
mtime: stat.ModTime(),
|
||||
})
|
||||
}
|
||||
// Glob according to the new file~timestamp.ext pattern.
|
||||
pattern := filepath.Join(inFolderPath, TagFilename(file, TimeGlob))
|
||||
versions, err := fs.Glob(pattern)
|
||||
if err != nil {
|
||||
l.Warnln("globbing:", err, "for", pattern)
|
||||
return nil
|
||||
}
|
||||
versions = util.UniqueTrimmedStrings(versions)
|
||||
sort.Strings(versions)
|
||||
|
||||
sort.Slice(versionsWithMtimes, func(i, j int) bool {
|
||||
return versionsWithMtimes[i].mtime.Before(versionsWithMtimes[j].mtime)
|
||||
})
|
||||
|
||||
return versionsWithMtimes
|
||||
return versions
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STDISCOSRV" "1" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STRELAYSRV" "1" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-RELAY" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-RELAY" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-REST-API" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-SECURITY" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING" "1" "Jun 03, 2019" "v1" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "Jun 11, 2019" "v1" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.
|
||||
|
||||
Reference in New Issue
Block a user