Compare commits

...

66 Commits

Author SHA1 Message Date
Jakob Borg
a79de840bd gui, man, authors: Update docs, translations, and contributors 2020-01-14 08:01:03 +01:00
Jakob Borg
f454e8b609 build: go mod tidy 2020-01-14 07:59:31 +01:00
dependabot-preview[bot]
c6cef168a5 build(deps): bump github.com/oschwald/geoip2-golang from 1.3.0 to 1.4.0 (#6245)
Bumps [github.com/oschwald/geoip2-golang](https://github.com/oschwald/geoip2-golang) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/oschwald/geoip2-golang/releases)
- [Commits](https://github.com/oschwald/geoip2-golang/compare/v1.3.0...v1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-14 09:57:08 +04:00
dependabot-preview[bot]
4de6b94de7 build(deps): bump github.com/pkg/errors from 0.8.1 to 0.9.0 (#6267)
Bumps [github.com/pkg/errors](https://github.com/pkg/errors) from 0.8.1 to 0.9.0.
- [Release notes](https://github.com/pkg/errors/releases)
- [Commits](https://github.com/pkg/errors/compare/v0.8.1...v0.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-13 12:18:32 +04:00
Simon Frei
08bb730ad0 lib/db: Wrap errors from leveldb iterators (fixes #6263) (#6264) 2020-01-12 09:06:31 +04:00
Jakob Borg
1b52197f71 gui, man, authors: Update docs, translations, and contributors 2020-01-11 12:56:45 +01:00
Simon Frei
71882765f2 lib/events, lib/model: Unflake test and prevent deadlock on event unsubscribing (#6261) 2020-01-11 08:14:05 +01:00
Simon Frei
119d76d035 lib/stun: Refactor to remove unnecessary logging (fixes #6213) (#6260) 2020-01-10 10:24:15 +01:00
Simon Frei
08753ccabe lib/model: Reset queue after all pulling is done (fixes #5867) (#6256) 2020-01-08 12:21:22 +01:00
Dan
ceb9475668 etc: Fix misleading comment in discosrv options file (#6258) 2020-01-06 22:43:41 +00:00
Simon Frei
f56a5545d4 gui, lib/model: Prevent negative sync completion (fixes #4570) (#6248) 2020-01-03 14:07:57 +01:00
Simon Frei
7a8e73d599 build, pmp: Replace fork with upstream for go-nat-pmp and tidy go.mod (#6247) 2020-01-03 12:39:59 +01:00
Simon Frei
1e69c31d87 gui: Missing line break (fixes #6240) (#6241) 2019-12-28 20:51:45 +01:00
Jakob Borg
3dc3e01f80 docker: Fix HOME setting (fixes #6234) (#6235)
su-exec sets $HOME, and we used to have this env call in there to fix
that up. It disappeared in the latest entrypoint.sh rewrite.
2019-12-22 17:50:16 +01:00
Jakob Borg
fb5f1bb56a golangci: Skip godox 2019-12-18 11:33:36 +01:00
dependabot-preview[bot]
0f1e0eff05 build(deps): bump github.com/mattn/go-isatty from 0.0.10 to 0.0.11 (#6231)
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.10 to 0.0.11.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.10...v0.0.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-16 07:52:02 +00:00
Jakob Borg
a963bc8b86 lib/upgrade: Let Mac load .zip archives (#6230)
There is no need to do this switch based on the current OS, instead do
it based on what the archive actually appears to be.

(Tested; works.)
2019-12-16 07:21:18 +01:00
Simon Frei
de64ffddab lib/api: Prevent leaks in tests (#6227) 2019-12-13 09:26:41 +01:00
Simon Frei
8140350094 lib/syncthing: Expose backend instead of lowlevel (#6224) 2019-12-12 16:50:09 +01:00
Simon Frei
82ed8e702c gui: Prevent spurious api call (fixes #6222) (#6223) 2019-12-11 20:37:35 +01:00
Artur Zubilewicz
fca2876795 gui: Fix a typo in a class name (#6220) 2019-12-11 08:34:51 +01:00
Artur Zubilewicz
633ddba2b2 gui: Allow to degrade 'Automatic upgrades' option to 'No upgrades' (fixes #6044) (#6168) 2019-12-11 08:33:18 +01:00
Jakob Borg
325c3c1fa7 lib/db, lib/protocol: Compact FileInfo and BlockInfo alignment (#6215)
* lib/db, lib/protocol: Compact FileInfo and BlockInfo alignment

This fixes the following two lint warnings

    FileInfo: struct of size 160 bytes could be of size 136 bytes
    BlockInfo: struct of size 48 bytes could be of size 40 bytes

by reordering fields in alignment order (64 bit fields, then 32 bit
fields, then 16 bit fields (if any), then small ones). The end result is
a slightly less aesthetically pleasing struct field order, but since
these are the objects we often juggle in bulk and keep large queues of I
think it's worth it.

It's a micro optimization, but a cheap one.
2019-12-08 13:31:26 +01:00
Jakob Borg
be0508cf26 lib/model, lib/protocol: Use error handling to avoid panic on non-started folder (fixes #6174) (#6212)
This adds error returns to model methods called by the protocol layer.
Returning an error will cause the connection to be torn down as the
message couldn't be handled. Using this to signal that a folder isn't
currently available will then cause a reconnection a few moments later,
when it'll hopefully work better.

Tested manually by running with STRECHECKDBEVERY=0 on a nontrivially
sized setup. This panics reliably before this patch, but just causes a
disconnect/reconnect now.
2019-12-04 10:46:55 +01:00
Simon Frei
6fd5e78740 lib: Consistently unsubscribe from config-wrapper (fixes #6133) (#6205) 2019-12-04 07:15:00 +01:00
Jakob Borg
a9e490adfa cmd/ursrv: Show more architectures (fixes #6211) 2019-12-03 21:34:32 +01:00
Jakob Borg
d8e7e92512 build: Generalize code signing
Should, at the least, also codesign when building zip for mac.
2019-12-03 08:37:43 +01:00
Paul Brit
eca156fd7f lib/osutil: Increase maxfiles on macOS properly (fixes #6206) (#6207) 2019-12-03 07:26:22 +01:00
Marcus Legendre
b3fd9a8d53 lib/ignore: Don't create empty ".stignore" files (fixes #6190) (#6197)
This will:

1. prevent creation of a new .stignore if there are no ignore patterns
2. delete an existing .stignore if all ignore patterns are removed
2019-12-02 08:19:02 +01:00
Simon Frei
0bec01b827 lib/db: Remove *instance by making everything *Lowlevel (#6204) 2019-12-02 08:18:04 +01:00
Jakob Borg
e82a7e3dfa all: Propagate errors from NamespacedKV (#6203)
As foretold by the prophecy, "once the database refactor is merged, then
shall appear a request to propagate errors from the store known
throughout the land as the NamedspacedKV, and it shall be good".
2019-11-30 13:03:24 +01:00
Jakob Borg
928767e316 lib/model: gofmt lol :( 2019-11-29 09:29:59 +01:00
Evgeny Kuznetsov
1c277fc096 lib/fs: Add case-insensitive fakefs (#6074) 2019-11-29 09:17:42 +01:00
Jakob Borg
c71116ee94 Implement database abstraction, error checking (ref #5907) (#6107)
This PR does two things, because one lead to the other:

- Move the leveldb specific stuff into a small "backend" package that
defines a backend interface and the leveldb implementation. This allows,
potentially, in the future, switching the db implementation so another
KV store should we wish to do so.

- Add proper error handling all along the way. The db and backend
packages are now errcheck clean. However, I drew the line at modifying
the FileSet API in order to keep this manageable and not continue
refactoring all of the rest of Syncthing. As such, the FileSet methods
still panic on database errors, except for the "database is closed"
error which is instead handled by silently returning as quickly as
possible, with the assumption that we're anyway "on the way out".
2019-11-29 09:11:52 +01:00
Robin Schoonover
a5bbc12625 gui: Sort versions by date in restore dropdown (#6201) 2019-11-29 08:32:25 +01:00
Simon Frei
606154b183 lib/model: Also send folder summary from sync-preparing (ref #6028) (#6202) 2019-11-29 08:30:17 +01:00
Jakob Borg
f9c380d45b cmd/syncthing: Implement log rotation (fixes #6104) (#6198)
Since we've taken upon ourselves to create a log file by default on
Windows, this adds proper management of that log file. There are two new
options:

  -log-max-old-files="3"    Number of old files to keep (zero to keep only current).
  -log-max-size="10485760"  Maximum size of any file (zero to disable log rotation).

The default values result in four files (syncthing.log, synchting.0.log,
..., syncthing.3.log) each up to 10 MiB in size. To not use log rotation
at all, the user can say --log-max-size=0.
2019-11-28 12:26:14 +01:00
Aman Gupta
a04f54a16a lib/upnp: Use simple continue in loop (#6192) 2019-11-26 22:55:34 +00:00
Aman Gupta
509d123251 lib/upnp: Ensure uPnP http requests have trailing \r\n (#6193) 2019-11-26 22:54:46 +00:00
Simon Frei
b32821a586 lib/config, lib/connections: Remove ListenAddresses hack (#6188) 2019-11-26 17:07:25 +01:00
Otiel
8ced8ad562 github: bump Syncthing version to v1... (#6191) 2019-11-26 08:25:50 +00:00
Simon Frei
1bae4b7f50 all: Use context in lib/dialer (#6177)
* all: Use context in lib/dialer

* a bit slimmer

* https://github.com/syncthing/syncthing/pull/5753

* bot

* missed adding debug.go

* errors.Cause

* simultaneous dialing

* anti-leak
2019-11-26 07:39:51 +00:00
Jakob Borg
4e151d380c lib/versioner: Reduce surface area (#6186)
* lib/versioner: Reduce surface area

This is a refactor while I was anyway rooting around in the versioner.
Instead of exporting every possible implementation and the factory and
letting the caller do whatever, this now encapsulates all that and
exposes a New() that takes a config.VersioningConfiguration.

Given that and that we don't know (from the outside) how a versioner
works or what state it keeps, we now just construct it once per folder
and keep it around. Previously it was recreated for each restore
request.

* unparam

* wip
2019-11-26 07:39:31 +00:00
Simon Frei
f747ba6d69 lib/ignore: Keep skipping ignored dirs for rooted patterns (#6151)
* lib/ignore: Keep skipping ignored dirs for rooted patterns

* review

* clarify comment and lint

* glob.QuoteMeta

* review
2019-11-26 07:37:41 +00:00
Simon Frei
33258b06f4 lib/connections: Dialer code deduplication (#6187) 2019-11-26 07:36:58 +00:00
Jakob Borg
4340589501 Merge branch 'release'
Discarding the commit on that branch...
2019-11-25 11:10:09 +01:00
Simon Frei
4d368a37e2 lib/model, lib/protocol: Add contexts sending indexes and download-progress (#6176) 2019-11-25 11:07:36 +01:00
dependabot-preview[bot]
999647b7d6 build(deps): bump github.com/urfave/cli from 1.22.1 to 1.22.2 (#6183)
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.1 to 1.22.2.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.1...v1.22.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-25 11:05:55 +01:00
Simon Frei
cf312abc72 lib: Wrap errors with errors.Wrap instead of fmt.Errorf (#6181) 2019-11-23 15:20:54 +00:00
Mateusz Ż
e2f6d0d6c4 UI enhancements on mobile (#6180)
* Set fallback font for log viewer

* Enable logo scaling in About view

* Don't split "dependency list" into 2 columns on mobile
2019-11-23 12:25:25 +00:00
Simon Frei
65d4dd32cb lib/model: Also handle ServeBackground (#6173) 2019-11-22 21:30:16 +01:00
Simon Frei
de886b3f22 lib/relay: Prevent lock nil deref when creation dynamic client (#6175) 2019-11-21 17:45:06 +00:00
Jakob Borg
8c91e012c7 Merge branch 'release'
* release:
  gui: Prioritize non-idle folder states (fixes #6169) (#6170)
2019-11-21 09:36:03 +01:00
Simon Frei
57d668ed1d lib/config: Do introductions in a single config change (#6162) 2019-11-21 08:41:41 +01:00
Simon Frei
90d85fd0a2 lib: Replace done channel with contexts in and add names to util services (#6166) 2019-11-21 08:41:15 +01:00
Simon Frei
552ea68672 gui: Prioritize non-idle folder states (fixes #6169) (#6170) 2019-11-20 19:06:03 +01:00
Artur Zubilewicz
80eac473d9 gui: Make 'Nearby devices' look like links (fixes #6057) (#6165)
- The 'help-box' with nearby devices will appear only when there are
  any nearby devices
- IDs of nearby devices will look like links (i.e. underlined when
  hovered over)
2019-11-19 22:15:27 +01:00
Ruslan Yevdokymov
c1db8b2680 gui: Add upgrade confirmation dialog (fixes #5887) (#6167) 2019-11-19 22:05:41 +01:00
Jakob Borg
df866e10c8 gui: Increase padding a bit again (ref #6153)
I change my mind on this, the modals need *some* padding to not look weird.
2019-11-19 22:03:31 +01:00
Simon Frei
0d14ee4142 lib/model: Don't info log repeat pull errors (#6149) 2019-11-19 09:56:53 +01:00
Simon Frei
28edf2f5bb lib/model: Keep fmut locked while adding/starting/restarting folders (#6156) 2019-11-18 21:15:26 +01:00
Jakob Borg
e7100bc573 golang-ci: Skip "cognitive complexity" check for now 2019-11-17 08:54:59 +01:00
Simon Frei
5edf4660e2 lib/model: Prevent cleanup-race in testing (ref #6152) (#6155) 2019-11-14 23:08:40 +01:00
Domenic Horner
a5699d40a8 gui: Decrease padding on the panel and modal bodies (#6153)
This allows better viewing when on a condensed screen, and reduces screen real estate slightly.
2019-11-13 15:14:00 +01:00
Simon Frei
f80ce17497 lib/model: In tests prevent goroutine leaks and increase timeouts (#6152) 2019-11-13 10:21:54 +01:00
Simon Frei
ce72bee576 lib/model: Simplify pull error/retry logic (fixes #6139) (#6141) 2019-11-11 15:50:28 +01:00
197 changed files with 6485 additions and 3685 deletions

View File

@@ -36,7 +36,7 @@ its entirety.
### Version Information
Syncthing Version: v0.x.y
Syncthing Version: v1.x.y
OS Version: Windows 7 / Ubuntu 14.04 / ...
Browser Version: (if applicable, for GUI issues)

View File

@@ -15,6 +15,8 @@ linters:
- gocyclo
- funlen
- wsl
- gocognit
- godox
service:
golangci-lint-version: 1.21.x

27
AUTHORS
View File

@@ -16,9 +16,11 @@
Aaron Bieber (qbit) <qbit@deftly.net>
Adam Piggott (ProactiveServices) <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com> <ProactiveServices@users.noreply.github.com> <adam@proactiveservices.co.uk>
Adel Qalieh (adelq) <aqalieh95@gmail.com> <adelq@users.noreply.github.com>
Alan Pope <alan@popey.com>
Alessandro G. (alessandro.g89) <alessandro.g89@gmail.com>
Alexander Graf (alex2108) <register-github@alex-graf.de>
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
Aman Gupta <aman@tmm1.net>
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
andresvia <andres.via@gmail.com>
Andrew Dunham (andrew-d) <andrew@du.nham.ca>
@@ -29,8 +31,11 @@ andyleap <andyleap@gmail.com>
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
Antony Male (canton7) <antony.male@gmail.com>
Aranjedeath <Aranjedeath@users.noreply.github.com>
Arkadiusz Tymiński <gevleeog@gmail.com>
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
Artur Zubilewicz <AkaZecik@users.noreply.github.com>
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks>
Aurélien Rainone <476650+arl@users.noreply.github.com>
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
Bart De Vries (mogwa1) <devriesb@gmail.com>
Ben Curthoys (bencurthoys) <ben@bencurthoys.com>
@@ -41,6 +46,7 @@ Benedikt Heine (bebehei) <bebe@bebehei.de>
Benedikt Morbach <benedikt.morbach@googlemail.com>
Benno Fünfstück <benno.fuenfstueck@gmail.com>
Benny Ng (tpng) <benny.tpng@gmail.com>
boomsquared <54829195+boomsquared@users.noreply.github.com>
Boris Rybalkin <ribalkin@gmail.com>
Brandon Philips (philips) <brandon@ifup.org>
Brendan Long (brendanlong) <self@brendanlong.com>
@@ -49,13 +55,16 @@ Caleb Callaway (cqcallaw) <enlightened.despot@gmail.com>
Carsten Hagemann (carstenhag) <moter8@gmail.com> <carsten@chagemann.de>
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
Cedric Staniewski (xduugu) <cedric@gmx.ca>
chenrui <rui@meetup.com>
Chris Howie (cdhowie) <me@chrishowie.com>
Chris Joel (cdata) <chris@scriptolo.gy>
Chris Tonkinson <chris@masterbran.ch>
chucic <chucic@seznam.cz>
Colin Kennedy (moshen) <moshen.colin@gmail.com>
Cromefire_ <tim.l@nghorst.net>
Cromefire_ <tim.l@nghorst.net> <26320625+cromefire@users.noreply.github.com>
Cyprien Devillez <cypx@users.noreply.github.com>
Dale Visser <dale.visser@live.com>
Dan <benda.daniel@gmail.com>
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
Daniel Harte (norgeous) <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
Daniel Martí (mvdan) <mvdan@mvdan.cc>
@@ -63,11 +72,12 @@ Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
Denis A. (dva) <denisva@gmail.com>
Dennis Wilson (snnd) <dw@risu.io>
dependabot-preview[bot] <dependabot-preview[bot]@users.noreply.github.com>
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>
derekriemer <derek.riemer@colorado.edu>
desbma <desbma@users.noreply.github.com>
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
Domenic Horner <domenic@tgxn.net>
Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
Elliot Huffman <thelich2@gmail.com>
@@ -80,6 +90,7 @@ Felix Unterpaintner (bigbear2nd) <bigbear2nd@gmail.com>
Francois-Xavier Gsell (zukoo) <fxgsell@gmail.com>
Frank Isemann (fti7) <frank@isemann.name>
georgespatton <georgespatton@users.noreply.github.com>
ghjklw <malo@jaffre.info>
Gilli Sigurdsson (gillisig) <gilli@vx.is>
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
Han Boetes <han@boetes.org>
@@ -88,9 +99,11 @@ Heiko Zuerker (Smiley73) <heiko@zuerker.org>
Hugo Locurcio <hugo.locurcio@hugo.pro>
Iain Barnett <iainspeed@gmail.com>
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
Ilya Brin <464157+ilyabrin@users.noreply.github.com>
Iskander Sharipov (Alex) <quasilyte@gmail.com>
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
Jacek Szafarkiewicz (hadogenes) <szafar@linux.pl>
Jacob <jyundt@gmail.com>
Jake Peterson (acogdev) <jake@acogdev.com>
Jakob Borg (calmh) <jakob@nym.se> <jakob@kastelo.net>
James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
@@ -98,6 +111,7 @@ janost <janost@tuta.io>
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
jaseg <githubaccount@jaseg.net>
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
jelle van der Waa <jelle@vdwaa.nl>
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
Jerry Jacobs (xor-gate) <jerry.jacobs@xor-gate.org> <xor-gate@users.noreply.github.com>
Jochen Voss (seehuhn) <voss@seehuhn.de>
@@ -124,13 +138,16 @@ Leo Arias (elopio) <yo@elopio.net>
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
Lode Hoste (Zillode) <zillode@zillode.be>
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
Lukas Lihotzki <lukas@lihotzki.de>
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
Marc Pujol (kilburn) <kilburn@la3.org>
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
marco-m <marco.molteni@laposte.net>
Marcus Legendre <marcus.legendre@gmail.com>
Mark Pulford (mpx) <mark@kyne.com.au>
Mateusz Naściszewski (mateon1) <matin1111@wp.pl>
Mateusz Ż <thedead4fun@live.com>
Matic Potočnik <hairyfotr@gmail.com>
Matt Burke (burkemw3) <mburke@amplify.com> <burkemw3@gmail.com>
Matt Robenolt <matt@ydekproductions.com>
@@ -153,9 +170,13 @@ Niels Peter Roest (Niller303) <nielsproest@hotmail.com> <seje.niels@hotmail.com>
Nils Jakobi (thunderstorm99) <jakobi.nils@gmail.com>
Nitroretro <43112364+Nitroretro@users.noreply.github.com>
NoLooseEnds <jon.koslung@gmail.com>
Oliver Freyermuth <o.freyermuth@googlemail.com>
otbutz <tbutz@optitool.de>
Otiel <Otiel@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>
Paul Brit <paulbrit44@gmail.com>
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
Paweł Rozlach <vespian@users.noreply.github.com>
perewa <cavalcante.ten@gmail.com>
@@ -171,9 +192,11 @@ Piotr Bejda (piobpl) <piotrb10@gmail.com>
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
Richard Hartmann <RichiH@users.noreply.github.com>
Robert Carosi (nov1n) <robert@carosi.nl>
Robin Schoonover <robin@cornhooves.org>
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
Ross Smith II (rasa) <ross@smithii.com>
rubenbe <github-com-00ff86@vandamme.email>
Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com>
Ryan Sullivan (KayoticSully) <kayoticsully@gmail.com>
Sacheendra Talluri (sacheendra) <sacheendra.t@gmail.com>
Scott Klupfel (kluppy) <kluppy@going2blue.com>

View File

@@ -489,10 +489,7 @@ func buildTar(target target) {
}
build(target, tags)
if goos == "darwin" {
macosCodesign(target.BinaryName())
}
codesign(target)
for i := range target.archiveFiles {
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.BinaryName(), 1)
@@ -515,10 +512,7 @@ func buildZip(target target) {
}
build(target, tags)
if goos == "windows" {
windowsCodesign(target.BinaryName())
}
codesign(target)
for i := range target.archiveFiles {
target.archiveFiles[i].src = strings.Replace(target.archiveFiles[i].src, "{{binary}}", target.BinaryName(), 1)
@@ -1179,6 +1173,15 @@ func zipFile(out string, files []archiveFile) {
}
}
func codesign(target target) {
switch goos {
case "windows":
windowsCodesign(target.BinaryName())
case "darwin":
macosCodesign(target.BinaryName())
}
}
func macosCodesign(file string) {
if pass := os.Getenv("CODESIGN_KEYCHAIN_PASS"); pass != "" {
bs, err := runError("security", "unlock-keychain", "-p", pass)

View File

@@ -1,3 +1,3 @@
# Default settings for syncthing-relaysrv (strelaysrv).
# Default settings for syncthing-discosrv (stdiscosrv).
## Add Options here:
DISCOSRV_OPTS=

View File

@@ -13,11 +13,15 @@ import (
"time"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/protocol"
)
func dump(ldb *db.Lowlevel) {
it := ldb.NewIterator(nil, nil)
func dump(ldb backend.Backend) {
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
log.Fatal(err)
}
for it.Next() {
key := it.Key()
switch key[0] {

View File

@@ -10,8 +10,10 @@ import (
"container/heap"
"encoding/binary"
"fmt"
"log"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
)
type SizedElement struct {
@@ -37,11 +39,14 @@ func (h *ElementHeap) Pop() interface{} {
return x
}
func dumpsize(ldb *db.Lowlevel) {
func dumpsize(ldb backend.Backend) {
h := &ElementHeap{}
heap.Init(h)
it := ldb.NewIterator(nil, nil)
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
log.Fatal(err)
}
var ele SizedElement
for it.Next() {
key := it.Key()

View File

@@ -10,8 +10,10 @@ import (
"bytes"
"encoding/binary"
"fmt"
"log"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -31,7 +33,7 @@ type sequenceKey struct {
sequence uint64
}
func idxck(ldb *db.Lowlevel) (success bool) {
func idxck(ldb backend.Backend) (success bool) {
folders := make(map[uint32]string)
devices := make(map[uint32]string)
deviceToIDs := make(map[string]uint32)
@@ -42,7 +44,10 @@ func idxck(ldb *db.Lowlevel) (success bool) {
var localDeviceKey uint32
success = true
it := ldb.NewIterator(nil, nil)
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
log.Fatal(err)
}
for it.Next() {
key := it.Key()
switch key[0] {

View File

@@ -13,7 +13,7 @@ import (
"os"
"path/filepath"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
)
func main() {
@@ -30,7 +30,7 @@ func main() {
path = filepath.Join(defaultConfigDir(), "index-v0.14.0.db")
}
ldb, err := db.OpenRO(path)
ldb, err := backend.OpenLevelDBRO(path)
if err != nil {
log.Fatal(err)
}

View File

@@ -7,6 +7,7 @@ package main
import (
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"encoding/json"
"flag"
@@ -480,7 +481,7 @@ func handleRelayTest(request request) {
if debug {
log.Println("Request for", request.relay)
}
if !client.TestRelay(request.relay.uri, []tls.Certificate{testCert}, time.Second, 2*time.Second, 3) {
if !client.TestRelay(context.TODO(), request.relay.uri, []tls.Certificate{testCert}, time.Second, 2*time.Second, 3) {
if debug {
log.Println("Test for relay", request.relay, "failed")
}

View File

@@ -4,6 +4,7 @@ package main
import (
"bufio"
"context"
"crypto/tls"
"flag"
"log"
@@ -19,6 +20,8 @@ import (
)
func main() {
ctx := context.Background()
log.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags | log.Lshortfile)
@@ -76,7 +79,7 @@ func main() {
}()
for {
conn, err := client.JoinSession(<-recv)
conn, err := client.JoinSession(ctx, <-recv)
if err != nil {
log.Fatalln("Failed to join", err)
}
@@ -90,13 +93,13 @@ func main() {
log.Fatal(err)
}
invite, err := client.GetInvitationFromRelay(uri, id, []tls.Certificate{cert}, 10*time.Second)
invite, err := client.GetInvitationFromRelay(ctx, uri, id, []tls.Certificate{cert}, 10*time.Second)
if err != nil {
log.Fatal(err)
}
log.Println("Received invitation", invite)
conn, err := client.JoinSession(invite)
conn, err := client.JoinSession(ctx, invite)
if err != nil {
log.Fatalln("Failed to join", err)
}
@@ -104,7 +107,7 @@ func main() {
connectToStdio(stdin, conn)
log.Println("Finished", conn.RemoteAddr(), conn.LocalAddr())
} else if test {
if client.TestRelay(uri, []tls.Certificate{cert}, time.Second, 2*time.Second, 4) {
if client.TestRelay(ctx, uri, []tls.Certificate{cert}, time.Second, 2*time.Second, 4) {
log.Println("OK")
} else {
log.Println("FAIL")

View File

@@ -154,6 +154,8 @@ type RuntimeOptions struct {
browserOnly bool
hideConsole bool
logFile string
logMaxSize int
logMaxFiles int
auditEnabled bool
auditFile string
paused bool
@@ -180,6 +182,8 @@ func defaultRuntimeOptions() RuntimeOptions {
cpuProfile: os.Getenv("STCPUPROFILE") != "",
stRestarting: os.Getenv("STRESTART") != "",
logFlags: log.Ltime,
logMaxSize: 10 << 20, // 10 MiB
logMaxFiles: 3, // plus the current one
}
if os.Getenv("STTRACE") != "" {
@@ -222,6 +226,8 @@ func parseCommandLineOptions() RuntimeOptions {
flag.BoolVar(&options.paused, "paused", false, "Start with all devices and folders paused")
flag.BoolVar(&options.unpaused, "unpaused", false, "Start with all devices and folders unpaused")
flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (still always logs to stdout). Cannot be used together with -no-restart/STNORESTART environment variable.")
flag.IntVar(&options.logMaxSize, "log-max-size", options.logMaxSize, "Maximum size of any file (zero to disable log rotation).")
flag.IntVar(&options.logMaxFiles, "log-max-old-files", options.logMaxFiles, "Number of old files to keep (zero to keep only current).")
flag.StringVar(&options.auditFile, "auditfile", options.auditFile, "Specify audit file (use \"-\" for stdout, \"--\" for stderr)")
flag.BoolVar(&options.allowNewerConfig, "allow-newer-config", false, "Allow loading newer than current config version")
if runtime.GOOS == "windows" {
@@ -480,7 +486,7 @@ func checkUpgrade() upgrade.Release {
func performUpgrade(release upgrade.Release) {
// Use leveldb database locks to protect against concurrent upgrades
_, err := syncthing.OpenGoleveldb(locations.Get(locations.Database), config.TuningAuto)
_, err := syncthing.OpenDBBackend(locations.Get(locations.Database), config.TuningAuto)
if err == nil {
err = upgrade.To(release)
if err != nil {
@@ -512,7 +518,7 @@ func upgradeViaRest() error {
r.Header.Set("X-API-Key", cfg.GUI().APIKey)
tr := &http.Transport{
Dial: dialer.Dial,
DialContext: dialer.DialContext,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
@@ -572,7 +578,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
}
dbFile := locations.Get(locations.Database)
ldb, err := syncthing.OpenGoleveldb(dbFile, cfg.Options().DatabaseTuning)
ldb, err := syncthing.OpenDBBackend(dbFile, cfg.Options().DatabaseTuning)
if err != nil {
l.Warnln("Error opening database:", err)
os.Exit(1)

View File

@@ -9,10 +9,12 @@ package main
import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strings"
"syscall"
@@ -48,7 +50,15 @@ func monitorMain(runtimeOptions RuntimeOptions) {
logFile := runtimeOptions.logFile
if logFile != "-" {
var fileDst io.Writer = newAutoclosedFile(logFile, logFileAutoCloseDelay, logFileMaxOpenTime)
var fileDst io.Writer
if runtimeOptions.logMaxSize > 0 {
open := func(name string) (io.WriteCloser, error) {
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime), nil
}
fileDst = newRotatedFile(logFile, open, int64(runtimeOptions.logMaxSize), runtimeOptions.logMaxFiles)
} else {
fileDst = newAutoclosedFile(logFile, logFileAutoCloseDelay, logFileMaxOpenTime)
}
if runtime.GOOS == "windows" {
// Translate line breaks to Windows standard
@@ -317,6 +327,81 @@ func restartMonitorWindows(args []string) error {
return cmd.Start()
}
// rotatedFile keeps a set of rotating logs. There will be the base file plus up
// to maxFiles rotated ones, each ~ maxSize bytes large.
type rotatedFile struct {
name string
create createFn
maxSize int64 // bytes
maxFiles int
currentFile io.WriteCloser
currentSize int64
}
// the createFn should act equivalently to os.Create
type createFn func(name string) (io.WriteCloser, error)
func newRotatedFile(name string, create createFn, maxSize int64, maxFiles int) *rotatedFile {
return &rotatedFile{
name: name,
create: create,
maxSize: maxSize,
maxFiles: maxFiles,
}
}
func (r *rotatedFile) Write(bs []byte) (int, error) {
// Check if we're about to exceed the max size, and if so close this
// file so we'll start on a new one.
if r.currentSize+int64(len(bs)) > r.maxSize {
r.currentFile.Close()
r.currentFile = nil
r.currentSize = 0
}
// If we have no current log, rotate old files out of the way and create
// a new one.
if r.currentFile == nil {
r.rotate()
fd, err := r.create(r.name)
if err != nil {
return 0, err
}
r.currentFile = fd
}
n, err := r.currentFile.Write(bs)
r.currentSize += int64(n)
return n, err
}
func (r *rotatedFile) rotate() {
// The files are named "name", "name.0", "name.1", ...
// "name.(r.maxFiles-1)". Increase the numbers on the
// suffixed ones.
for i := r.maxFiles - 1; i > 0; i-- {
from := numberedFile(r.name, i-1)
to := numberedFile(r.name, i)
err := os.Rename(from, to)
if err != nil && !os.IsNotExist(err) {
fmt.Println("LOG: Rotating logs:", err)
}
}
// Rename the base to base.0
err := os.Rename(r.name, numberedFile(r.name, 0))
if err != nil && !os.IsNotExist(err) {
fmt.Println("LOG: Rotating logs:", err)
}
}
// numberedFile adds the number between the file name and the extension.
func numberedFile(name string, num int) string {
ext := filepath.Ext(name) // contains the dot
withoutExt := name[:len(name)-len(ext)]
return fmt.Sprintf("%s.%d%s", withoutExt, num, ext)
}
// An autoclosedFile is an io.WriteCloser that opens itself for appending on
// Write() and closes itself after an interval of no writes (closeDelay) or
// when the file has been open for too long (maxOpenTime). A call to Write()

View File

@@ -7,6 +7,7 @@
package main
import (
"io"
"io/ioutil"
"os"
"path/filepath"
@@ -14,6 +15,123 @@ import (
"time"
)
func TestRotatedFile(t *testing.T) {
// Verify that log rotation happens.
dir, err := ioutil.TempDir("", "syncthing")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
open := func(name string) (io.WriteCloser, error) {
return os.Create(name)
}
logName := filepath.Join(dir, "log.txt")
testData := []byte("12345678\n")
maxSize := int64(len(testData) + len(testData)/2)
// We allow the log file plus two rotated copies.
rf := newRotatedFile(logName, open, maxSize, 2)
// Write some bytes.
if _, err := rf.Write(testData); err != nil {
t.Fatal(err)
}
// They should be in the log.
checkSize(t, logName, len(testData))
checkNotExist(t, logName+".0")
// Write some more bytes. We should rotate and write into a new file as the
// new bytes don't fit.
if _, err := rf.Write(testData); err != nil {
t.Fatal(err)
}
checkSize(t, logName, len(testData))
checkSize(t, numberedFile(logName, 0), len(testData))
checkNotExist(t, logName+".1")
// Write another byte. That should fit without causing an extra rotate.
_, _ = rf.Write([]byte{42})
checkSize(t, logName, len(testData)+1)
checkSize(t, numberedFile(logName, 0), len(testData))
checkNotExist(t, numberedFile(logName, 1))
// Write some more bytes. We should rotate and write into a new file as the
// new bytes don't fit.
if _, err := rf.Write(testData); err != nil {
t.Fatal(err)
}
checkSize(t, logName, len(testData))
checkSize(t, numberedFile(logName, 0), len(testData)+1) // the one we wrote extra to, now rotated
checkSize(t, numberedFile(logName, 1), len(testData))
checkNotExist(t, numberedFile(logName, 2))
// Write some more bytes. We should rotate and write into a new file as the
// new bytes don't fit.
if _, err := rf.Write(testData); err != nil {
t.Fatal(err)
}
checkSize(t, logName, len(testData))
checkSize(t, numberedFile(logName, 0), len(testData))
checkSize(t, numberedFile(logName, 1), len(testData)+1)
checkNotExist(t, numberedFile(logName, 2)) // exceeds maxFiles so deleted
}
func TestNumberedFile(t *testing.T) {
// Mostly just illustrates where the number ends up and makes sure it
// doesn't crash without an extension.
cases := []struct {
in string
num int
out string
}{
{
in: "syncthing.log",
num: 42,
out: "syncthing.42.log",
},
{
in: filepath.Join("asdfasdf", "syncthing.log.txt"),
num: 42,
out: filepath.Join("asdfasdf", "syncthing.log.42.txt"),
},
{
in: "syncthing-log",
num: 42,
out: "syncthing-log.42",
},
}
for _, tc := range cases {
res := numberedFile(tc.in, tc.num)
if res != tc.out {
t.Errorf("numberedFile(%q, %d) => %q, expected %q", tc.in, tc.num, res, tc.out)
}
}
}
func checkSize(t *testing.T, name string, size int) {
t.Helper()
info, err := os.Lstat(name)
if err != nil {
t.Fatal(err)
}
if info.Size() != int64(size) {
t.Errorf("%s wrong size: %d != expected %d", name, info.Size(), size)
}
}
func checkNotExist(t *testing.T, name string) {
t.Helper()
_, err := os.Lstat(name)
if !os.IsNotExist(err) {
t.Errorf("%s should not exist", name)
}
}
func TestAutoClosedFile(t *testing.T) {
os.RemoveAll("_autoclose")
defer os.RemoveAll("_autoclose")

View File

@@ -1416,7 +1416,7 @@ func getReport(db *sql.DB) map[string]interface{} {
r["categories"] = categories
r["versions"] = group(byVersion, analyticsFor(versions, 2000), 10)
r["versionPenetrations"] = penetrationLevels(analyticsFor(versions, 2000), []float64{50, 75, 90, 95})
r["platforms"] = group(byPlatform, analyticsFor(platforms, 2000), 5)
r["platforms"] = group(byPlatform, analyticsFor(platforms, 2000), 10)
r["compilers"] = group(byCompiler, analyticsFor(compilers, 2000), 5)
r["builders"] = analyticsFor(builders, 12)
r["distributions"] = analyticsFor(distributions, 10)

11
go.mod
View File

@@ -1,7 +1,6 @@
module github.com/syncthing/syncthing
require (
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
github.com/AudriusButkevicius/recli v0.0.5
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
@@ -19,19 +18,19 @@ require (
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6
github.com/golang/mock v1.3.1 // indirect
github.com/jackpal/gateway v1.0.5
github.com/jackpal/go-nat-pmp v1.0.2
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.2.0
github.com/lucas-clemente/quic-go v0.12.1
github.com/maruel/panicparse v1.3.0
github.com/mattn/go-isatty v0.0.10
github.com/mattn/go-isatty v0.0.11
github.com/minio/sha256-simd v0.1.1
github.com/onsi/ginkgo v1.9.0 // indirect
github.com/onsi/gomega v1.6.0 // indirect
github.com/oschwald/geoip2-golang v1.3.0
github.com/oschwald/maxminddb-golang v1.4.0 // indirect
github.com/oschwald/geoip2-golang v1.4.0
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.0
github.com/prometheus/client_golang v1.2.1
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563
github.com/sasha-s/go-deadlock v0.2.0
@@ -39,7 +38,7 @@ require (
github.com/syncthing/notify v0.0.0-20190709140112-69c7a957d3e2
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
github.com/thejerf/suture v3.0.2+incompatible
github.com/urfave/cli v1.22.1
github.com/urfave/cli v1.22.2
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297

51
go.sum
View File

@@ -1,5 +1,3 @@
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362 h1:l4qGIzSY0WhdXdR74XMYAtfc0Ri/RJVM4p6x/E/+WkA=
github.com/AudriusButkevicius/go-nat-pmp v0.0.0-20160522074932-452c97607362/go.mod h1:CEaBhA5lh1spxbPOELh5wNLKGsVQoahjUhVrJViVK8s=
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=
@@ -58,8 +56,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
@@ -84,6 +80,8 @@ 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 v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc=
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -100,8 +98,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.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucas-clemente/quic-go v0.12.0 h1:TRbvZ6F++sofeGbh+Z2IIyIOhl8KyGnYuA06g2yrHdI=
github.com/lucas-clemente/quic-go v0.12.0/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
github.com/lucas-clemente/quic-go v0.12.1 h1:BPITli+6KnKogtTxBk2aS4okr5dUHz2LtIDAP1b8UL4=
github.com/lucas-clemente/quic-go v0.12.1/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
@@ -114,10 +110,8 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
@@ -138,21 +132,21 @@ 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.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M=
github.com/onsi/gomega v1.6.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 v1.4.0 h1:5/rpmW41qrgSed4wK32rdznbkTSXHcraY2LOMJX4DMc=
github.com/oschwald/maxminddb-golang v1.4.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
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/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.0 h1:J8lpUdobwIeCI7OiSxHqEwJUKvJwicL5+3v1oe2Yb4k=
github.com/pkg/errors v0.9.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
@@ -163,17 +157,11 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCb
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
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/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.4 h1:w8DjqFMJDjuVwdZBQoOozr4MVWOnwF7RcL/7uxBjY78=
github.com/prometheus/procfs v0.0.4/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ=
@@ -195,18 +183,18 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
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/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syncthing/notify v0.0.0-20190709140112-69c7a957d3e2 h1:6tuEEEpg+mxM82E0YingzoXzXXISYR/o/7I9n573LWI=
github.com/syncthing/notify v0.0.0-20190709140112-69c7a957d3e2/go.mod h1:Sn4ChoS7e4FxjCN1XHPVBT43AgnRLbuaB8pEc1Zcdjg=
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/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/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=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
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=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -240,15 +228,12 @@ golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2V
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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76 h1:Dho5nD6R3PcW2SH1or8vS0dszDaXRxIw55lBX7XiE5g=
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/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=

View File

@@ -246,6 +246,14 @@ a.toggler:hover {
text-decoration: none;
}
/**
* Panel padding decrease
*/
.panel-collapse .panel-body {
padding: 5px;
}
/**
* Progress bars with centered text
*/
@@ -348,6 +356,12 @@ ul.three-columns li, ul.two-columns li {
* columns. */
white-space: normal;
}
.two-columns {
-webkit-column-count: 1;
-moz-column-count: 1;
column-count: 1;
}
}
@media (max-width:479px) {
@@ -392,7 +406,7 @@ ul.three-columns li, ul.two-columns li {
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),
@@ -400,4 +414,4 @@ ul.three-columns li, ul.two-columns li {
.btn:not(.panel-heading) + .btn:not(.panel-heading) {
margin-bottom: 1rem;
}
}
}

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Сигурни ли сте, че искате да премахнете устройството {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Сигурни ли сте, че искате да премахнете папката {{label}}?",
"Are you sure you want to restore {%count%} files?": "Сигурни ли сте, че искате да възстановите файла {{count}}?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Автоматично приемане",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматичното обновяване вече предлага избор между стабилни версии и кандидат версии.",
"Automatic upgrades": "Автоматично обновяване",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Всички правата запазени © 2014-2017. Сътрудници:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Създаване на шаблони за игнориране, презаписване на съществуващ файл в {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Опасност!",
"Debugging Facilities": "Дебъг функционалност",
"Default Folder Path": "Път до папка по подразбиране",
"Deleted": "Изтрито",
"Deselect All": "Никое",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство \"{{name}}\" ({{device}}) с адрес {{address}} желае да се свърже. Да бъде ли добавено?",
"Device ID": "Идентификатор на устройство",
@@ -71,6 +75,7 @@
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Устройство, което последно промени обекта",
"Devices": "Устройства",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Деактивирано",
"Disabled periodic scanning and disabled watching for changes": "Периодичните сканирания и наблюденията за промяна са деактивирани.",
"Disabled periodic scanning and enabled watching for changes": "Периодичните сканирания са деактивирани , а наблюденията за промяна са активирани.",
@@ -92,6 +97,7 @@
"Edit Folder": "Промяна на папката",
"Editing": "Променяне",
"Editing {%path%}.": "Променяне на {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Разреши NAT traversal",
"Enable Relaying": "Разреши препращане",
"Enabled": "Активирано",
@@ -141,6 +147,7 @@
"Global State": "Глобално състояние",
"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.",
"Ignore": "Игнорирай",
"Ignore Patterns": "Шаблони за игнориране",
"Ignore Permissions": "Игнорирай правата за достъп",
@@ -164,6 +171,7 @@
"Listeners": "Синхронизиращи устройства",
"Loading data...": "Зареждане на информация...",
"Loading...": "Зареждане...",
"Local Additions": "Local Additions",
"Local Discovery": "Локално откриване",
"Local State": "Локално състояние",
"Local State (Total)": "Локално състояние (общо)",
@@ -220,6 +228,7 @@
"Please wait": "Моля изчакайте",
"Prefix indicating that the file can be deleted if preventing directory removal": "Представка, която индикира, че файлът може да бъде изтрит ако пречи на премахването на папка",
"Prefix indicating that the pattern should be matched without case sensitivity": "Представка, която индикира, че шаблона няма да прави разлика между главни/малки букви",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Преглед",
"Preview Usage Report": "Преглед на статистиката",
"Quick guide to supported patterns": "Бърз наръчник към поддържаните шаблони",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Прегледайте външната документацията за поддържаните командни параметри. ",
"Select All": "Всички",
"Select a version": "Изберете версия",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Избор на най-новата версия",
"Select oldest version": "Избор на най-старата версия",
"Select the devices to share this folder with.": "Изберете устройствата, с които да споделите папката.",
@@ -299,6 +309,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 се рестартира",
"Syncthing is upgrading.": "Syncthing се обновява.",
"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 не е включен, или има проблем с връзката с Интернет. Повторен опит...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing има проблем при обработването на заявката. Моля, презаредете браузъра или рестартирайте Syncthing ако проблемът продължи.",
"Take me back": "Take me back",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Пътят не може да бъде празен.",
"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 devices to share this folder with.",
"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.": "Така се предоставя изключително лесен достъп (четене, редактиране и изтриване) до всеки файл, на компютъра Ви.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Неясно",
"Unshared": "Несподелена",
"Unshared Devices": "Unshared Devices",
"Unused": "Неизползвано",
"Up to Date": "Синхронизирано",
"Updated": "Обновено",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Estàs segur de que vols eliminar el dispositiu {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Estàs segur de que vols eliminar la carpeta {{label}}?",
"Are you sure you want to restore {%count%} files?": "Estàs segur de que vols restaurar {{count}} fitxers?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Auto Acceptar",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "L'actualització automàtica ara ofereix l'elecció entre les versions estables i les versions candidates.",
"Automatic upgrades": "Actualitzacions automàtiques",
"Automatic upgrades are always enabled for candidate releases.": "Les actualitzacions automàtiques sempre estàn activades per a les versions candidates.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 els següents Col·laboradors:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 els següents Col·laboradors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creant patrons a ignorar, sobreescriguent un fitxer que ja existeix a {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Perill!",
"Debugging Facilities": "Utilitats de Depuració",
"Default Folder Path": "Carpeta de la Ruta per Defecte",
"Deleted": "Esborrat",
"Deselect All": "Anul·lar tota la selecció",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Dispositiu",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Dispositiu \"{{name}}\" ({{device}} a l'adreça {{address}}) vol connectar. Afegir nou dispositiu?",
"Device ID": "ID del dispositiu",
@@ -71,6 +75,7 @@
"Device rate limits": "Límits de la tasa del dispositiu",
"Device that last modified the item": "El dispositiu que va modificar el item per última vegada",
"Devices": "Dispositius",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Desactivat",
"Disabled periodic scanning and disabled watching for changes": "Desactivat l'escaneig periòdic i el rastreig continu de canvis",
"Disabled periodic scanning and enabled watching for changes": "Desactivat l'escaneig periòdic i activat el rastreig continu de canvis",
@@ -92,6 +97,7 @@
"Edit Folder": "Editar Carpeta",
"Editing": "Editant",
"Editing {%path%}.": "Editant {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Permetre NAT transversal",
"Enable Relaying": "Permetre Transmissions",
"Enabled": "Activat",
@@ -141,6 +147,7 @@
"Global State": "Estat global",
"Help": "Ajuda",
"Home page": "Pàgina inicial",
"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": "Ignorar",
"Ignore Patterns": "Patrons a ignorar",
"Ignore Permissions": "Permisos a ignorar",
@@ -164,6 +171,7 @@
"Listeners": "Escoltants",
"Loading data...": "Carregant dades...",
"Loading...": "Carregant...",
"Local Additions": "Local Additions",
"Local Discovery": "Descobriment local",
"Local State": "Estat local",
"Local State (Total)": "Estat Local (Total)",
@@ -220,6 +228,7 @@
"Please wait": "Per favor, espere",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix que indica que el fitxer pot ser eliminat encara que estiga restringida l'eliminació del directori",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix que indica que el patró deu coincidir sense tindre en compte les majúscules",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Vista prèvia",
"Preview Usage Report": "Informe d'ús de vista prèvia",
"Quick guide to supported patterns": "Guía ràpida de patrons suportats",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Consulta l'ajuda externa sobre versions per a conéixer els paràmetres de la plantilla de la línia de comandaments.",
"Select All": "Sel·leccionar Tot",
"Select a version": "Seleccionar una versió",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Seleccionar l'última versió",
"Select oldest version": "Seleccionar la versió més antiga",
"Select the devices to share this folder with.": "Selecciona els dispositius amb els que compartir aquesta carpeta.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing és Software Gratuït i Open Source llicenciat com MPL v2.0.",
"Syncthing is restarting.": "Syncthing està reiniciant.",
"Syncthing is upgrading.": "Syncthing està actualitzant-se.",
"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 pareix apagat o hi ha un problema amb la connexió a Internet. Tornant a intentar...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing pareix que té un problema processant la seua sol·licitud. Per favor, refresque la pàgina o reinicie Syncthing si el problema persistix.",
"Take me back": "Porta'm enrere",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "La ruta no pot estar buida.",
"The rate limit must be a non-negative number (0: no limit)": "El llímit del ritme deu ser un nombre no negatiu (0: sense llímit)",
"The rescan interval must be a non-negative number of seconds.": "L'interval de reescaneig deu ser un nombre positiu de segons.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Es reintenta automàticament i es sincronitzaràn quant el resolga l'error.",
"This Device": "Aquest Dispositiu",
"This can easily give hackers access to read and change any files on your computer.": "Açò pot donar accés fàcilment als hackers per a llegir i canviar qualsevol fitxer al teu ordinador.",
@@ -342,6 +354,7 @@
"Unignore": "Designorar",
"Unknown": "Desconegut",
"Unshared": "No compartit",
"Unshared Devices": "Unshared Devices",
"Unused": "No utilitzat",
"Up to Date": "Actualitzat",
"Updated": "Actualitzat",

View File

@@ -1,18 +1,18 @@
{
"A device with that ID is already added.": "Zařízení s tímto ID je již přidáno.",
"A device with that ID is already added.": "Zařízení s takovým identifikátorem už je přidáno.",
"A negative number of days doesn't make sense.": "Záporný počet dní nedává smysl.",
"A new major version may not be compatible with previous versions.": "Nová důležitá verze nemusí být kompatibilní s předchozími verzemi.",
"API Key": "API klíč",
"A new major version may not be compatible with previous versions.": "Nová hlavní verze nemusí být kompatibilní s předchozími verzemi.",
"API Key": "Klíč k API",
"About": "O aplikaci",
"Action": "Akce",
"Actions": "Akce",
"Add": "Přidat",
"Add Device": "Přidat zařízení",
"Add Folder": "Přidat adresář",
"Add Folder": "Přidat složku",
"Add Remote Device": "Přidat vzdálené zařízení",
"Add devices from the introducer to our device list, for mutually shared folders.": "Přidat zařízení ze zavaděče do našeho seznamu zařízení, pro vzájemně sdílené adresáře.",
"Add new folder?": "Přidat nový adresář?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dále bude zvýšen interval plného skenu (60krát, t.j. nová výchozí hodnota 1h). Toto můžete nastavit také později ručně pro každý adresář pokud vyberete Ne.",
"Add devices from the introducer to our device list, for mutually shared folders.": "Přidat zařízení z uvaděče do místního seznamu zařízení a získat tak vzájemně sdílené složky.",
"Add new folder?": "Přidat novou složku?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dále bude prodloužen interval mezi plnými skeny (60krát, t.j. nová výchozí hodnota 1h). V případě, že nyní zvolíte Ne, stále ještě toto později můžete u každé složky jednotlivě ručně upravit.",
"Address": "Adresa",
"Addresses": "Adresy",
"Advanced": "Pokročilé",
@@ -20,140 +20,147 @@
"Advanced settings": "Pokročilá nastavení",
"All Data": "Všechna data",
"Allow Anonymous Usage Reporting?": "Povolit anonymní hlášení o používání?",
"Allowed Networks": "Povolené sítě",
"Alphabetic": "Abecedně",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Verzování obstarává externí příkaz. Musí odstranit soubor ze sdíleného adresáře.",
"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.": "Verzování obstarává externí skript. Musí odstranit soubor ze sdíleného adresáře. Pokud cesta ke skriptu obsahuje mezeru, měla by být v uvozovkách.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Verzování obstarává externí příkaz. Musí odstranit soubor ze sdíleného adresáře.",
"Allowed Networks": "Sítě, ze kterých je umožněn přístup",
"Alphabetic": "Abecední",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Správu verzí obstarává externí příkaz. U toho je třeba, aby neaktuální soubory jím byly odsouvány pryč ze sdílené složky.",
"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.": "Správu verzí obstarává externí příkaz. U toho je třeba, aby neaktuální soubory jím byly odsouvány pryč ze sdílené složky. Pokud popis umístění tohoto příkazu obsahuje mezeru, je třeba popis umístění uzavřít do uvozovek.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Správu verzí obstarává externí příkaz. U toho je třeba, aby neaktuální soubory jím byly odsouvány pryč ze synchronizované složky.",
"Anonymous Usage Reporting": "Anonymní hlášení o používání",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formát anonymního hlášení o používání byl změněn. Chcete přejít na nový formát?",
"Any devices configured on an introducer device will be added to this device as well.": "Jakákoliv zařízení nakonfigurovaná na zavaděči budou přidána také na toto zařízení.",
"Are you sure you want to remove device {%name%}?": "Skutečně chcete odebrat zařízení {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Skutečně chcete odebrat adresář {{label}}?",
"Any devices configured on an introducer device will be added to this device as well.": "Jakákoliv zařízení, nastavená na zavaděči, budou přidána také na toto zařízení.",
"Are you sure you want to remove device {%name%}?": "Opravdu chcete odebrat zařízení {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Opravdu chcete odebrat složku {{label}}?",
"Are you sure you want to restore {%count%} files?": "Opravdu chcete obnovit {{count}} souborů?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Přijmout automaticky",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatická aktualizace nyní nabízí volbu mezi stabilními vydáními a kandidáty na vydání.",
"Automatic Crash Reporting": "Automatické hlášení pádů",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatická aktualizace nyní nabízí volbu mezi stabilními vydáními a kandidáty na ně.",
"Automatic upgrades": "Automatické aktualizace",
"Automatic upgrades are always enabled for candidate releases.": "Automatické aktualizace jsou vždy povolené u kandidátů na vydání.",
"Automatically create or share folders that this device advertises at the default path.": "Automaticky vytvářet nebo sdílet adresáře, které toto zařízení odesílá ve výchozí cestě.",
"Automatically create or share folders that this device advertises at the default path.": "Automaticky vytvářet nebo sdílet složky, které toto zařízení propaguje ve výchozím popisu umístě.",
"Available debug logging facilities:": "Dostupná logovací zařízení pro ladění:",
"Be careful!": "Pozor!",
"Be careful!": "Buďte opatrní!",
"Bugs": "Chyby",
"CPU Utilization": "Využití CPU",
"Changelog": "Changelog",
"CPU Utilization": "Využití procesoru",
"Changelog": "Seznam změn",
"Clean out after": "Vyčistit po",
"Click to see discovery failures": "Kliknutím zobrazíte selhání při oznamování",
"Click to see discovery failures": "Kliknutím zobrazíte nezdary při objevování",
"Close": "Zavřít",
"Command": "Příkaz",
"Comment, when used at the start of a line": "Komentář, pokud použito na začátku řádku",
"Comment, when used at the start of a line": "Pokud použito na jeho začátku, je řádek považován za komentář",
"Compression": "Komprese",
"Configured": "Nastaveno",
"Connection Error": "Chyba připojení",
"Connection Type": "Typ připojení",
"Connections": "Připojení",
"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 nyní umožňuje nepřetržité sledování změn. To zachytí změny na disku a spustí sken pouze pro změněné cesty. Výhody jsou rychlejší propagace změn a méně plných skenů.",
"Connections": "Spojení",
"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 nyní umožňuje nepřetržité sledování změn. To zachytí změny na úložišti a spustí sken pouze pro umístění, ve kterých se něco změnilo. Výhodami jsou rychlejší propagace změn a méně plných skenů.",
"Copied from elsewhere": "Zkopírováno odjinud",
"Copied from original": "Zkopírováno z originálu",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 následující přispěvatelé:",
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 následující přispěvatelé:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 následující přispěvatelé:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Vytváření ignorovaných vzorů, přepisování existujícího souboru v {{path}}.",
"Danger!": "Pozor!",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Vytvářejí se vzory ignorovaného a přepisuje se jimi existující soubor v {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Nebezpečí!",
"Debugging Facilities": "Nástroje pro ladění",
"Default Folder Path": "Výchozí cesta k adresáři",
"Default Folder Path": "Popis umístění výchozí složky",
"Deleted": "Smazáno",
"Deselect All": "Zrušit výběr",
"Deselect All": "Zrušit výběr všeho",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Zařízení",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zařízení \"{{name}}\" ({{device}} na {{address}}) se chce připojit. Přidat nové zařízení?",
"Device ID": "ID zařízení",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zařízení {{name}} ({{device}} na {{address}}) se chce připojit. Přidat nové zařízení?",
"Device ID": "Identifikátor zařízení",
"Device Identification": "Identifikace zařízení",
"Device Name": "Jméno zařízení",
"Device rate limits": "Rychlostní limity zařízení",
"Device that last modified the item": "Poslední zařízení, které změnilo položku",
"Device Name": "Název zařízení",
"Device rate limits": "Omezení přenosové rychlosti pro zařízení",
"Device that last modified the item": "Zařízení, které položku změnilo naposledy",
"Devices": "Zařízení",
"Disable Crash Reporting": "Vypnout hlášení pádů",
"Disabled": "Vypnuto",
"Disabled periodic scanning and disabled watching for changes": "Periodické skenování i sledování změn vypnuto",
"Disabled periodic scanning and enabled watching for changes": "Periodické skenování vypnuto; sledování změn zapnuto",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodické skenování vypnuto; nastavení sledování změn selhalo, nový pokud každou 1m:",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodické skenování vypnuto; nastavení sledování změn se nezdařilo, opětovný pokus každou 1 min:",
"Discard": "Zahodit",
"Disconnected": "Odpojen",
"Discovered": "Nalezeno",
"Disconnected": "Odpojeno",
"Discovered": "Objeveno",
"Discovery": "Oznamování",
"Discovery Failures": "Selhání při oznamování",
"Discovery Failures": "Nezdary při oznamování",
"Do not restore": "Neobnovit",
"Do not restore all": "Neobnovit vše",
"Do you want to enable watching for changes for all your folders?": "Chcete povolit sledování změn pro všechny adresáře?",
"Do not restore all": "Neobnovit nic",
"Do you want to enable watching for changes for all your folders?": "Chcete zapnout sledování změn pro všechny složky?",
"Documentation": "Dokumentace",
"Download Rate": "Rychlost stahování",
"Downloaded": "Staženo",
"Downloading": "Stahuji",
"Downloading": "Stahuje se",
"Edit": "Upravit",
"Edit Device": "Upravit zařízení",
"Edit Folder": "Upravit adresář",
"Edit Folder": "Upravit složku",
"Editing": "Upravuje se",
"Editing {%path%}.": "Editace {{path}}.",
"Enable NAT traversal": "Povolit NAT přenos",
"Enable Relaying": "Povolit přenašeče",
"Editing {%path%}.": "Upravuje se {{path}}.",
"Enable Crash Reporting": "Povolit hlášení pádů",
"Enable NAT traversal": "Povolit průchod skrze NAT překlad",
"Enable Relaying": "Povolit přenašeče (relay)",
"Enabled": "Zapnuto",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Zadajte kladné číslo (např. \"2.35\") a zvolte jednotku. Percenta znamenají část celkové velikosti disku.",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Zadejte kladné číslo (např. 2.35) a zvolte jednotku. Procenta znamenají část celkové velikosti úložiště.",
"Enter a non-privileged port number (1024 - 65535).": "Zadejte číslo neprivilegovaného portu (1024-65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadejte adresy oddělené čárkou (\"tcp://ip:port\", \"tcp://host:port\") nebo \"dynamic\" pro automatické zjišťování adres.",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadejte adresy oddělené čárkami (\"tcp://ip:port\", \"tcp://host:port\") nebo \"dynamic\" pro automatické zjištění adresy.",
"Enter ignore patterns, one per line.": "Vložit ignorované vzory, jeden na řádek.",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadejte adresy oddělené čárkou (tcp://ip:port“, „tcp://host:port) nebo dynamic pro automatické zjišťování adres.",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadejte adresy oddělené čárkami (tcp://ip:port“, „tcp://host:port) nebo dynamic pro automatické zjištění adresy.",
"Enter ignore patterns, one per line.": "Zadejte vzory toho, co ignorovat každý na zvlášť řádek.",
"Error": "Chyba",
"External File Versioning": "Externí verzování souborů",
"Failed Items": "Selhalo",
"Failed to load ignore patterns": "Nahrání ignorovaných vzorů selhalo",
"Failed to setup, retrying": "Nastavování selhalo, zkouším znovu",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Je v pořádku, když připojení k IPv6 serverům selže, pokud není k dispozici IPv6 konektivita.",
"External File Versioning": "Externí správa verzí souborů",
"Failed Items": "Nezdařené položky",
"Failed to load ignore patterns": "Načtení vzorů ignorovaného se nezdařilo",
"Failed to setup, retrying": "Nastavování se nezdařilo, zkouší se znovu",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Je v pořádku, když připojení k IPv6 serverům nezdaří, pokud není k dispozici IPv6 konektivita.",
"File Pull Order": "Pořadí stahování souborů",
"File Versioning": "Verzování souborů",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Bity označující práva souborů jsou při hledání změn ignorovány. Použít pro souborové systémy FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Po nahrazení nebo smazání aplikací Syncthing jsou soubory přesunuty do adresáře .stversions.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Po nahrazení nebo smazání aplikací Syncthing jsou soubory přesunuty do složky .stversions.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Po nahrazení nebo smazání aplikací Syncthing jsou soubory přesunuty do verzí označených daty v adresáři .stversions.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Po nahrazení nebo smazání aplikací Syncthing jsou soubory přesunuty do verzí označených daty ve složce .stversions.",
"File Versioning": "Správa verzí souborů",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Bity označující přístupová oprávnění k souborům jsou při hledání změn ignorovány. Použijte pro souborové systémy FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Při nahrazování nebo mazání aplikací Syncthing jsou původní soubory přesunuty do složky .stversions.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Při nahrazování nebo mazání aplikací Syncthing jsou původní soubory přesunuty do složky .stversions.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Při nahrazování nebo mazání aplikací Syncthing jsou původní soubory opatřeny časovou značkou a takto označené verze přesunuty do složky .stversions.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Při nahrazování nebo mazání aplikací Syncthing jsou původní soubory opatřeny časovou značkou a takto označené verze přesunuty do složky .stversions.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Soubory jsou chráněny před změnami na ostatních zařízeních, ale změny provedené z tohoto zařízení budou rozeslány na zbytek clusteru.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Soubory jsou synchronizovány z clusteru, ale lokální změny nebudou rozesílány na ostatní zařízení.",
"Filesystem Notifications": "Oznámení souborového systému",
"Filesystem Watcher Errors": "Chyby sledování soubor. systému",
"Filter by date": "Vybrat podle data",
"Filter by name": "Vybrat podle názvu",
"Folder": "Adresář",
"Folder ID": "ID adresáře",
"Folder Label": "Jmenovka adresáře",
"Folder Path": "Cesta k adresáři",
"Folder Type": "Typ adresáře",
"Folders": "Adresáře",
"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.": "Pokus o sledování změn v těchto adresářích selhal. Budeme to znovu zkoušet každou minutu, takže se to možná brzo povede. Pokud ne, pokuste se najít příčinu. případně požádejte o pomoc.",
"Full Rescan Interval (s)": "Interval (y) plného skenu",
"GUI": "GUI",
"Folder": "Složka",
"Folder ID": "Identifikátor složky",
"Folder Label": "Jmenovka složky",
"Folder Path": "Popis umístění složky",
"Folder Type": "Typ složky",
"Folders": "Složky",
"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.": "Pokus o spuštění sledování změn v těchto složkách se nezdařil. Bude se to opětovně zkoušet každou minutu, takže se to možná brzo povede. Pokud ne, pokuste se najít příčinu. případně požádejte o pomoc.",
"Full Rescan Interval (s)": "Interval plného skenu (sek.)",
"GUI": "Grafické rozhraní",
"GUI Authentication Password": "Přihlašovací heslo pro GUI",
"GUI Authentication User": "Přihlašovací jméno pro GUI",
"GUI Listen Address": "Adresa naslouchání GUI",
"GUI Listen Addresses": "Adresa naslouchání GUI",
"GUI Theme": "Grafické téma",
"GUI Listen Address": "Adresa, na které GUI očekává spojení",
"GUI Listen Addresses": "Adresy, na kterých GUI očekává spojení",
"GUI Theme": "Motiv vzhledu pro GUI",
"General": "Obecné",
"Generate": "Generovat",
"Generate": "Vytvořit",
"Global Changes": "Globální změny",
"Global Discovery": "Globální oznamování",
"Global Discovery Servers": "Servery globálního oznamování",
"Global State": "Globální status",
"Help": "Pomoc",
"Help": "Nápověda",
"Home page": "Domovská stránka",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Nicméně Vaše současná nastavení značí, že si nepřejete funkci povolit. Automatické hlášení pádů tedy bylo vypnuto.",
"Ignore": "Ignorovat",
"Ignore Patterns": "Ignorované vzory",
"Ignore Patterns": "Vzory ignorovaného",
"Ignore Permissions": "Ignorovat oprávnění",
"Ignored Devices": "Ignorovaná zařízení",
"Ignored Folders": "Ignorované adresáře",
"Ignored Folders": "Ignorované složky",
"Ignored at": "Ignorováno v",
"Incoming Rate Limit (KiB/s)": "Omezení příchozí rychlosti (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Nesprávné nastavení může poškodit obsah Vašich adresářů a učinit Syncthing nefunkční.",
"Introduced By": "Zavedl",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Nesprávné nastavení může poškodit obsah Vašich složek a znefunkčnít Syncthing.",
"Introduced By": "Zavedeno od",
"Introducer": "Zavaděč",
"Inversion of the given condition (i.e. do not exclude)": "Prohození zadané podmínky (např. nevynechat)",
"Keep Versions": "Ponechat verze",
"Largest First": "Od největšího",
"Inversion of the given condition (i.e. do not exclude)": "Převrácení dané podmínky (např. nevynechat)",
"Keep Versions": "Kolik verzí ponechávat",
"Largest First": "Od největších",
"Last File Received": "Poslední přijatý soubor",
"Last Scan": "Poslední sken",
"Last seen": "Naposledy spatřen",
@@ -162,70 +169,72 @@
"Learn more": "Zjistěte více",
"Limit": "Limit",
"Listeners": "Naslouchající",
"Loading data...": "Nahrávání dat...",
"Loading...": "Načítání...",
"Loading data...": "Načítání dat",
"Loading...": "Načítání",
"Local Additions": "Local Additions",
"Local Discovery": "Místní oznamování",
"Local State": "Místní status",
"Local State (Total)": "Místní status (Celkem)",
"Locally Changed Items": "Lokálně změněné položky",
"Log": "Log",
"Log tailing paused. Click here to continue.": "Log pozastaven. Klikněte zde pro pokračování.",
"Log tailing paused. Scroll to bottom continue.": "Log pozastaven. Sjeďte dolů pro pokračování.",
"Log tailing paused. Scroll to the bottom to continue.": "Sledování logu pozastaveno. Sjeďte dolů pro pokračování.",
"Logs": "Logy",
"Major Upgrade": "Důležitá aktualizace",
"Log": "Záznam událostí",
"Log tailing paused. Click here to continue.": "Zaznamenávání událostí pozastaveno. Kliknutím sem v něm bude pokračováno.",
"Log tailing paused. Scroll to bottom continue.": "Zaznamenávání událostí pozastaveno. Sjeďte dolů pro pokračování.",
"Log tailing paused. Scroll to the bottom to continue.": "Zaznamenávání událostí pozastaveno. Sjeďte dolů pro pokračování.",
"Logs": "Záznamy událostí",
"Major Upgrade": "Aktualizace hlavní verze",
"Mass actions": "Hromadné akce",
"Master": "Master",
"Master": "Hlavní",
"Maximum Age": "Maximální časový limit",
"Metadata Only": "Pouze metadata",
"Minimum Free Disk Space": "Minimální velikost volného místa na disku",
"Minimum Free Disk Space": "Minimální velikost volného místa na úložišti",
"Mod. Device": "Zařízení, které provedlo změnu",
"Mod. Time": "Čas modifikace",
"Mod. Time": "Okamžik změny",
"Move to top of queue": "Přesunout na začátek fronty",
"Multi level wildcard (matches multiple directory levels)": "Víceúrovňový zástupný znak (shoda skrz více úrovní složek)",
"Multi level wildcard (matches multiple directory levels)": "Víceúrovňový zástupný znak (shody i skrz více úrovní složek)",
"Never": "Nikdy",
"New Device": "Nové zařízení",
"New Folder": "Nový adresář",
"Newest First": "Od nejnovějšího",
"New Folder": "Nová složka",
"Newest First": "Od nejnovějších",
"No": "Ne",
"No File Versioning": "Bez verzování souborů",
"No File Versioning": "Neuchovávat předchozí verze souborů",
"No files will be deleted as a result of this operation.": "Tato operace nesmaže žádné soubory.",
"No upgrades": "Žádné aktualizace",
"Normal": "Normální",
"Notice": "Oznámení",
"OK": "OK",
"Off": "Vypnuta",
"Oldest First": "Od nejstaršího",
"Optional descriptive label for the folder. Can be different on each device.": "Volitelný popisek adresáře. Může být rozdílný na každém zařízení.",
"Oldest First": "Od nejstarších",
"Optional descriptive label for the folder. Can be different on each device.": "Volitelný popisek složky. Na každém zařízení je možné nazvat jinak.",
"Options": "Nastavení",
"Out of Sync": "Nesesynchronizováno",
"Out of Sync Items": "Nesesynchronizované položky",
"Outgoing Rate Limit (KiB/s)": "Omezení odchozí rychlosti (KiB/s)",
"Override Changes": "Přepsat změny",
"Path": "Cesta",
"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": "Cesta k adresáři na lokálním počítači. Pokud neexistuje, bude vytvořen. Znak vlnovky (~) může být použit jako zkratka pro",
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "Cesta pro ukládání nových autom. přijatých adresářů a také výchozí cesta při přidávání nových adresářů v GUI. Vlnka (~) se rozvine na {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Cesta pro ukládání verzí (ponechte prázdné pro výchozí adresář .stversions ve sdíleném adresáři).",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Cesta pro ukládání verzí (nechat prázdné pro výchozí složku .stversions v adresáři).",
"Override Changes": "Přet změny na ostatních",
"Path": "Popis umístění",
"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": "Popis umístění složky na tomto počítači. Pokud neexistuje, bude vytvořeno. Znak vlnovky (~) může být použit jako zkratka pro",
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "Popis umístění, ve kterém budou vytvářeny nové automaticky přijaté složky a také výchozího při přidávání nových složek v grafickém uživatelském rozhraní. Vlnovka (~) se rozvine na {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Popis umístění, ve kterém ukládat verze (ponechte prázdné pro výchozí podsložku .stversions ve sdílené složce).",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Popis umístění, ve kterém ukládat verze (ponechte prázdné pro výchozí podsložku .stversions ve složce).",
"Pause": "Pozastavit",
"Pause All": "Pozastavit vše",
"Paused": "Pozastaveno",
"Pending changes": "Čekající změny",
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn vypnuto",
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn zapnuto",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodické skenování podle zadaného intervalu; nastavení sledování změn selhalo, nový pokud každou 1m:",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodické skenování podle zadaného intervalu; nastavení sledování změn se nezdařilo, opětovný pokus každou 1 min: ",
"Permissions": "Oprávnění",
"Please consult the release notes before performing a major upgrade.": "Před spuštěním důležité aktualizace si nejdříve přečtěte poznámky k vydání nové verze.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Zadejte prosím přihlašovací jméno a heslo pro GUI v dialogu nastavení.",
"Please consult the release notes before performing a major upgrade.": "Před přechodem na novější hlavní verzi si nejdříve přečtěte poznámky k vydání nové verze.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "V dialogu Nastavení zadejte uživatelské jméno a heslo pro ověření se v GUI.",
"Please wait": "Chvíli strpení",
"Prefix indicating that the file can be deleted if preventing directory removal": "Předpona značící možnost smazání souboru, pokud brání odebrání složky",
"Prefix indicating that the pattern should be matched without case sensitivity": "Předpona značící ignorování velkých/malých písmen při hledání řetězce",
"Prefix indicating that the file can be deleted if preventing directory removal": "Tato předpona značí, že pokud soubor brání odebrání složky, je možné ho smazat",
"Prefix indicating that the pattern should be matched without case sensitivity": "Tato předpona značí, že při porovnávání se vzorem nemají být rozlišována malá/velká písmena",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Náhled",
"Preview Usage Report": "Náhled záznamu o využítí",
"Preview Usage Report": "Náhled hlášení o využívání",
"Quick guide to supported patterns": "Rychlá nápověda k podporovaným vzorům",
"RAM Utilization": "Využití RAM",
"Random": "Náhodně",
"Receive Only": "Pouze příjem",
"RAM Utilization": "Využití operační paměti",
"Random": "Náhodné",
"Receive Only": "Pouze přijímací",
"Recent Changes": "Nedávné změny",
"Reduced by ignore patterns": "Redukováno o ignorované vzory",
"Release Notes": "Poznámky k vydání",
@@ -233,15 +242,15 @@
"Remote Devices": "Vzdálená zařízení",
"Remove": "Odstranit",
"Remove Device": "Odebrat zařízení",
"Remove Folder": "Odebrat adresář",
"Required identifier for the folder. Must be the same on all cluster devices.": "Požadovaný identifikátor adresáře. Musí být stejný na všech zařízeních clusteru.",
"Remove Folder": "Odebrat složku",
"Required identifier for the folder. Must be the same on all cluster devices.": "Vyžadován identifikátor složky. Je třeba, aby byl na všech zařízeních clusteru stejný.",
"Rescan": "Opakovat skenování",
"Rescan All": "Opakovat skenování všech",
"Rescan Interval": "Interval opakování skenování",
"Rescans": "Opakovaná skenování",
"Restart": "Restart",
"Restart Needed": "Je nutný restart",
"Restarting": "Restartuji",
"Restarting": "Restartuje se",
"Restore": "Obnovit",
"Restore Versions": "Obnovené verze",
"Resume": "Pokračovat",
@@ -250,139 +259,143 @@
"Revert Local Changes": "Vrátit lokální změny",
"Running": "Probíhá",
"Save": "Uložit",
"Scan Time Remaining": "Zbývající čas skenování",
"Scan Time Remaining": "Čas do dokončení skenování",
"Scanning": "Skenování",
"See external versioner help for supported templated command line parameters.": "Pro upřesnění požadovaných parametrů příkazu navštivte nápovědu pro externí verzování.",
"See external versioning help for supported templated command line parameters.": "Podporované šablonové parametry příkazové řádky jsou dostupné v nápovědě k externímu verzování.",
"See external versioner help for supported templated command line parameters.": "Pro upřesnění požadovaných parametrů příkazu navštivte nápovědu pro externí správu verzí.",
"See external versioning help for supported templated command line parameters.": "Podporované šablonové parametry příkazové řádky jsou dostupné v nápovědě k externí správě verzí.",
"Select All": "Vybrat vše",
"Select a version": "Vyberte verzi",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Vybrat nejnovější verzi",
"Select oldest version": "Vybrat nejstarší verzi",
"Select the devices to share this folder with.": "Vybrat zařízení, se kterými sdílet tento adresář.",
"Select the folders to share with this device.": "Vybrat adresáře sdílené s tímto zařízením.",
"Send & Receive": "Odeslat a přijmout",
"Send Only": "Pouze odeslat",
"Select the devices to share this folder with.": "Vybrat zařízení, se kterými sdílet tuto složku.",
"Select the folders to share with this device.": "Vybrat složky ke sdílení s tímto zařízením.",
"Send & Receive": "Odesíla a přijímací",
"Send Only": "Pouze odesíla",
"Settings": "Nastavení",
"Share": "Sdílet",
"Share Folder": "Sdílet adresář",
"Share Folders With Device": "Sdílet adresáře s tímto zařízením",
"Share Folder": "Sdílet složku",
"Share Folders With Device": "Sdílet složky s tímto zařízením",
"Share With Devices": "Sdílet se zařízeními",
"Share this folder?": "Sdílet tento adresář?",
"Share this folder?": "Sdílet tuto složku?",
"Shared With": "Sdíleno s",
"Sharing": "Sdílení",
"Show ID": "Zobrazit ID",
"Show QR": "Zobrazit QR",
"Show ID": "Zobrazit identifikátor",
"Show QR": "Zobrazit QR kód",
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazeno místo ID zařízení na náhledu stavu clusteru. Bude odesíláno ostatním zařízením jako výchozí jméno zařízení.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazeno místo ID zařízení na náhledu stavu clusteru. Pokud nebude vyplněno, bude nastaveno na jméno, které zařízení odesílá.",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Bude odesíláno ostatním zařízením jako výchozí název zařízení.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Pokud nebude vyplněno, bude nastaveno na název, který zařízení odesílá.",
"Shutdown": "Vypnout",
"Shutdown Complete": "Vypnutí dokončeno",
"Simple File Versioning": "Jednoduché verzování souborů",
"Simple File Versioning": "Jednoduchá správa verzí souborů",
"Single level wildcard (matches within a directory only)": "Jednoúrovňový zástupný znak (shody pouze uvnitř složky)",
"Size": "Velikost",
"Smallest First": "Od nejmenšího",
"Smallest First": "Od nejmenších",
"Some items could not be restored:": "Některé položky nemohly být obnoveny:",
"Source Code": "Zdrojový kód",
"Source Code": "Zdrojové kódy",
"Stable releases and release candidates": "Stabilní vydání a kandidáti na vydání",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilní vydání jsou opožděna zhruba o dva týdny. Po tuto dobu se testují jako kandidáti na vydání.",
"Stable releases only": "Pouze stabilní vydání",
"Staggered File Versioning": "Postupné verzování souborů",
"Start Browser": "Otevřít prohlížeč",
"Staggered File Versioning": "Správa verzí souborů po stupních",
"Start Browser": "Otevřít webový prohlížeč",
"Statistics": "Statistiky",
"Stopped": "Pozastaveno",
"Stopped": "Zastaveno",
"Support": "Podpora",
"Support Bundle": "Balík podpory",
"Sync Protocol Listen Addresses": "Adresa naslouchání synchronizačního protokolu",
"Sync Protocol Listen Addresses": "Adresa, na které synchronizační protokol očekává spojení",
"Syncing": "Synchronizuje se",
"Syncthing has been shut down.": "Syncthing byl vypnut.",
"Syncthing has been shut down.": "Syncthing bylo vypnuto.",
"Syncthing includes the following software or portions thereof:": "Syncthing obsahuje následující software nebo jejich část:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing je svobodný a open source software licencovaný jako MPL v2.0.",
"Syncthing is restarting.": "Syncthing se restartuje.",
"Syncthing is upgrading.": "Syncthing se aktualizuje.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing se zdá být nefunkční, nebo je problém s připojením k Internetu. Opakuji...",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing nyní umožňuje automaticky hlásit vývojářům pády aplikace. Tato funkce je ve výchozím stavu povolena.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing se zdá být nefunkční, nebo je problém s připojením k Internetu. Zkouší se znovu…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing má nejspíše problém s provedením vašeho požadavku. Pokud problém přetrvává, obnovte stránku v prohlížeči nebo restartujte Syncthing.",
"Take me back": "Jít zpět",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Adresa v GUI je potlačena parametry při spuštění. Dokud potlačení trvá, zdejší změny nemají efekt.",
"The Syncthing admin interface is configured to allow remote access without a password.": "V nastavení aplikace Syncthing je povoleno vzdálené připojení k administrátorskému rozhraní bez zadání hesla.",
"The aggregated statistics are publicly available at the URL below.": "Souhrnné statistiky jsou veřejně dostupné na níže uvedené URL.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurace byla uložena, ale ne aktivována. Pro aktivaci nové konfigurace je třeba restartovat Syncthing.",
"The device ID cannot be blank.": "ID zařízení nemůže být prázdné.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID zařízení, které je třeba vložit, lze nalézt v dialogu \"Akce > Zobrazit ID\" na druhém zařízení. Mezery a pomlčky nejsou nutné (budou ignorovány).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Šifrovaná data o využití jsou zasílána denně. Jsou používána pro zjištění nejobvyklejších platforem, velikosti adresářů a verzí aplikace. Pokud se hlášená data změní, budete opět upozorněni tímto dialogem.",
"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.": "Zadané ID zařízení není platné. Mělo by mít 52 nebo 56 znaků a mělo by obsahovat písmena a čísla. Mezery a pomlčky jsou nepovinné.",
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "První parametr příkazové řádky je cesta k adresáři, druhý je relativní cesta v témže adresáři.",
"The folder ID cannot be blank.": "ID adresáře nemůže být prázdné.",
"The folder ID must be unique.": "ID adresáře musí být unikátní.",
"The folder path cannot be blank.": "Cesta k adresáři nemůže být prázdná.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Nastavení byla uložena, ale nejsou aktivována. Pro aktivaci nového nastavení je třeba Syncthing restartovat.",
"The device ID cannot be blank.": "Identifikátor zařízení nemůže zůstat nevyplněný.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Identifikátor zařízení, který je třeba vložit, lze nalézt v dialogu Akce > Zobrazit identifikátor“ na druhém zařízení. Mezery a pomlčky nejsou nutné (budou ignorovány).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Šifrovaná data o využití jsou zasílána denně. Jsou používána pro zjištění nejobvyklejších platforem, velikosti složek a verzí aplikace. Pokud se rozsah hlášených dat změní, budete opět upozorněni tímto dialogem.",
"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.": "Zadaný identifikátor zařízení není platný. Měl by mít 52 nebo 56 znaků a měl by obsahovat písmena a číslice. Mezery a pomlčky jsou nepovinné.",
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "První parametr příkazové řádky je popis umístění složky, druhý je relativní popis umístění v téže složce.",
"The folder ID cannot be blank.": "Identifikátor složky nemůže zůstat nevyplněný.",
"The folder ID must be unique.": "Je třeba, aby se identifikátor složky neopakoval.",
"The folder path cannot be blank.": "Popis umístění složky nemůže zůstat nevyplněný.",
"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.": "Jsou použity následující intervaly: za první hodinu jsou ponechány verze pro každých 30 sekund, za první den jsou ponechány verze pro každou hodinu, za prvních 30 dní jsou ponechány verze pro každý den a do nejvyššího nastaveného stáří jsou ponechány verze pro každý týden.",
"The following items could not be synchronized.": "Následující položky nemohly být synchronizovány.",
"The following items were changed locally.": "Tyto položky byly změněny lokálně",
"The maximum age must be a number and cannot be blank.": "Nejvyšší stáří je třeba zadat v podobě čísla a nemůže být prázdné.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maximální doba pro zachování verze (dny, zapsáním hodnoty 0 bude ponecháno navždy).",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Procentuální údaj minimální velikosti volného místa na disku musí být číslo mezi 0 až 100 (včetně).",
"The number of days must be a number and cannot be blank.": "Počet dní musí být číslo a nesmí být prázdný.",
"The number of days must be a number and cannot be blank.": "Je třeba, aby počet dní bylo číslo a nemůže zůstat nevyplněné.",
"The number of days to keep files in the trash can. Zero means forever.": "Počet dní, po který budou soubory uchovány v koši. Nula znamená navždy.",
"The number of old versions to keep, per file.": "Počet udržovaných starších verzí souboru.",
"The number of versions must be a number and cannot be blank.": "Počet verzí musí být číslo a nemůže být prázdné.",
"The path cannot be blank.": "Cesta nesmí být prázdná.",
"The rate limit must be a non-negative number (0: no limit)": "Limit rychlosti musí být nezáporné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "Interval opakování skenování musí být pozitivní číslo.",
"The number of old versions to keep, per file.": "Počet uchovávaných starších verzí každého ze souborů.",
"The number of versions must be a number and cannot be blank.": "Je třeba, aby počet verzí bylo číslo a nemůže zůstat nevyplněné.",
"The path cannot be blank.": "Popis umístění nemůže zůstat nevyplněný.",
"The rate limit must be a non-negative number (0: no limit)": "Je třeba, aby limit rychlosti bylo kladné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "Je třeba, aby interval opakování skenování bylo kladné číslo.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Nové pokusy o synchronizaci budou probíhat automaticky a položky budou synchronizovány jakmile bude chyba odstraněna.",
"This Device": "Toto zařízení",
"This can easily give hackers access to read and change any files on your computer.": "To může útočníkům jednoduše povolit čtení a úpravy souborů na vašem přístroji. ",
"This is a major version upgrade.": "Toto je důležitá aktualizace.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Toto nastavení ovládá velikost volného prostoru na hlavním disku (ten, na kterém je databáze indexu).",
"This can easily give hackers access to read and change any files on your computer.": "Toto může útočníkům jednoduše umožnit čtení a úpravy souborů na vašem počítači. ",
"This is a major version upgrade.": "Toto je velká aktualizace.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Toto nastavení ovládá velikost volného prostoru na hlavním datovém úložišti (to, na kterém je databáze rejstříku).",
"Time": "Čas",
"Time the item was last modified": "Čas poslední modifikace položky",
"Trash Can File Versioning": "Verzování souborů v koši",
"Trash Can File Versioning": "Ponechávat jednu předchozí verzi (jako Koš) ",
"Type": "Typ",
"Unavailable": "Nedostupné",
"Unavailable/Disabled by administrator or maintainer": "Nedostupné; Zakázáno administrátorem",
"Unavailable/Disabled by administrator or maintainer": "Není k dispozici / vypnuto správcem systému či balíčku",
"Undecided (will prompt)": "Nerozhodnuto (zeptá se)",
"Unignore": "Přestat ignorovat",
"Unknown": "Neznámý",
"Unshared": "Nesdílený",
"Unshared Devices": "Unshared Devices",
"Unused": "Nepoužitý",
"Up to Date": "Aktuální",
"Updated": "Aktualizováno",
"Upgrade": "Aktualizace",
"Upgrade": "Přechod na novější verzi",
"Upgrade To {%version%}": "Aktualizovat na {{version}}",
"Upgrading": "Aktualizuji",
"Upgrading": "Aktualizuje se",
"Upload Rate": "Rychlost odesílání",
"Uptime": "Celkový čas běhu",
"Usage reporting is always enabled for candidate releases.": "Hlášení o používání je pro kandidáty na vydání vždy zapnuto.",
"Use HTTPS for GUI": "Použít HTTPS pro grafické rozhraní",
"Use HTTPS for GUI": "Použít pro grafické rozhraní zabezpečení HTTPS",
"Use notifications from the filesystem to detect changed items.": "Použít oznamování soubor. systému pro nalezení změněných položek.",
"Variable Size Blocks": "Bloky variabilní velikosti",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Bloky variabilní velikosti (také \"velké bloky\") jsou účinnější pro velké soubory.",
"Variable Size Blocks": "Bloky proměnlivé velikosti",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Bloky proměnlivé velikosti (také velké bloky) jsou účinnější pro velké soubory.",
"Version": "Verze",
"Versions": "Verze",
"Versions Path": "Cesta k verzím",
"Versions Path": "Popis umístění verzí",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Verze jsou automaticky smazány, pokud jsou starší než maximální časový limit nebo překročí počet souborů povolených pro interval.",
"Waiting to scan": "Čekání na skenování",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varování, tato cesta je nadřazenou složkou existujícího adresáře \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varování, tato cesta je nadřazenou složkou existujícího adresáře \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varování: tato cesta je podsložkou existujícího adresáře \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varování, tato cesta je podsložkou existujícího adresáře \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varování, tento popis umístění je nadřazenou složkou existující{{otherFolder}}.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varování, tento popis umístění je nadřazenou složkou existující{{otherFolderLabel}} ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varování: toto umístění je podsložkou existující{{otherFolder}}.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varování, toto umístění e podsložkou existující{{otherFolderLabel}} ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Pozor: Pokud používáte externí sledování změn jako {{syncthingInotify}}, měly byste se ujistit, že je toto sledování vypnuto.",
"Watch for Changes": "Sledovat změny",
"Watching for Changes": "Sledování změn",
"Watching for changes discovers most changes without periodic scanning.": "Sledování změn odhalí většinu změn bez periodického skenování.",
"Watching for changes discovers most changes without periodic scanning.": "Sledování změn odhalí většinu změn ještě před periodickým skenováním.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Při přidávání nového zařízení mějte na paměti, že je ho třeba také zadat na druhé straně.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Při přidávání nového adresáře mějte na paměti, že jeho ID je použito ke svázání adresářů napříč zařízeními. Rozlišují se malá a velká písmena a musí přesně souhlasit mezi všemi zařízeními.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Při přidávání nové složky mějte na paměti, že její identifikátor je použit jako vazba mezi složkami napříč zařízeními. Rozlišují se malá a velká písmena a je třeba, aby přesně souhlasilo mezi všemi zařízeními.",
"Yes": "Ano",
"You can also select one of these nearby devices:": "Také můžete vybrat jedno z těchto okolních zařízení:",
"You can change your choice at any time in the Settings dialog.": "Vaši volbu můžete kdykoliv změnit v dialogu nastavení.",
"You can read more about the two release channels at the link below.": "O kandidátech na vydání si můžete přečíst více v odkazu níže.",
"You have no ignored devices.": "Nemáte žádná ignorovaná zařízení",
"You have no ignored folders.": "Nemáte žádné ignorované adresáře",
"You have no ignored folders.": "Nemáte žádné ignorované složky.",
"You have unsaved changes. Do you really want to discard them?": "Máte neuložené změny. Opravdu je chcete zahodit?",
"You must keep at least one version.": "Je třeba ponechat alespoň jednu verzi.",
"days": "dní",
"directories": "složek",
"files": "souborů",
"full documentation": "plná dokumentace",
"full documentation": "úplná dokumentace",
"items": "položky",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet adresář \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce sdílet adresář \"{{folderlabel}}\" ({{folder}})."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet složku „{{folder}}.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce sdílet složku „{{folderlabel}} ({{folder}})."
}

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Er du sikker på, at du vil fjerne enheden {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Er du sikker på, at du vil fjerne mappen {{label}}?",
"Are you sure you want to restore {%count%} files?": "Er du sikker på, at du vil genskabe {{count}} filer?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Autoacceptér",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Den automatiske opdatering tilbyder nu valget mellem stabile udgivelser og udgivelseskandidater.",
"Automatic upgrades": "Automatisk opdatering",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 de følgende bidragsydere:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 de følgende bidragsydere:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Opretter ignoreringsmønstre; overskriver en eksisterende fil på {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Fare!",
"Debugging Facilities": "Faciliteter til fejlretning",
"Default Folder Path": "Standardmappesti",
"Deleted": "Slettet",
"Deselect All": "Fravælg alle",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Enhed",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enheden “{{name}}” ({{device}} på {{address}}) vil gerne forbinde. Tilføj denne enhed?",
"Device ID": "Enheds-ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Enhedens hastighedsbegrænsning",
"Device that last modified the item": "Enhed, som sidst ændrede filen",
"Devices": "Enheder",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Deaktiveret",
"Disabled periodic scanning and disabled watching for changes": "Deaktiverede periodisk skanning og deaktiverede overvågning af ændringer",
"Disabled periodic scanning and enabled watching for changes": "Deaktiverede periodisk skanning og aktiverede overvågning af ændringer",
@@ -92,6 +97,7 @@
"Edit Folder": "Redigér mappe",
"Editing": "Redigerer",
"Editing {%path%}.": "Redigerer {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Aktivér NAT-traversering",
"Enable Relaying": "Aktivér videresending",
"Enabled": "Aktiveret",
@@ -141,6 +147,7 @@
"Global State": "Global tilstand",
"Help": "Hjælp",
"Home page": "Hjem",
"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": "Ignorér",
"Ignore Patterns": "Ignoreringsmønstre",
"Ignore Permissions": "Ignorér rettigheder",
@@ -164,6 +171,7 @@
"Listeners": "Lyttere",
"Loading data...": "Indlæser data ...",
"Loading...": "Indlæser ...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokal opslag",
"Local State": "Lokal tilstand",
"Local State (Total)": "Lokal tilstand (total)",
@@ -220,6 +228,7 @@
"Please wait": "Vent venligst",
"Prefix indicating that the file can be deleted if preventing directory removal": "Forstavelse, der indikerer, at filen kan slettes, hvis fjernelse at mappe undgåes",
"Prefix indicating that the pattern should be matched without case sensitivity": "Forstavelse, der indikerer det mønster, der skal sammenlignes uden versalfølsomhed",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Forhåndsvisning",
"Preview Usage Report": "Forhåndsvisning af forbrugsrapport",
"Quick guide to supported patterns": "Kvikguide til understøttede mønstre",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Se hjælp til ekstern versionering for understøttede kommandolinjeparametre.",
"Select All": "Vælg alle",
"Select a version": "Vælg en version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Vælg seneste version",
"Select oldest version": "Vælg ældste version",
"Select the devices to share this folder with.": "Vælg hvilke enheder du vil dele denne mappe med.",
@@ -299,6 +309,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 genstarter.",
"Syncthing is upgrading.": "Syncthing opgraderer.",
"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 ser ud til at være stoppet eller oplever problemer med din internetforbindelse. Prøver igen…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Det ser ud til, at Syncthing har problemer med at udføre opgaven. Prøv at genindlæse siden eller genstarte Synching, hvis problemet vedbliver.",
"Take me back": "Tag mig tilbage",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Stien må ikke være tom.",
"The rate limit must be a non-negative number (0: no limit)": "Hastighedsbegrænsningen skal være et ikke-negativt tal (0: ingen begrænsning)",
"The rescan interval must be a non-negative number of seconds.": "Genskanningsintervallet skal være et ikke-negativt antal sekunder.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "De prøves igen automatisk og vil blive synkroniseret, når fejlen er løst.",
"This Device": "Denne enhed",
"This can easily give hackers access to read and change any files on your computer.": "Dette gør det nemt for hackere at få adgang til at læse og ændre filer på din computer.",
@@ -342,6 +354,7 @@
"Unignore": "Fjern ignorering",
"Unknown": "Ukendt",
"Unshared": "Ikke delt",
"Unshared Devices": "Unshared Devices",
"Unused": "Ubrugt",
"Up to Date": "Fuldt opdateret",
"Updated": "Opdateret",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Sind Sie sicher, dass sie das Gerät {{name}} entfernen möchten?",
"Are you sure you want to remove folder {%label%}?": "Sind Sie sicher, dass sie den Ordner {{label}} entfernen möchten?",
"Are you sure you want to restore {%count%} files?": "Sind Sie sicher, dass Sie {{count}} Dateien wiederherstellen möchten?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Automatische Annahme",
"Automatic Crash Reporting": "Automatische Absturzmeldung",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Die automatische Aktualisierung bietet jetzt die Wahl zwischen stabilen Veröffentlichungen und Veröffentlichungskandidaten.",
"Automatic upgrades": "Automatische Aktualisierungen aktivieren",
"Automatic upgrades are always enabled for candidate releases.": "Automatische Upgrades sind für Veröffentlichungskandidaten immer aktiviert.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 der folgenden Unterstützer:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 folgende Mitwirkende:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Erstelle Ignoriermuster, welche die existierende Datei {{path}} überschreiben.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Achtung!",
"Debugging Facilities": "Debugging-Möglichkeiten",
"Default Folder Path": "Standardmäßiger Ordnerpfad",
"Deleted": "Gelöscht",
"Deselect All": "Alle abwählen",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Gerät",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Gerät \"{{name}}\" ({{device}} {{address}}) möchte sich verbinden. Gerät hinzufügen?",
"Device ID": "Gerätekennung",
@@ -71,6 +75,7 @@
"Device rate limits": "Gerät Datenratelimit",
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
"Devices": "Geräte",
"Disable Crash Reporting": "Absturzmeldung deaktivieren",
"Disabled": "Deaktiviert",
"Disabled periodic scanning and disabled watching for changes": "Deaktivierter periodischer Scann und deaktivierter Überwachung von Änderungen",
"Disabled periodic scanning and enabled watching for changes": "Deaktivierter periodischer Scann und aktivierter Überwachung von Änderungen",
@@ -92,6 +97,7 @@
"Edit Folder": "Ordner bearbeiten",
"Editing": "Bearbeitet",
"Editing {%path%}.": "Bearbeite {{path}}.",
"Enable Crash Reporting": "Absturzmeldung aktivieren",
"Enable NAT traversal": "NAT-Durchdringung aktivieren",
"Enable Relaying": "Weiterleitung aktivieren",
"Enabled": "Aktiviert",
@@ -141,6 +147,7 @@
"Global State": "Globaler Status",
"Help": "Hilfe",
"Home page": "Homepage",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Ihre aktuellen Einstellungen weisen jedoch daraufhin, dass Sie die Aktivierung möglicherweise nicht wünschen. Wir haben die automatischen Absturzberichte für Sie deaktiviert.",
"Ignore": "Ignorieren",
"Ignore Patterns": "Ignoriermuster",
"Ignore Permissions": "Berechtigungen ignorieren",
@@ -164,6 +171,7 @@
"Listeners": "Zuhörer",
"Loading data...": "Daten werden geladen...",
"Loading...": "Wird geladen...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokale Gerätesuche",
"Local State": "Lokaler Status",
"Local State (Total)": "Lokaler Status (Gesamt)",
@@ -220,6 +228,7 @@
"Please wait": "Bitte warten",
"Prefix indicating that the file can be deleted if preventing directory removal": "Präfix, das anzeigt, dass die Datei gelöscht werden kann, wenn sie die Entfernung des Ordners verhindert",
"Prefix indicating that the pattern should be matched without case sensitivity": "Präfix, das anzeigt, dass das Muster ohne Beachtung der Groß-/Kleinschreibung übereinstimmen soll",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Vorschau",
"Preview Usage Report": "Vorschau des Nutzungsberichts",
"Quick guide to supported patterns": "Schnellanleitung zu den unterstützten Mustern",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Siehe Hilfe zur externen Versionierung für unterstützte Befehlszeilenparameter.",
"Select All": "Alle auswählen",
"Select a version": "Wählen Sie eine Version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Letzte Version auswählen",
"Select oldest version": "Älteste Version auswählen",
"Select the devices to share this folder with.": "Wähle die Geräte aus, mit denen Sie diesen Ordner teilen wollen.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing ist freie und quelloffene Software, lizenziert als MPL v2.0.",
"Syncthing is restarting.": "Syncthing wird neu gestartet",
"Syncthing is upgrading.": "Syncthing wird aktualisiert",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit Deiner Internetverbindung. Versuche erneut...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing scheint ein Problem mit der Verarbeitung Deiner Eingabe zu haben. Bitte lade die Seite neu oder führe einen Neustart durch, falls das Problem weiterhin besteht.",
"Take me back": "Führe mich zurück",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
"The rate limit must be a non-negative number (0: no limit)": "Das Datenratelimit muss eine nicht negative Zahl sein (0 = kein Limit).",
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht negative Anzahl (in Sekunden) sein.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Sie werden automatisch heruntergeladen und werden synchronisiert, wenn der Fehler behoben wurde.",
"This Device": "Dieses Gerät",
"This can easily give hackers access to read and change any files on your computer.": "Dies kann dazu führen, dass Unberechtigte relativ einfach auf Ihre Dateien zugreifen und diese ändern können.",
@@ -342,6 +354,7 @@
"Unignore": "Beachten",
"Unknown": "Unbekannt",
"Unshared": "Ungeteilt",
"Unshared Devices": "Unshared Devices",
"Unused": "Ungenutzt",
"Up to Date": "Aktuell",
"Updated": "Aktualisiert",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Σίγουρα επιθυμείτε να αφαιρέσετε τη συσκευή {{name}};",
"Are you sure you want to remove folder {%label%}?": "Σίγουρα επιθυμείτε να αφαιρέσετε τον φάκελο {{label}};",
"Are you sure you want to restore {%count%} files?": "Σίγουρα επιθυμείτε να επαναφέρετε {{count}} αρχεία;",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Αυτόματη αποδοχή",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Για τις αυτόματες αναβαθμίσεις μπορείτε πλέον να επιλέξετε μεταξύ σταθερών εκδόσεων και υποψήφιων εκδόσεων.",
"Automatic upgrades": "Αυτόματη αναβάθμιση",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 για τους παρακάτω συνεισφέροντες:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Δημιουργία προτύπων αγνόησης, αντικατάσταση του υπάρχοντος αρχείου στο {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Προσοχή!",
"Debugging Facilities": "Εργαλεία αποσφαλμάτωσης",
"Default Folder Path": "Προκαθορισμένη διαδρομή φακέλων",
"Deleted": "Διαγραμμένα",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Συσκευή",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Η συσκευή \"{{name}}\" ({{device}} στη διεύθυνση {{address}}) επιθυμεί να συνδεθεί. Προσθήκη της νέας συσκευής;",
"Device ID": "Ταυτότητα συσκευής",
@@ -71,6 +75,7 @@
"Device rate limits": "Όρια ταχύτητας συσκευών",
"Device that last modified the item": "Συσκευή από την οποία πραγματοποιήθηκε η τελευταία τροποποίηση του στοιχείου",
"Devices": "Συσκευές",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Απενεργοποιημένη",
"Disabled periodic scanning and disabled watching for changes": "Έχουν απενεργοποιηθεί η τακτική σάρωση και η επιτήρηση αλλαγών",
"Disabled periodic scanning and enabled watching for changes": "Έχει απενεργοποιηθεί η τακτική σάρωση και ενεργοποιηθεί η επιτήρηση αλλαγών",
@@ -92,6 +97,7 @@
"Edit Folder": "Επεξεργασία φακέλου",
"Editing": "Επεξεργασία σε εξέλιξη",
"Editing {%path%}.": "Επεξεργασία του {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Ενεργοποίηση διάσχισης NAT",
"Enable Relaying": "Ενεργοποίηση αναμετάδοσης",
"Enabled": "Ενεργοποιημένη",
@@ -141,6 +147,7 @@
"Global State": "Καθολική κατάσταση",
"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.",
"Ignore": "Αγνόησε",
"Ignore Patterns": "Πρότυπο για αγνόηση",
"Ignore Permissions": "Αγνόησε τα δικαιώματα",
@@ -164,6 +171,7 @@
"Listeners": "Ακροατές",
"Loading data...": "Φόρτωση δεδομένων...",
"Loading...": "Φόρτωση...",
"Local Additions": "Local Additions",
"Local Discovery": "Τοπική ανεύρεση",
"Local State": "Τοπική κατάσταση",
"Local State (Total)": "Τοπική κατάσταση (συνολικά)",
@@ -220,6 +228,7 @@
"Please wait": "Παρακαλώ περιμένετε",
"Prefix indicating that the file can be deleted if preventing directory removal": "Πρόθεμα που δείχνει ότι το αρχείο θα μπορεί να διαγραφεί αν εμποδίζει τη διαγραφή καταλόγου",
"Prefix indicating that the pattern should be matched without case sensitivity": "Πρόθεμα που δείχνει ότι η αντιστοίχιση προτύπου θα γίνεται χωρίς διάκριση πεζών και κεφαλαίων χαρακτήρων",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Προεπισκόπηση",
"Preview Usage Report": "Προεπισκόπηση αναφοράς χρήσης",
"Quick guide to supported patterns": "Σύντομη βοήθεια σχετικά με τα πρότυπα αναζήτησης που υποστηρίζονται",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Ανατρέξτε στην τεκμηρίωση της εξωτερικής τήρησης εκδόσεων για πληροφορίες σχετικά με τις υποστηριζόμενες παραμέτρους της γραμμής εντολών.",
"Select All": "Select All",
"Select a version": "Επιλογή έκδοσης",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Επιλογή τελευταίας έκδοσης",
"Select oldest version": "Επιλογή παλαιότερης έκδοσης",
"Select the devices to share this folder with.": "Διάλεξε τις συσκευές προς τις οποίες θα διαμοιράζεται αυτός ο φάκελος.",
@@ -299,6 +309,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 επανεκκινείται.",
"Syncthing is upgrading.": "Το Syncthing αναβαθμίζεται.",
"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 φαίνεται πως είναι απενεργοποιημένο ή υπάρχει πρόβλημα στη σύνδεσή σου στο διαδίκτυο. Προσπαθώ πάλι…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Το Syncthing φαίνεται να αντιμετωπίζει ένα πρόβλημα με την επεξεργασία του αιτήματός σου. Παρακαλούμε, αν το πρόβλημα συνεχίζει, ανανέωσε την σελίδα ή επανεκκίνησε το Syncthing.",
"Take me back": "Take me back",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Το μονοπάτι δεν μπορεί να είναι κενό.",
"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 devices to share this folder with.",
"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.": "Αυτό μπορεί εύκολα να δώσει πρόσβαση ανάγνωσης και επεξεργασίας αρχείων του υπολογιστή σας σε χάκερς.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Άγνωστο",
"Unshared": "Δε μοιράζεται",
"Unshared Devices": "Unshared Devices",
"Unused": "Δε χρησιμοποιείται",
"Up to Date": "Ενημερωμένη",
"Updated": "Ενημερωμένο",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Are you sure you want to remove device {{name}}?",
"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?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"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.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Danger!",
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Deleted",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Device",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Device \"{{name}}\" ({{device}} at {{address}}) wants to connect. Add new device?",
"Device ID": "Device ID",
@@ -71,6 +75,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 +97,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 +147,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",
@@ -164,6 +171,7 @@
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Additions": "Local Additions",
"Local Discovery": "Local Discovery",
"Local State": "Local State",
"Local State (Total)": "Local State (Total)",
@@ -220,6 +228,7 @@
"Please wait": "Please wait",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Preview",
"Preview Usage Report": "Preview Usage Report",
"Quick guide to supported patterns": "Quick guide to supported patterns",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
"Select the devices to share this folder with.": "Select the devices to share this folder with.",
@@ -299,6 +309,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",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Unknown",
"Unshared": "Unshared",
"Unshared Devices": "Unshared Devices",
"Unused": "Unused",
"Up to Date": "Up to Date",
"Updated": "Updated",

View File

@@ -31,6 +31,7 @@
"Are you sure you want to remove device {%name%}?": "Are you sure you want to remove device {{name}}?",
"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?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"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.",
@@ -59,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Danger!",
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Deleted",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Device",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Device \"{{name}}\" ({{device}} at {{address}}) wants to connect. Add new device?",
"Device ID": "Device ID",
@@ -168,6 +171,7 @@
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Additions": "Local Additions",
"Local Discovery": "Local Discovery",
"Local State": "Local State",
"Local State (Total)": "Local State (Total)",
@@ -224,6 +228,7 @@
"Please wait": "Please wait",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Preview",
"Preview Usage Report": "Preview Usage Report",
"Quick guide to supported patterns": "Quick guide to supported patterns",
@@ -260,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
"Select the devices to share this folder with.": "Select the devices to share this folder with.",
@@ -332,6 +338,7 @@
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
@@ -347,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Unknown",
"Unshared": "Unshared",
"Unshared Devices": "Unshared Devices",
"Unused": "Unused",
"Up to Date": "Up to Date",
"Updated": "Updated",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Ĉu vi certas, ke vi volas forigi aparaton {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Ĉu vi certas, ke vi volas forigi dosierujon {{label}}?",
"Are you sure you want to restore {%count%} files?": "Ĉu vi certas, ke vi volas restarigi {{count}} dosierojn?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Akcepti Aŭtomate",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Aŭtomata ĝisdatigo nun proponas la elekton inter stabilaj eldonoj kaj kandidataj eldonoj.",
"Automatic upgrades": "Aŭtomataj ĝisdatigoj",
"Automatic upgrades are always enabled for candidate releases.": "Aŭtomataj ĝisdatigoj ĉiam ŝaltitaj por kandidataj eldonoj.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Kopirajto © 2014-2017 por la sekvantaj Kontribuantoj:",
"Copyright © 2014-2019 the following Contributors:": "Kopirajto © 2014-2019 por la sekvantaj Kontribuantoj:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Kreante ignorantajn ŝablonojn, anstataŭige ekzistantan dosieron ĉe {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Danĝero!",
"Debugging Facilities": "Elpurigadiloj",
"Default Folder Path": "Defaŭlta Dosieruja Vojo",
"Deleted": "Forigita",
"Deselect All": "Malelekti Ĉiujn",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Aparato",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Aparato \"{{name}}\" ({{device}} ĉe {{address}}) volas konekti. Aldoni la novan aparaton?",
"Device ID": "Aparato ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Limoj de rapideco de aparato",
"Device that last modified the item": "Aparato kiu laste modifis la eron",
"Devices": "Aparatoj",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Malebligita",
"Disabled periodic scanning and disabled watching for changes": "Malebligita perioda skanado kaj malebligita rigardado je ŝanĝoj",
"Disabled periodic scanning and enabled watching for changes": "Malebligita perioda skanado kaj ebligita rigardado je ŝanĝoj",
@@ -92,6 +97,7 @@
"Edit Folder": "Redakti Dosierujon",
"Editing": "Redaktado",
"Editing {%path%}.": "Redaktado de {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Ŝaltu trairan NAT",
"Enable Relaying": "Ŝaltu Relajsadon",
"Enabled": "Ebligita",
@@ -141,6 +147,7 @@
"Global State": "Malloka Stato",
"Help": "Helpo",
"Home page": "Hejma paĝo",
"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": "Ignoru",
"Ignore Patterns": "Ignorantaj Ŝablonoj",
"Ignore Permissions": "Ignori Permesojn",
@@ -164,6 +171,7 @@
"Listeners": "Aŭskultantoj",
"Loading data...": "Ŝarĝas datumojn...",
"Loading...": "Ŝarĝas...",
"Local Additions": "Local Additions",
"Local Discovery": "Loka Malkovro",
"Local State": "Loka Stato",
"Local State (Total)": "Loka Stato (Tuta)",
@@ -220,6 +228,7 @@
"Please wait": "Bonvolu atendi",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefikso indikanta, ke la dosiero povas esti forigita, se ĝi malhelpas forigi dosierujon",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefikso indikanta, ke la ŝablono devus esti egalita usklecoblinde.",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Antaŭrigardo",
"Preview Usage Report": "Antaŭrigardo Uzada Raporto",
"Quick guide to supported patterns": "Rapida gvidilo pri subtenata ŝablonoj",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Vidu informlibron de ekstera versionado por subtenata ŝablona parametroj de komandlinio.",
"Select All": "Elekti Ĉiujn",
"Select a version": "Elekti version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Elekti plej novan version",
"Select oldest version": "Elekti plej malnovan version",
"Select the devices to share this folder with.": "Elekti la aparatojn por komunigi ĉi tiun dosierujon.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing estas libera kaj malferma fonta programaro licencita kiel MPL v2.0.",
"Syncthing is restarting.": "Syncthing estas restartanta.",
"Syncthing is upgrading.": "Syncthing estas ĝisdatigita.",
"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 ŝajnas nefunkcii, aŭ estas problemo kun via retkonekto. Reprovado...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ŝajnas renkonti problemon kun la traktado de via peto. Bonvolu refreŝigi la paĝon aŭ restarti Syncthing se la problemo daŭras.",
"Take me back": "Prenu min reen",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "La vojo ne povas esti malplena.",
"The rate limit must be a non-negative number (0: no limit)": "La rapideca limo devas esti pozitiva nombro (0: senlimo)",
"The rescan interval must be a non-negative number of seconds.": "La intervalo de reskano devas esti pozitiva nombro da sekundoj.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Ili estas reprovitaj aŭtomate kaj estos sinkronigitaj kiam la eraro estas solvita.",
"This Device": "Ĉi Tiu Aparato",
"This can easily give hackers access to read and change any files on your computer.": "Ĉi tio povas facile doni al kodumuloj atingon por legi kaj ŝanĝi ajnajn dosierojn en via komputilo.",
@@ -342,6 +354,7 @@
"Unignore": "Malignoru",
"Unknown": "Nekonata",
"Unshared": "Nekomunigita",
"Unshared Devices": "Unshared Devices",
"Unused": "Neuzita",
"Up to Date": "Ĝisdata",
"Updated": "Ĝisdatigita",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "¿Estás seguro de que quieres quitar el dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "¿Estás seguro de que quieres quitar la carpeta {{label}}?",
"Are you sure you want to restore {%count%} files?": "¿Estás seguro de que quieres restaurar {{count}} ficheros?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Auto aceptar",
"Automatic Crash Reporting": "Informe automático de errores",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Ahora la actualización automática permite elegir entre versiones estables o versiones candidatas.",
"Automatic upgrades": "Actualizaciones automáticas",
"Automatic upgrades are always enabled for candidate releases.": "Las actualizaciones automáticas siempre están activadas para las versiones candidatas.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 Los siguientes colaboradores:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 los siguientes Colaboradores:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Crear patrones a ignorar, sobreescribiendo un fichero existente en {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "¡Peligro!",
"Debugging Facilities": "Ayudas a la depuración",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Deleted": "Eliminado",
"Deselect All": "Deseleccionar Todo",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
"Device ID": "ID del Dispositivo",
@@ -71,6 +75,7 @@
"Device rate limits": "Límites de la tasa del dispositivo",
"Device that last modified the item": "Último dispositivo que cambió el objeto",
"Devices": "Dispositivos",
"Disable Crash Reporting": "Desactivar informe de errores",
"Disabled": "Desactivado",
"Disabled periodic scanning and disabled watching for changes": "Desactivados el escaneo periódico y la vigilancia de cambios",
"Disabled periodic scanning and enabled watching for changes": "Desactivado el escaneo periódico y activada la vigilancia de cambios",
@@ -92,6 +97,7 @@
"Edit Folder": "Editar Carpeta",
"Editing": "Editando",
"Editing {%path%}.": "Editando {{path}}.",
"Enable Crash Reporting": "Permitir informe de errores",
"Enable NAT traversal": "Permitir NAT transversal",
"Enable Relaying": "Habilitar Retransmisión",
"Enabled": "Activado",
@@ -141,6 +147,7 @@
"Global State": "Estado global",
"Help": "Ayuda",
"Home page": "Página de inicio",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no querer habilitarlo. Hemos deshabilitado el informe automático de errores por usted.",
"Ignore": "Ignorar",
"Ignore Patterns": "Patrones a ignorar",
"Ignore Permissions": "Permisos a ignorar",
@@ -164,6 +171,7 @@
"Listeners": "Oyentes",
"Loading data...": "Cargando datos...",
"Loading...": "Cargando...",
"Local Additions": "Local Additions",
"Local Discovery": "Descubrimiento local",
"Local State": "Estado local",
"Local State (Total)": "Estado Local (Total)",
@@ -220,6 +228,7 @@
"Please wait": "Por favor, espere",
"Prefix indicating that the file can be deleted if preventing directory removal": "El prefijo indica que el fichero puede ser borrado si se previene la eliminación de directorios",
"Prefix indicating that the pattern should be matched without case sensitivity": "El prefijo indica que el patrón se comparará sin tener en cuenta las mayúsculas",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Vista previa",
"Preview Usage Report": "Informe de uso de vista previa",
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Consultar la ayuda externa del versionado para ver las plantillas de los parámetros de línea de comandos",
"Select All": "Seleccionar Todo",
"Select a version": "Selecciona una versión",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Selecciona la última versión",
"Select oldest version": "Selecciona la versión más antigua",
"Select the devices to share this folder with.": "Selecciona los dispositivos con los que compartir esta carpeta.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es Software Gratuito y Open Source Software licenciado como MPL v2.0.",
"Syncthing is restarting.": "Syncthing se está reiniciando.",
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora permite el informe automático de errores a los desarrolladores. Esta característica está activada por defecto.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing parece no estar activo o hay un problema con tu conexión de internet. Reintentando...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing tiene problemas para procesar tu solicitud. Por favor, actualiza la página o reinicia Syncthing si el problema persiste.",
"Take me back": "Llévame atrás",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "La ruta no puede estar vacía.",
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
"This Device": "Este Dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
@@ -342,6 +354,7 @@
"Unignore": "Designorar",
"Unknown": "Desconocido",
"Unshared": "No compartido",
"Unshared Devices": "Unshared Devices",
"Unused": "No usado",
"Up to Date": "Actualizado",
"Updated": "Actualizado",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "¿Está seguro que desea eliminar el dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "¿Está seguro que desea eliminar la carpeta {{label}}?",
"Are you sure you want to restore {%count%} files?": "¿Está seguro que desea restaurar {{count}} archivos?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Aceptar automáticamente",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Ahora la actualización automática permite elegir entre versiones estables o versiones candidatas.",
"Automatic upgrades": "Actualizaciones automáticas",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 Los siguientes colaboradores:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Crear patrones a ignorar, sobreescribiendo un fichero existente en {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "¡Peligro!",
"Debugging Facilities": "Servicios de depuración",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Deleted": "Eliminado",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
"Device ID": "ID del Dispositivo",
@@ -71,6 +75,7 @@
"Device rate limits": "Límites de velocidad del dispositivo",
"Device that last modified the item": "Dispositivo que modificó por última vez el ítem",
"Devices": "Dispositivos",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Deshabilitado",
"Disabled periodic scanning and disabled watching for changes": "Se desactivó el escaneo periódico y se desactivó el control de cambios",
"Disabled periodic scanning and enabled watching for changes": "Se desactivó el escaneo periódico y se activó el control de cambios",
@@ -92,6 +97,7 @@
"Edit Folder": "Editar Carpeta",
"Editing": "Editando",
"Editing {%path%}.": "Editando {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Permitir NAT transversal",
"Enable Relaying": "Habilitar Retransmisión",
"Enabled": "Activado",
@@ -141,6 +147,7 @@
"Global State": "Estado global",
"Help": "Ayuda",
"Home page": "Página de inicio",
"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": "Ignorar",
"Ignore Patterns": "Patrones a ignorar",
"Ignore Permissions": "Permisos a ignorar",
@@ -164,6 +171,7 @@
"Listeners": "Oyentes",
"Loading data...": "Cargando datos...",
"Loading...": "Cargando...",
"Local Additions": "Local Additions",
"Local Discovery": "Descubrimiento local",
"Local State": "Estado local",
"Local State (Total)": "Estado Local (Total)",
@@ -220,6 +228,7 @@
"Please wait": "Por favor, espere",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Vista previa",
"Preview Usage Report": "Informe de uso de vista previa",
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
"Select All": "Select All",
"Select a version": "Seleccione una versión",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Seleccione la última versión",
"Select oldest version": "Seleccione la versión más antigua",
"Select the devices to share this folder with.": "Selecciona los dispositivos con los que compartir esta carpeta.",
@@ -299,6 +309,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 se está reiniciando.",
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"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 parece no estar activo o hay un problema con tu conexión de internet. Reintentando...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing tiene problemas para procesar tu solicitud. Por favor, actualiza la página o reinicia Syncthing si el problema persiste.",
"Take me back": "Llévame de vuelta",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "La ruta no puede estar vacía.",
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
"This Device": "Este Dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
@@ -342,6 +354,7 @@
"Unignore": "Dejar de ignorar",
"Unknown": "Desconocido",
"Unshared": "No compartido",
"Unshared Devices": "Unshared Devices",
"Unused": "No usado",
"Up to Date": "Actualizado",
"Updated": "Actualizado",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Oletko varma, että haluat postaa laitteen {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Oletko varma, että haluat poistaa kansion {{label}}?",
"Are you sure you want to restore {%count%} files?": "Haluatko varmasti palauttaa {{count}} tiedostoa?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Hyväksy automaattisesti",
"Automatic Crash Reporting": "Kaatumisen automaattinen raportointi",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automaattinen päivitys sallii valita vakaiden- ja kehitysversioiden välillä.",
"Automatic upgrades": "Automaattiset päivitykset",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -56,13 +58,15 @@
"Copied from original": "Kopioitu alkuperäisestä lähteestä",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 seuraavat avustajat",
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 seuraavat avustajat:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Tekijänoikeus © 2014-2019 seuraavat avustajat:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Luodaan ohituslausekkeet. Ylikirjoitetaan tiedosto: {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Vaara!",
"Debugging Facilities": "Debug -luokat",
"Default Folder Path": "Oletuspolku kansioille",
"Deleted": "Poistettu",
"Deselect All": "Poista valinnat",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Laite",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Laite \"{{name}}\" {{device}} osoitteessa ({{address}}) haluaa yhdistää. Lisää uusi laite?",
"Device ID": "Laitteen ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Laitteen siirtonopeuden rajoitus",
"Device that last modified the item": "Laite, joka viimeisimmäksi muokkasi kohdetta",
"Devices": "Laitteet",
"Disable Crash Reporting": "Poista kaatumisraportointi käytöstä",
"Disabled": "Ei käytössä",
"Disabled periodic scanning and disabled watching for changes": "Ajoitettu skannaus ja muutosten seuranta pois päältä",
"Disabled periodic scanning and enabled watching for changes": "Ajoitettu skannaus pois päältä. Jatkuva seuranta on päällä.",
@@ -92,6 +97,7 @@
"Edit Folder": "Muokkaa kansiota",
"Editing": "Muokkaus",
"Editing {%path%}.": "Muokkaa {{path}}.",
"Enable Crash Reporting": "Ota kaatumisraportointi käyttöön",
"Enable NAT traversal": "Aktivoi osoitteenmuunnoksen kierto",
"Enable Relaying": "Aktivoi yhteyden välitys",
"Enabled": "Käytössä",
@@ -141,6 +147,7 @@
"Global State": "Globaali tila",
"Help": "Apua",
"Home page": "Kotisivu",
"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": "Ohita",
"Ignore Patterns": "Ohituslausekkeet",
"Ignore Permissions": "Jätä oikeudet huomiotta",
@@ -164,14 +171,15 @@
"Listeners": "Kuuntelijat",
"Loading data...": "Lataa...",
"Loading...": "Lataa...",
"Local Additions": "Local Additions",
"Local Discovery": "Paikallinen etsintä",
"Local State": "Paikallinen tila",
"Local State (Total)": "Paikallinen tila (Yhteensä)",
"Locally Changed Items": "Locally Changed Items",
"Locally Changed Items": "Paikallisesti muuttuneet tiedot",
"Log": "Loki",
"Log tailing paused. Click here to continue.": "Login seuraaminen pysäytetty. Jatka klikkaamalla tästä.",
"Log tailing paused. Scroll to bottom continue.": "Login seuraaminen pysäytetty. Jatka vierittämällä alas.",
"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.": "Login seuraaminen pysäytetty. Jatka vierittämällä alas.",
"Logs": "Lokit",
"Major Upgrade": "Pääversion päivitys.",
"Mass actions": "Massamuutokset",
@@ -220,6 +228,7 @@
"Please wait": "Ole hyvä ja odota",
"Prefix indicating that the file can be deleted if preventing directory removal": "Etuliite, joka määrittää että tiedosto voidaan poistaa, mikäli se estää kansion poistamisen.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Etuliite, joka määrittää että isot ja pienet kirjaimet eivät merkitse",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Esikatselu",
"Preview Usage Report": "Esikatsele käyttöraportti",
"Quick guide to supported patterns": "Tuettujen lausekkeiden pikaohje",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Katso ulkopuolisen versiohallinnan tukisivu komentoriviparametreistä.",
"Select All": "Valitse kaikki",
"Select a version": "Valitse versio",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Valitse viimeisin versio",
"Select oldest version": "Valitse vanhin versio",
"Select the devices to share this folder with.": "Valitse laitteet, joiden kanssa tämä kansio jaetaan.",
@@ -296,9 +306,10 @@
"Syncing": "Synkronoidaan",
"Syncthing has been shut down.": "Syncthing on sammutettu.",
"Syncthing includes the following software or portions thereof:": "Syncthing sisältää seuraavat ohjelmistot tai sen osat:",
"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 Free and Open Source Software licensed as MPL v2.0.": "Syncthing on avointa lähdekoodia, joka on lisensöity MPL v2.0 lisenssillä.",
"Syncthing is restarting.": "Syncthing käynnistyy uudelleen.",
"Syncthing is upgrading.": "Syncthing päivittyy.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Synthing tukee automaattista kaatumisraportointia. Tämä ominaisuus on oletuksena käytössä.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing näyttää olevan alhaalla tai internetyhteydessä on ongelma. Yritetään uudelleen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ei pysty käsittelemään pyyntöäsi. Ole hyvä ja päivitä sivu tai käynnistä Syncthing uudelleen, jos ongelma jatkuu.",
"Take me back": "Takaisin",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Polku ei voi olla tyhjä.",
"The rate limit must be a non-negative number (0: no limit)": "Nopeusrajan tulee olla positiivinen luku tai nolla. (0: ei rajaa)",
"The rescan interval must be a non-negative number of seconds.": "Uudelleenskannauksen aikavälin tulee olla ei-negatiivinen numero sekunteja.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Niiden synkronointia yritetään uudelleen automaattisesti.",
"This Device": "Tämä laite",
"This can easily give hackers access to read and change any files on your computer.": "Tämä voi helposti sallia vihamielisille tahoille pääsyn lukea ja muokata kaikkia tiedostojasi",
@@ -342,6 +354,7 @@
"Unignore": "Poista ohitus",
"Unknown": "Tuntematon",
"Unshared": "Jakamaton",
"Unshared Devices": "Unshared Devices",
"Unused": "Käyttämätön",
"Up to Date": "Ajan tasalla",
"Updated": "Päivitetty",
@@ -359,7 +372,7 @@
"Versions": "Versiot",
"Versions Path": "Versioiden polku",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versiot poistetaan automaattisesti mikäli ne ovat vanhempia kuin maksimi-ikä tai niiden määrä ylittää sallitun määrän tietyllä aikavälillä.",
"Waiting to scan": "Waiting to scan",
"Waiting to scan": "Odotetaan skannausta",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varoitus: tämä polku on olemassa olevan kansion \"{{otherFolder}}\" yläkansio.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varoitus: Tämä kansio on jo olemassa olevan kansion yläkansio \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varoitus: tämä polku on olemassa olevan kansion \"{{otherFolder}}\" alikansio.",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Êtes-vous sûr de vouloir supprimer l'appareil {{name}} ?",
"Are you sure you want to remove folder {%label%}?": "Êtes-vous sûr de vouloir supprimer le partage {{label}} ?",
"Are you sure you want to restore {%count%} files?": "Êtes-vous sûr de vouloir restaurer {{count}} fichiers ?",
"Are you sure you want to upgrade?": "Voulez-vous vraiment mettre à jour ?",
"Auto Accept": "Accepter automatiquement",
"Automatic Crash Reporting": "Rapports de plantage automatiques",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Le système de mise à jour automatique propose le choix entre versions stables et versions préliminaires.",
"Automatic upgrades": "Mises à jour automatiques",
"Automatic upgrades are always enabled for candidate releases.": "Les mises à jour automatiques sont toujours activées pour les versions préliminaires (-rc.N).",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017, les contributeurs sont:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 les Contributeurs suivants :",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Création de masques d'exclusion, remplacement du fichier existant : {{path}}.",
"Currently Shared With Devices": "Appareils membres actuels de ce partage :",
"Danger!": "Attention !",
"Debugging Facilities": "Outils de débogage",
"Default Folder Path": "Chemin parent par défaut pour les nouveaux partages",
"Deleted": "Supprimé",
"Deselect All": "Tout déselectionner",
"Deselect devices to stop sharing this folder with.": "Désélectionnez les appareils avec lesquels vous ne souhaitez plus partager ce dossier.",
"Device": "Appareil",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" ({{device}}), appareil actuellement à {{address}}, demande à se connecter.\nAcceptez-vous de l'ajouter à votre liste d'appareils connus ?",
"Device ID": "ID de l'appareil",
@@ -71,6 +75,7 @@
"Device rate limits": "Limitations de débit",
"Device that last modified the item": "Dernier appareil modificateur",
"Devices": "Appareils",
"Disable Crash Reporting": "Désactiver l'envoi des rapports de plantage automatiques",
"Disabled": "Désactivé",
"Disabled periodic scanning and disabled watching for changes": "Analyse périodique et surveillance des changements désactivées.",
"Disabled periodic scanning and enabled watching for changes": "Analyse périodique désactivée et surveillance des changements activée.",
@@ -92,6 +97,7 @@
"Edit Folder": "Gérer le partage",
"Editing": "Modifications",
"Editing {%path%}.": "Modification de {{path}}.",
"Enable Crash Reporting": "Activer l'envoi des rapports de plantage automatiques",
"Enable NAT traversal": "Activer la translation d'adresses (NAT)",
"Enable Relaying": "Relayage possible",
"Enabled": "Activée",
@@ -139,8 +145,9 @@
"Global Discovery": "Découverte globale",
"Global Discovery Servers": "Serveurs de découverte globale",
"Global State": "État global",
"Help": "Aide (anglais)",
"Help": "Aide (en anglais)",
"Home page": "Page d'accueil",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Cependant, vos réglages indiquent que vous pourriez souhaiter ne pas l'activer. Nous avons désactivé pour vous l'envoi automatique des rapports.",
"Ignore": "Refuser",
"Ignore Patterns": "Exclusions...",
"Ignore Permissions": "Ignorer les permissions",
@@ -164,6 +171,7 @@
"Listeners": "Systèmes en écoute",
"Loading data...": "Chargement des données...",
"Loading...": "Chargement...",
"Local Additions": "Ajouts locaux",
"Local Discovery": "Découverte locale",
"Local State": "État local",
"Local State (Total)": "État local (Total)",
@@ -220,6 +228,7 @@
"Please wait": "Merci de patienter",
"Prefix indicating that the file can be deleted if preventing directory removal": "Ce préfixe autorise la suppression des fichiers pour permettre la suppression de répertoires.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Ce préfixe, inutile sur Windows et Mac OS, indique que le masque d'exclusion est insensible à la casse.",
"Preparing to Sync": "Préparation à la synchronisation",
"Preview": "Aperçu",
"Preview Usage Report": "Aperçu du rapport de statistiques d'utilisation",
"Quick guide to supported patterns": "Guide rapide des masques compatibles ci-dessous",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Consulter l'aide à la gestion externe des versions pour voir les paramètres de ligne de commande supportés.",
"Select All": "Tout sélectionner",
"Select a version": "Choisissez une version",
"Select additional devices to share this folder with.": "Sélectionnez des appareils membres supplémentaires pour ce partage.",
"Select latest version": "Restaurer la dernière version",
"Select oldest version": "Restaurer la plus ancienne version",
"Select the devices to share this folder with.": "Choisir les participants :",
@@ -269,7 +279,7 @@
"Share With Devices": "Synchroniser avec des appareils",
"Share this folder?": "Acceptez-vous ce partage ?",
"Shared With": "Participant(s)",
"Sharing": "Partage",
"Sharing": "Partages",
"Show ID": "Afficher mon ID",
"Show QR": "Afficher le QR",
"Show diff with previous version": "Afficher les différences avec la version précédente",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing est un logiciel Libre et Open Source sous licence MPL v2.0.",
"Syncthing is restarting.": "Syncthing redémarre.",
"Syncthing is upgrading.": "Syncthing se met à jour.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing permet maintenant d'envoyer automatiquement aux développeurs des rapports de plantage. Cette fonctionnalité est activée par défaut.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing semble être arrêté, ou il y a un problème avec votre connexion Internet. Nouvelle tentative ...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing semble avoir un problème pour traiter votre demande. Rafraîchissez la page (F5 sur PC) ou redémarrez Syncthing si le problème persiste.",
"Take me back": "Vérifier ...",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Le chemin ne peut pas être vide.",
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0 = pas de limite)",
"The rescan interval must be a non-negative number of seconds.": "L'intervalle d'analyse ne doit pas être un nombre négatif de secondes.",
"There are no devices to share this folder with.": "Il n'y a aucun appareil à ajouter à ce partage.",
"They are retried automatically and will be synced when the error is resolved.": "Ils seront automatiquement retentés et synchronisés quand l'erreur sera résolue.",
"This Device": "Cet appareil",
"This can easily give hackers access to read and change any files on your computer.": "Ceci peut aisément permettre à un intrus de lire et modifier n'importe quel fichier de votre ordinateur.",
@@ -342,6 +354,7 @@
"Unignore": "Reprendre",
"Unknown": "Inconnu",
"Unshared": "Non partagé",
"Unshared Devices": "Appareils non membres de ce partage :",
"Unused": "Non utilisé",
"Up to Date": "À jour",
"Updated": "Mis à jour",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Bist der wis fan datsto apparaat {{name}} fuortsmite wolst?",
"Are you sure you want to remove folder {%label%}?": "Bist der wis fan datsto map {{label}} fuortsmite wolst?",
"Are you sure you want to restore {%count%} files?": "Bist der wis fan datsto {{count}} triemen weromsette wolst?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Auto-akseptaasje",
"Automatic Crash Reporting": "Automatyske Rapportaazje fan Fêstrinners",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatyske fernijing biedt no de kar tusken stabyle ferzjes en ferzje kandidaten",
"Automatic upgrades": "Automatyske fernijings",
"Automatic upgrades are always enabled for candidate releases.": "Automatyske opwurdearrings stean altyd oan foar kandidaat-ferzjes.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 de folgende Bydragers:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 de folgende Bydragers:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Meitsje negear-patroanen dy in besteande triem oerskriuwe yn {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Gefaar!",
"Debugging Facilities": "Debug-foarsjennings",
"Default Folder Path": "Standert Map-paad",
"Deleted": "Fuortsmiten",
"Deselect All": "Alles Deselektearje",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Apparaat",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" {{device}} op ({{address}}) wol ferbining meitsje. Nij apparaat taheakje?",
"Device ID": "Apparaat-ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Apparaatfluggenslimiet",
"Device that last modified the item": "Apparaat dat dit item it lêst oanpast hat",
"Devices": "Apparaten",
"Disable Crash Reporting": "Automatyske Rapportaazje fan Fêstrinners Utsette",
"Disabled": "Utskeakele",
"Disabled periodic scanning and disabled watching for changes": "Periodic scanning útskeakele en feroarings wurde net mear yn'e gaten hâlden.",
"Disabled periodic scanning and enabled watching for changes": "Periodic scanning útskeakele en feroarings wurde yn'e gaten hâlden.",
@@ -92,6 +97,7 @@
"Edit Folder": "Map Bewurkje",
"Editing": "Bewurkjen",
"Editing {%path%}.": "{{path}} wurd bewurke.",
"Enable Crash Reporting": "Automatyske Rapportaazje fan Fêstrinners Oansette",
"Enable NAT traversal": "NAT-trochkruse ynskeakelje",
"Enable Relaying": "Trochjaan tastean",
"Enabled": "Ynskeakele",
@@ -141,6 +147,7 @@
"Global State": "Wrâldwide tastân",
"Help": "Help",
"Home page": "Hiemstee",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Lykwols, jo aktuele ynstellings litte sjen dat jo it miskien net oan sette wol. Wy hawwe automatysk rapportearjen fan fêstrinnen foar jo útsetten.",
"Ignore": "Negearje",
"Ignore Patterns": "Negear-patroanen",
"Ignore Permissions": "Negear-rjochten",
@@ -164,6 +171,7 @@
"Listeners": "Harkers",
"Loading data...": "Data oan it laden...",
"Loading...": "Oan it laden...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokale ûntdekking",
"Local State": "Lokale tastân",
"Local State (Total)": "Lokale tastân (Folledich)",
@@ -171,7 +179,7 @@
"Log": "Loch",
"Log tailing paused. Click here to continue.": "Loch-sturt skofte. Klik hjir om fjirder te gean.",
"Log tailing paused. Scroll to bottom continue.": "Loch-sturt skofte. Rolje helendal nei ûnder om fjirder te gean.",
"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.": "Loch-sturt skofte. Rolje helendal nei ûnder om fjirder te gean.",
"Logs": "Lochs",
"Major Upgrade": "Wichtige fernijing",
"Mass actions": "Massa-aksjes",
@@ -220,6 +228,7 @@
"Please wait": "In amerijke",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefiks dy't oanjout dat de triem fourtsmiten wurde kin wannear dit it ferwiderjen belet.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefiks dy't oanjout dat fergelykings mei it patroans net haadlettergefoelich wêze sille.",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Foarbyld",
"Preview Usage Report": "Foarbyld fan brûkensrapport ",
"Quick guide to supported patterns": "Fluch-paadwizer foar stipe patroanen",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Sjoch de eksterne help fan fersjebehearder foar stipe foarbylden fan kommando-rige-parameters.",
"Select All": "Alles Selektearje",
"Select a version": "Kies in ferzje",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Selektearje de nijste ferzje",
"Select oldest version": "Selektearje de âldste ferzje",
"Select the devices to share this folder with.": "Sykje de apparaten út om dizze map mei te dielen.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Fergees en Iepenboarne Programmatuer mei in MPL V2.0 lisinsje.",
"Syncthing is restarting.": "Syncthing oan it werstarten.",
"Syncthing is upgrading.": "Syncthing is oan it fernijen.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Synthing stipet no it automatysk rapportearjen fan fêstrinners nei de ûntwikkelders. Dizze eigenskip stiet standert út.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "It liket dêrop dat Syncthing op dit stuit net rint, of der is in swierrichheid mei jo ynternetferbining. Wurd no opnij besocht...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "It liket dêrop dat Syncthing swierrichheden ûnderfynt mei it ferwurkjen fan jo fersyk. Graach de stee ferfarskje of Syncthing werstarte as it probleem der bliuwt.",
"Take me back": "Bring my werom",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "It paad mei net leech wêze.",
"The rate limit must be a non-negative number (0: no limit)": "It fluggenslimyt moat in posityf nûmer wêze (0: gjin limyt)",
"The rescan interval must be a non-negative number of seconds.": "It wersken-ynterfal moat in posityf tal fan sekonden wêze.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Sy wurde automatysk opnij probearre en sille syngronisearre wurde wannear at de flater oplost is.",
"This Device": "Dit Apparaat",
"This can easily give hackers access to read and change any files on your computer.": "Dit kin samar ynkringers (hackers) tagong jaan om elke triem op jo kompjûter te besjen en te feroarjen.",
@@ -342,6 +354,7 @@
"Unignore": "Net mear negeare",
"Unknown": "Unbekend",
"Unshared": "Net dielt",
"Unshared Devices": "Unshared Devices",
"Unused": "Net brûkt",
"Up to Date": "By de tiid",
"Updated": "Fernijt",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Biztos, hogy el akarod távolítani az eszközt: {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Biztos, hogy el akarod távolítani a mappát: {{label}}?",
"Are you sure you want to restore {%count%} files?": "Biztos, hogy vissza akarod állítani a(z) {{count}} fájlt?",
"Are you sure you want to upgrade?": "Valóban frissíthető?",
"Auto Accept": "Automatikus elfogadás",
"Automatic Crash Reporting": "Automatikus összeomlás-jelentés",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Az automatikus frissítés most lehetőséget kínál a stabil és az előzetes kiadások közötti választásra.",
"Automatic upgrades": "Automatikus frissítések",
"Automatic upgrades are always enabled for candidate releases.": "Az előzetes kiadásokban az automatikus frissítések mindig engedélyezettek.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Szerzői jog © 2014-2017 az alábbi közreműködők:",
"Copyright © 2014-2019 the following Contributors:": "Szerzői jog © 2014-2019 az alábbi közreműködők:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Mellőzési minták létrehozása, egy létező fájl felülírása itt: {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Veszély!",
"Debugging Facilities": "Hibakeresési képességek",
"Default Folder Path": "Alapértelmezett mappa útvonala",
"Deleted": "Törölve",
"Deselect All": "Kijelölés megszüntetése",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Eszköz",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" eszköz ({{device}} @ {{address}}) szeretne csatlakozni. Hozzáadható az új eszköz?",
"Device ID": "Eszközazonosító",
@@ -71,6 +75,7 @@
"Device rate limits": "Eszköz sávszélessége",
"Device that last modified the item": "Az eszköz, amely utoljára módosította az elemet",
"Devices": "Eszközök",
"Disable Crash Reporting": "Összeomlás-jelentés letiltása",
"Disabled": "Letiltva",
"Disabled periodic scanning and disabled watching for changes": "A periodikus átnézés és a változások keresése letiltva",
"Disabled periodic scanning and enabled watching for changes": "A periodikus átnézés letiltva a változások keresése engedélyezve",
@@ -92,6 +97,7 @@
"Edit Folder": "Mappa szerkesztése",
"Editing": "Szerkesztés",
"Editing {%path%}.": "{{path}} szerkesztése.",
"Enable Crash Reporting": "Összeomlás-jelentés engedélyezése",
"Enable NAT traversal": "NAT bejárás engedélyezése",
"Enable Relaying": "Közvetítés engedélyezése",
"Enabled": "Engedélyezve",
@@ -141,6 +147,7 @@
"Global State": "Globális állapot",
"Help": "Súgó",
"Home page": "Főoldal",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "A jelenlegi beállítások azonban azt jelzik, hogy nem kívánja engedélyezni. Az automatikus összeomlás-jelentés ezért letiltásra került.",
"Ignore": "Mellőzés",
"Ignore Patterns": "Mellőzési minták",
"Ignore Permissions": "Jogosultságok mellőzése",
@@ -164,6 +171,7 @@
"Listeners": "Kapcsolatok",
"Loading data...": "Adatok betöltése...",
"Loading...": "Betöltés...",
"Local Additions": "Local Additions",
"Local Discovery": "Helyi felfedezés",
"Local State": "Helyi állapot",
"Local State (Total)": "Helyi állapot (teljes)",
@@ -220,6 +228,7 @@
"Please wait": "Türelem",
"Prefix indicating that the file can be deleted if preventing directory removal": "Előtag, amely jelzi, hogy a fájl törölhető, ha tiltva van a mappák eltávolítása",
"Prefix indicating that the pattern should be matched without case sensitivity": "Előtag, amely jelzi, hogy a mintát nagy- ill. kisbetűérzékenység nélkül kell illeszteni.",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Előnézet",
"Preview Usage Report": "Használati jelentés áttekintése",
"Quick guide to supported patterns": "Rövid útmutató a használható mintákról",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "A támogatott parancssori paraméter sablonokat a külső verziókezelő súgójában találod.",
"Select All": "Mindent kijelöl",
"Select a version": "Válassz egy verziót",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Legfrissebb verzió kijelölése",
"Select oldest version": "Legrégebbi verzió kijelölése",
"Select the devices to share this folder with.": "Eszközök, amelyekkel megosztandó a mappa",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "A Syncthing szabad és nyílt forráskódú szoftver MPL v2.0 licenccel.",
"Syncthing is restarting.": "Syncthing újraindul",
"Syncthing is upgrading.": "Syncthing frissül",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "A Syncthing már támogatja az automatikus összeomlás-jelentések küldését a fejlesztők felé. Ez a funkció alapértelmezetten be van kapcsolva.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Úgy tűnik, hogy a Syncthing nem működik, vagy valami probléma van a hálózati kapcsolattal. Újra próbálom...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Úgy tűnik, hogy a Syncthing problémába ütközött a kérés feldolgozása során. Ha a probléma továbbra is fennáll, akkor frissíteni kell az oldalt, vagy újra kell indítani a Syncthinget.",
"Take me back": "Vissza",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Az elérési útvonal nem lehet üres.",
"The rate limit must be a non-negative number (0: no limit)": "A sebességlimitnek pozitív számnak kell lennie (0: nincs limit)",
"The rescan interval must be a non-negative number of seconds.": "Az átnézési intervallum nullánál nagyobb másodperc érték kell legyen.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "A hiba javítása után automatikusan újra megpróbálja a szinkronizálást.",
"This Device": "Ez az eszköz",
"This can easily give hackers access to read and change any files on your computer.": "Így a hekkerek könnyedén hozzáférést szerezhetnek a gépen tárolt fájlok olvasásához és módosításához.",
@@ -342,6 +354,7 @@
"Unignore": "Mellőzés visszavonása",
"Unknown": "Ismeretlen",
"Unshared": "Nincs megosztva",
"Unshared Devices": "Unshared Devices",
"Unused": "Nincs használatban",
"Up to Date": "Friss",
"Updated": "Frissítve",

View File

@@ -31,7 +31,9 @@
"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}}?",
"Are you sure you want to restore {%count%} files?": "Sei sicuro di voler ripristinare {{count}} file?",
"Are you sure you want to upgrade?": "Sei sicuro di voler aggiornare?",
"Auto Accept": "Accettazione Automatica",
"Automatic Crash Reporting": "Segnalazione Automatica degli Arresti Anomali",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Gli aggiornamenti automatici offrono la scelta tra versioni stabili e versioni candidate al rilascio.",
"Automatic upgrades": "Aggiornamenti automatici",
"Automatic upgrades are always enabled for candidate releases.": "Gli aggiornamenti automatici sono sempre abilitati per le versioni candidate al rilascio.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 i seguenti Collaboratori:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 i seguenti Collaboratori:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creazione di schemi di esclusione, sovrascrivendo un file esistente in {{path}}.",
"Currently Shared With Devices": "Attualmente Condiviso Con Dispositivi",
"Danger!": "Pericolo!",
"Debugging Facilities": "Servizi di Debug",
"Default Folder Path": "Percorso Cartella di Default",
"Deleted": "Cancellato",
"Deselect All": "Deseleziona tutto",
"Deselect devices to stop sharing this folder with.": "Deseleziona i dispositivi con cui interrompere la condivisione di questa cartella.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Il dispositivo \"{{name}}\" ({{device}} - {{address}}) chiede di connettersi. Aggiungere il nuovo dispositivo?",
"Device ID": "ID Dispositivo",
@@ -71,6 +75,7 @@
"Device rate limits": "Limiti di velocità del dispositivo",
"Device that last modified the item": "Dispositivo che ha modificato l'elemento per ultimo",
"Devices": "Dispositivi",
"Disable Crash Reporting": "Disattiva la Segnalazione degli Arresti Anomali",
"Disabled": "Disabilitato",
"Disabled periodic scanning and disabled watching for changes": "Scansione periodica disabilitata e monitoraggio cambiamenti disabilitata",
"Disabled periodic scanning and enabled watching for changes": "Scansione periodica disabilitata e monitoraggio cambiamenti abilitata",
@@ -92,6 +97,7 @@
"Edit Folder": "Modifica Cartella",
"Editing": "Modifica di",
"Editing {%path%}.": "Modifica di {{path}}.",
"Enable Crash Reporting": "Attiva la Segnalazione degli Arresti Anomali",
"Enable NAT traversal": "Abilita NAT traversal",
"Enable Relaying": "Abilita Reindirizzamento",
"Enabled": "Abilitato",
@@ -141,6 +147,7 @@
"Global State": "Stato Globale",
"Help": "Aiuto",
"Home page": "Pagina home",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Tuttavia, le impostazioni correnti indicano che potresti non volerla attiva. Abbiamo disattivato la segnalazione automatica degli arresti anomali per te.",
"Ignore": "Ignora",
"Ignore Patterns": "Schemi Esclusione File",
"Ignore Permissions": "Ignora Permessi",
@@ -159,11 +166,12 @@
"Last seen": "Ultima connessione",
"Later": "Più Tardi",
"Latest Change": "Ultima Modifica",
"Learn more": "Impara di piu",
"Learn more": "Per saperne di più",
"Limit": "Limite",
"Listeners": "In Ascolto",
"Loading data...": "Caricamento dati...",
"Loading...": "Caricamento...",
"Local Additions": "Aggiunte Locali",
"Local Discovery": "Individuazione Locale",
"Local State": "Stato Locale",
"Local State (Total)": "Stato Locale (Totale)",
@@ -190,7 +198,7 @@
"No": "No",
"No File Versioning": "Nessun Controllo Versione",
"No files will be deleted as a result of this operation.": "Nessun file verrà eliminato come risultato di questa operazione.",
"No upgrades": "Senza aggiornamenti",
"No upgrades": "Nessun aggiornamento",
"Normal": "Normale",
"Notice": "Avviso",
"OK": "OK",
@@ -220,6 +228,7 @@
"Please wait": "Attendere prego",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefisso che indica che il file può essere eliminato se impedisce la rimozione della cartella",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefisso che indica che lo schema deve essere abbinato senza tener conto delle maiuscole",
"Preparing to Sync": "Preparazione alla Sincronizzazione",
"Preview": "Anteprima",
"Preview Usage Report": "Anteprima Statistiche di Utilizzo",
"Quick guide to supported patterns": "Guida veloce agli schemi supportati",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Consultare la guida al controllo di versione per i modelli dei parametri di riga di comando supportati.",
"Select All": "Seleziona Tutto",
"Select a version": "Seleziona una versione",
"Select additional devices to share this folder with.": "Seleziona altri dispositivi con cui condividere questa cartella.",
"Select latest version": "Seleziona l'ultima versione",
"Select oldest version": "Seleziona la versione più vecchia",
"Select the devices to share this folder with.": "Seleziona i dispositivi con i quali condividere questa cartella.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing è un software Libero e Open Source concesso in licenza MPL v2.0.",
"Syncthing is restarting.": "Riavvio di Syncthing in corso.",
"Syncthing is upgrading.": "Aggiornamento di Syncthing in corso.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ora supporta la segnalazione automaticamente agli sviluppatori degli arresti anomali. Questa funzione è abilitata per impostazione predefinita.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing sembra inattivo, oppure c'è un problema con la tua connessione a Internet. Nuovo tentativo…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Sembra che Syncthing abbia problemi nell'elaborazione della tua richiesta. Aggiorna la pagina o riavvia Syncthing se il problema persiste.",
"Take me back": "Portami indietro",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Il percorso non può essere vuoto.",
"The rate limit must be a non-negative number (0: no limit)": "Il limite di banda deve essere un numero non negativo (0: nessun limite)",
"The rescan interval must be a non-negative number of seconds.": "L'intervallo di scansione deve essere un numero non negativo secondi.",
"There are no devices to share this folder with.": "Non ci sono dispositivi con cui condividere questa cartella.",
"They are retried automatically and will be synced when the error is resolved.": "Verranno effettuati tentativi in automatico e verranno sincronizzati quando l'errore sarà risolto.",
"This Device": "Questo Dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Ciò potrebbe facilmente permettere agli hackers accesso alla lettura e modifica di qualunque file del tuo computer.",
@@ -342,6 +354,7 @@
"Unignore": "Non ignorare",
"Unknown": "Sconosciuto",
"Unshared": "Non Condiviso",
"Unshared Devices": "Dispositivi Non Condivisi",
"Unused": "Non Utilizzato",
"Up to Date": "Sincronizzato",
"Updated": "Aggiornato",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "デバイス {{name}} を削除してよろしいですか?",
"Are you sure you want to remove folder {%label%}?": "フォルダー {{label}} を削除してよろしいですか?",
"Are you sure you want to restore {%count%} files?": "Are you sure you want to restore {{count}} files?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "自動承諾",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動アップグレードは、安定版とリリース候補版のいずれかを選べるようになりました。",
"Automatic upgrades": "自動アップグレード",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "無視パターンを作成中。既存のファイルが {{path}} にある場合は上書きされます。",
"Currently Shared With Devices": "現在共有中のデバイス",
"Danger!": "危険!",
"Debugging Facilities": "デバッグ機能",
"Default Folder Path": "デフォルトのフォルダーパス",
"Deleted": "削除",
"Deselect All": "すべて選択解除",
"Deselect devices to stop sharing this folder with.": "このフォルダの共有を停止したいデバイスがある場合は、当該デバイスの選択を解除してください。",
"Device": "デバイス",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "デバイス「{{name}}」 ({{address}} の {{device}}) が接続を求めています。新しいデバイスとして追加しますか?",
"Device ID": "デバイスID",
@@ -71,6 +75,7 @@
"Device rate limits": "デバイス速度制限",
"Device that last modified the item": "Device that last modified the item",
"Devices": "デバイス",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "無効",
"Disabled periodic scanning and disabled watching for changes": "定期スキャンと変更の監視はいずれも無効です",
"Disabled periodic scanning and enabled watching for changes": "定期スキャンは無効で変更の監視は有効です",
@@ -92,6 +97,7 @@
"Edit Folder": "フォルダーの編集",
"Editing": "編集中",
"Editing {%path%}.": "{{path}} を編集中",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "NATトラバーサルを有効にする",
"Enable Relaying": "中継サーバー経由の通信を有効にする",
"Enabled": "有効",
@@ -141,6 +147,7 @@
"Global State": "グローバル状態",
"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.",
"Ignore": "無視",
"Ignore Patterns": "無視するファイル名",
"Ignore Permissions": "パーミッションを無視する",
@@ -164,6 +171,7 @@
"Listeners": "待ち受けポート",
"Loading data...": "データの読み込み中...",
"Loading...": "読み込み中...",
"Local Additions": "Local Additions",
"Local Discovery": "LAN内で探索",
"Local State": "ローカル状態",
"Local State (Total)": "ローカル状態 (合計)",
@@ -220,6 +228,7 @@
"Please wait": "お待ちください",
"Prefix indicating that the file can be deleted if preventing directory removal": "このファイルが中に残っているためにディレクトリを削除できない場合、このファイルごと消してもよいことを示す接頭辞",
"Prefix indicating that the pattern should be matched without case sensitivity": "大文字・小文字を同一視してマッチさせる接頭辞",
"Preparing to Sync": "Preparing to Sync",
"Preview": "プレビュー",
"Preview Usage Report": "使用状況レポートのプレビュー",
"Quick guide to supported patterns": "サポートされているパターンのクイックガイド",
@@ -242,7 +251,7 @@
"Restart": "再起動",
"Restart Needed": "再起動が必要です",
"Restarting": "再起動中",
"Restore": "Restore",
"Restore": "復元",
"Restore Versions": "Restore Versions",
"Resume": "再開",
"Resume All": "すべて再開",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "使用可能なコマンドラインパラメータについてはヘルプの外部バージョン管理の項目を参照してください。",
"Select All": "すべて選択",
"Select a version": "バージョンを選択してください",
"Select additional devices to share this folder with.": "このフォルダの共有に追加したいデバイスがある場合は、当該デバイスを選択してください。",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
"Select the devices to share this folder with.": "このフォルダーを共有するデバイスを選択してください。",
@@ -296,12 +306,13 @@
"Syncing": "同期中",
"Syncthing has been shut down.": "Syncthingをシャットダウンしました。",
"Syncthing includes the following software or portions thereof:": "Syncthingは以下のソフトウェアまたはその一部を内包しています:",
"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 Free and Open Source Software licensed as MPL v2.0.": "Syncthingはフリーでオープンソースのソフトウェアであり、ライセンスは MPL v2.0 です。",
"Syncthing is restarting.": "Syncthingを再起動しています。",
"Syncthing is upgrading.": "Syncthingをアップグレード中です。",
"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が落ちているか、インターネット接続に問題があります。リトライ中です…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "リクエストの処理に問題があるようです。問題が継続する場合、ページを更新するかSyncthingを再起動してください。",
"Take me back": "Take me back",
"Take me back": "キャンセル",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthingの管理画面が、パスワードなしで外部からアクセスできるように設定されています。",
"The aggregated statistics are publicly available at the URL below.": "集計結果は以下のURLで公開されています。",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "パスを入力してください。",
"The rate limit must be a non-negative number (0: no limit)": "帯域制限値は0以上で指定して下さい。 (0で無制限)",
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔は0秒以上で指定してください。",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"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.": "この設定のままでは、あなたのコンピューターにある任意のファイルを、他者が簡単に盗み見たり書き換えたりすることができます。",
@@ -342,6 +354,7 @@
"Unignore": "無視を解除",
"Unknown": "不明",
"Unshared": "非共有",
"Unshared Devices": "非共有のデバイス",
"Unused": "未使用",
"Up to Date": "最新",
"Updated": "更新",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "{{name}} 장치를 제거 하시겠습니까?",
"Are you sure you want to remove folder {%label%}?": "{{label}} 폴더를 제거 하시겠습니까?",
"Are you sure you want to restore {%count%} files?": "{{count}} 개의 파일을 복원 하시겠습니까?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "자동 수락",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "자동 업데이트를 이제 안정 버전과 출시 후보 사이에 선택 할 수 있게 됩니다.",
"Automatic upgrades": "자동 업데이트",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "무시 패턴 만들기, {{path}}에 존재하는 파일을 덮어쓰기 합니다",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "경고!",
"Debugging Facilities": "디버깅 기능",
"Default Folder Path": "기본 폴더 경로",
"Deleted": "삭제됨",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "기기",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "다른 기기 {{device}} ({{address}}) 에서 접속을 요청했습니다. 새 장치를 추가하시겠습니까?",
"Device ID": "기기 ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Device rate limits",
"Device that last modified the item": "항목을 마지막으로 수정 한 기기",
"Devices": "기기",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "비활성화",
"Disabled periodic scanning and disabled watching for changes": "주기적 스캔을 사용 중지하고 변경 사항을 감시하지 않음",
"Disabled periodic scanning and enabled watching for changes": "주기적 스캔을 사용 중지하고 변경 사항 감시 하기",
@@ -92,6 +97,7 @@
"Edit Folder": "폴더 편집",
"Editing": "편집",
"Editing {%path%}.": "{{path}} 수정하기.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "NAT traversal 활성화",
"Enable Relaying": "Relaying 활성화",
"Enabled": "활성화됨",
@@ -141,6 +147,7 @@
"Global State": "글로벌 서버 상태",
"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.",
"Ignore": "무시",
"Ignore Patterns": "패턴 무시",
"Ignore Permissions": "권한 무시",
@@ -164,6 +171,7 @@
"Listeners": "수신자",
"Loading data...": "데이터 불러오는중...",
"Loading...": "불러오는 중...",
"Local Additions": "Local Additions",
"Local Discovery": "로컬 노드 검색",
"Local State": "로컬 상태",
"Local State (Total)": "로컬 상태 (합계)",
@@ -220,6 +228,7 @@
"Please wait": "기다려 주십시오",
"Prefix indicating that the file can be deleted if preventing directory removal": "디렉토리 제거를 방지 할 경우 파일을 삭제할 수 있음을 나타내는 접두사",
"Prefix indicating that the pattern should be matched without case sensitivity": "대소 문자를 구분하지 않고 패턴을 일치시켜야 함을 나타내는 접두사",
"Preparing to Sync": "Preparing to Sync",
"Preview": "미리보기",
"Preview Usage Report": "사용 보고서 미리보기",
"Quick guide to supported patterns": "지원하는 패턴에 대한 빠른 도움말",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "지원되는 템플릿 명령 행 매개 변수에 대해서는 외부 버전 도움말을 참조하십시오.",
"Select All": "Select All",
"Select a version": "버전 선택",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "가장 최신 버전 선택",
"Select oldest version": "가장 오래된 버전 선택",
"Select the devices to share this folder with.": "이 폴더를 공유할 장치를 선택합니다.",
@@ -299,6 +309,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이 재시작 중입니다.",
"Syncthing is upgrading.": "Syncthing이 업데이트 중입니다.",
"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이 중지되었거나 인터넷 연결에 문제가 있는 것 같습니다. 재시도 중입니다...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing에서 요청을 처리하는 중에 문제가 발생했습니다. 계속 문제가 발생하면 페이지를 다시 불러오거나 Syncthing을 재시작해 보세요.",
"Take me back": "Take me back",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "경로는 비워 둘 수 없습니다.",
"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 devices to share this folder with.",
"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.": "이 설정은 해커가 손쉽게 사용자 컴퓨터의 모든 파일을 읽고 변경할 수 있도록 할 수 있습니다.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "알 수 없음",
"Unshared": "공유되지 않음",
"Unshared Devices": "Unshared Devices",
"Unused": "사용되지 않음",
"Up to Date": "최신 데이터",
"Updated": "업데이트 완료",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Ar tikrai norite pašalinti įrenginį {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Ar tikrai norite pašalinti aplanką {{label}}?",
"Are you sure you want to restore {%count%} files?": "Ar tikrai norite atkurti {{count}} failų(-us)?",
"Are you sure you want to upgrade?": "Ar tikrai norite naujinti?",
"Auto Accept": "Automatiškai priimti",
"Automatic Crash Reporting": "Automatinės ataskaitos apie strigtis",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatiniai atnaujinimai dabar siūlo pasirinkimą tarp stabilių versijų ir kandidatinių versijų.",
"Automatic upgrades": "Automatiniai atnaujinimai",
"Automatic upgrades are always enabled for candidate releases.": "Automatiniai naujinimai kandidatinėms versijoms visada yra įjungti.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Autorių teisės © 2014-2017 šių bendraautorių:",
"Copyright © 2014-2019 the following Contributors:": "Autorių teisės © 2014-2019 šių bendraautorių:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Kuriami nepaisomi šablonai, perrašomas esamas failas, esantis {{path}}.",
"Currently Shared With Devices": "Šiuo metu bendrinama su įrenginiais",
"Danger!": "Pavojus!",
"Debugging Facilities": "Derinimo priemonės",
"Default Folder Path": "Numatytojo aplanko kelias",
"Deleted": "Ištrinta",
"Deselect All": "Nuimti žymėjimą nuo visų",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Įrenginys",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Įrenginys \"{{name}}\" ({{device}} {{address}}) nori prisijungti. Pridėti naują įrenginį?",
"Device ID": "Įrenginio ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Įrenginio spartos apribojimai",
"Device that last modified the item": "Įrenginys, kuris paskutinis modifikavo elementą",
"Devices": "Įrenginiai",
"Disable Crash Reporting": "Išjungti automatines ataskaitas apie strigtis",
"Disabled": "Išjungta",
"Disabled periodic scanning and disabled watching for changes": "Išjungtas periodinis nuskaitymas ir išjungtas pakeitimų stebėjimas",
"Disabled periodic scanning and enabled watching for changes": "Išjungtas periodinis nuskaitymas ir įjungtas pakeitimų stebėjimas",
@@ -92,6 +97,7 @@
"Edit Folder": "Redaguoti aplanką",
"Editing": "Redagavimas",
"Editing {%path%}.": "Redaguojama {{path}}.",
"Enable Crash Reporting": "Įjungti automatines ataskaitas apie strigtis",
"Enable NAT traversal": "Leisti kirsti NAT",
"Enable Relaying": "Įjungti retransliavimą",
"Enabled": "Įjungta",
@@ -141,6 +147,7 @@
"Global State": "Visuotinė būsena",
"Help": "Pagalba",
"Home page": "Pagrindinis puslapis",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Vis dėlto, jūsų esami nustatymai nurodo, kad jūs, greičiausiai, nenorite turėti jas įjungtas. Mes jums išjungėme automatines ataskaitas apie strigtis.",
"Ignore": "Nepaisyti",
"Ignore Patterns": "Nepaisyti šablonų",
"Ignore Permissions": "Nepaisyti failų prieigos leidimų",
@@ -164,6 +171,7 @@
"Listeners": "Klausytojai",
"Loading data...": "Įkeliami duomenys...",
"Loading...": "Įkeliama...",
"Local Additions": "Vietiniai pridėjimai",
"Local Discovery": "Vietinis matomumas",
"Local State": "Vietinė būsena",
"Local State (Total)": "Vietinė būsena (Bendrai)",
@@ -220,6 +228,7 @@
"Please wait": "Prašome palaukti",
"Prefix indicating that the file can be deleted if preventing directory removal": "Priešdelis, nurodantis, kad failas gali būti ištrintas tuo atveju, jei neleidžia šalinti katalogo",
"Prefix indicating that the pattern should be matched without case sensitivity": "Priešdelis, nurodantis, kad šablonas turėtų būti atitiktas neskiriant raidžių dydžio",
"Preparing to Sync": "Ruošiama sinchronizuoti",
"Preview": "Peržiūra",
"Preview Usage Report": "Naudojimo ataskaitos peržiūra",
"Quick guide to supported patterns": "Trumpas leistinų šablonų vadovas",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Palaikomiems šabloniniams komandų eilutės parametrams, žiūrėkite išorinį versijų valdymo žinyną.",
"Select All": "Žymėti visus",
"Select a version": "Pasirinkti versiją",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Pasirinkti paskiausią versiją",
"Select oldest version": "Pasirinkti seniausią versiją",
"Select the devices to share this folder with.": "Pasirinkite įrenginius, su kuriais dalinsitės šį aplanką.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing yra laisva ir atvirojo kodo programinė įranga, licencijuota pagal MPL v2.0.",
"Syncthing is restarting.": "Syncthing perleidžiamas",
"Syncthing is upgrading.": "Syncthing atsinaujina.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Dabar, Syncthing palaiko ir automatiškai plėtotojams siunčia ataskaitas apie strigtis. Pagal numatymą, ši ypatybė yra įjungta.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing išjungta arba problemos su Interneto ryšių. Bandoma iš naujo...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Atrodo, kad Syncthing, vykdydamas jūsų užklausą, susidūrė su problemomis. Prašome iš naujo įkelti puslapį, arba jei problema išlieka, iš naujo paleisti Syncthing.",
"Take me back": "Sugrąžinkite mane",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Kelias negali būti tuščias.",
"The rate limit must be a non-negative number (0: no limit)": "Srauto maksimalus greitis privalo būti ne neigiamas skaičius (0: nėra apribojimo)",
"The rescan interval must be a non-negative number of seconds.": "Nuskaitymo dažnis negali būti neigiamas skaičius.",
"There are no devices to share this folder with.": "Nėra įrenginių su kuriais bendrinti šį aplanką.",
"They are retried automatically and will be synced when the error is resolved.": "Failus bus automatiškai bandoma parsiųsti dar kartą kai išspręsite klaidas.",
"This Device": "Šis įrenginys",
"This can easily give hackers access to read and change any files on your computer.": "Tai gali suteikti programišiams lengvą prieigą skaityti ir keisti bet kokius failus jūsų kompiuteryje.",
@@ -342,6 +354,7 @@
"Unignore": "Nustoti nepaisyti",
"Unknown": "Nežinoma",
"Unshared": "Nesidalinama",
"Unshared Devices": "Unshared Devices",
"Unused": "Nenaudojamas",
"Up to Date": "Atnaujinta",
"Updated": "Atnaujinta",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Er du sikker på at du ønsker å fjerne enheten {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Er du sikker på at du ønsker å fjerne mappen {{label}}?",
"Are you sure you want to restore {%count%} files?": "Er du sikker på at du ønsker å gjenopprette {{count}} filer?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Godta automatisk",
"Automatic Crash Reporting": "Automatisk krasjrapportering",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisk oppgradering lar deg nå få valget mellom ferdige utgaver og utgivelseskandidater.",
"Automatic upgrades": "Automatiske oppdateringer",
"Automatic upgrades are always enabled for candidate releases.": "Automatisk oppgradering er alltid påslått for utgivelseskandidater.",
@@ -58,19 +60,22 @@
"Copyright © 2014-2017 the following Contributors:": "Opphavsrett © 2014-2017 for følgende bidragsytere:",
"Copyright © 2014-2019 the following Contributors:": "Opphavrett © 2014-2019 for følgende bidragsytere:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Oppretter ignoreringsmønster, overskriver eksisterende fil i {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Fare!",
"Debugging Facilities": "Feilrettingsverktøy",
"Default Folder Path": "Forvalgt mappeplassering",
"Deleted": "Slettet",
"Deselect All": "Deselect All",
"Deselect All": "Fjern alle markeringer",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Enhet",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) ønsker å koble til. Legge til ny enhet?",
"Device ID": "Enhets-ID",
"Device Identification": "Enhetskjennemerke",
"Device Name": "Navn på enhet",
"Device rate limits": "Device rate limits",
"Device rate limits": "enhetshastighetsgrense",
"Device that last modified the item": "Enheten som sist endret elementet",
"Devices": "Enheter",
"Disable Crash Reporting": "Skru av krasjrapportering",
"Disabled": "Avskrudd",
"Disabled periodic scanning and disabled watching for changes": "Skrudde av både periodisk skanning og oppsyn med endringer",
"Disabled periodic scanning and enabled watching for changes": "Skrudde av periodisk skanning og skrudde på oppsyn med endringer",
@@ -92,6 +97,7 @@
"Edit Folder": "Rediger mappe",
"Editing": "Redigerer",
"Editing {%path%}.": "Redigerer {{path}}.",
"Enable Crash Reporting": "Skru på krasjrapportering",
"Enable NAT traversal": "Slå på NAT-traversering",
"Enable Relaying": "Aktiver reléforsendelse",
"Enabled": "Påskrudd",
@@ -114,7 +120,7 @@
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Filer flyttes til en datostemplet versjon i .stversions-mappa når den oppdateres eller slettes av Syncthing.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Filer flyttes til en datostemplet versjon i .stversions-mappa når den oppdateres eller slettes av Syncthing.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskyttet mot endringer som er gjort på andre enheter, men endringer som er gjort på denne enheten blir sendt til resten av gruppen.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Filer er synkronisert fra klyngen, men lokale endringer vil ikke bli sent til andre enheter.",
"Filesystem Notifications": "Filsystemvarsler ",
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
"Filter by date": "Filtrer etter dato",
@@ -141,12 +147,13 @@
"Global State": "Global tilstand",
"Help": "Hjelp",
"Home page": "Hjemmeside",
"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": "Ignorer",
"Ignore Patterns": "Utelatelsesmønster",
"Ignore Permissions": "Ignorer rettigheter",
"Ignored Devices": "Ignored Devices",
"Ignored Devices": "Ignorerte enheter",
"Ignored Folders": "Utelatte mapper",
"Ignored at": "Ignored at",
"Ignored at": "Ignorert i",
"Incoming Rate Limit (KiB/s)": "Innkommende hastighetsbegrensning (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Feilaktige innstillinger kan skade innholdet i dine delte mapper og hindre Syncthing i å fungere.",
"Introduced By": "Introdusert av",
@@ -160,14 +167,15 @@
"Later": "Senere",
"Latest Change": "Sist endret",
"Learn more": "Lær mer",
"Limit": "Limit",
"Limit": "Grense",
"Listeners": "Lyttere",
"Loading data...": "Laster inn data…",
"Loading...": "Laster…",
"Local Additions": "Local Additions",
"Local Discovery": "Lokalt oppslag",
"Local State": "Lokal tilstand",
"Local State (Total)": "Lokal tilstand (total)",
"Locally Changed Items": "Locally Changed Items",
"Locally Changed Items": "Lokalt endrede elementer",
"Log": "Logg",
"Log tailing paused. Click here to continue.": "Tail-utdata pauset. Klikk her for å fortsette.",
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
@@ -210,7 +218,7 @@
"Pause": "Oppholde",
"Pause All": "Sett alt på pause",
"Paused": "Oppholdt",
"Pending changes": "Pending changes",
"Pending changes": "Påventende endringer",
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning på gitte intervaller og avskrudd oppsyn med endringer",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning på gitte intervall og påskrudd oppsyn med endringer",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning på gitte intervall og mislyktes i å sette opp oppsyn med endringer, prøver igjen hvert minutt:",
@@ -220,12 +228,13 @@
"Please wait": "Vent",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefiks som indikerer at fila kan slettes hvis den forhindrer fjerning av mappe",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefiks som indikerer at mønsteret skal samsvare uten versalsensitivitet",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Forhåndsvisning",
"Preview Usage Report": "Forhåndsvisning av datainnsamling",
"Quick guide to supported patterns": "Kjapp innføring i godkjente mønstre",
"RAM Utilization": "RAM-utnyttelse",
"Random": "Tilfeldig",
"Receive Only": "Receive Only",
"Receive Only": "Bare motta",
"Recent Changes": "Nylige endringer",
"Reduced by ignore patterns": "Reduser med utelatelsesmønster",
"Release Notes": "Utgivelsesnotat",
@@ -247,15 +256,16 @@
"Resume": "Gjenoppta",
"Resume All": "Gjenoppta alt",
"Reused": "Gjenbrukt",
"Revert Local Changes": "Revert Local Changes",
"Revert Local Changes": "Tilbakestill lokale endringer",
"Running": "Kjører",
"Save": "Lagre",
"Scan Time Remaining": "Gjenstående tid for gjennomsøking",
"Scanning": "Gjennomsøker",
"See external versioner help for supported templated command line parameters.": "Se ekstern versjoneringshjelp for støttede mal-baserte kommandolinjeparameter.",
"See external versioning help for supported templated command line parameters.": "Se ekstern versjoneringshjelp for støttede mal-baserte kommandolinjeparameter.",
"Select All": "Select All",
"Select All": "Velg alle",
"Select a version": "Velg en versjon",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Velg siste versjon",
"Select oldest version": "Velg eldste versjon",
"Select the devices to share this folder with.": "Velg enhetene du vil dele denne mappen med.",
@@ -269,7 +279,7 @@
"Share With Devices": "Del med enheter",
"Share this folder?": "Del denne mappa?",
"Shared With": "Delt med",
"Sharing": "Sharing",
"Sharing": "Deling",
"Show ID": "Vis ID",
"Show QR": "Vis QR-kode",
"Show diff with previous version": "Vis diff med forrige version",
@@ -291,17 +301,18 @@
"Statistics": "Statistikk",
"Stopped": "Stoppet",
"Support": "Brukerstøtte",
"Support Bundle": "Support Bundle",
"Support Bundle": "Støttebunt",
"Sync Protocol Listen Addresses": "Lytteadresse for synkroniseringsprotokoll",
"Syncing": "Synkroniserer",
"Syncthing has been shut down.": "Syncthing har blitt slått av.",
"Syncthing includes the following software or portions thereof:": "Syncthing inkluderer helt eller delvis følgende programvare:",
"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 Free and Open Source Software licensed as MPL v2.0.": "Syncthing er fri programvare med MPL v2.0-lisens.",
"Syncthing is restarting.": "Syncthing starter på ny.",
"Syncthing is upgrading.": "Syncthing oppgraderer.",
"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 ser ut til å være nede, eller så er det et problem med nettforbindelsen din. Prøver på ny …",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ser ut til å ha støtt på et problem under behandling av din forespørsel. Gjenoppfrisk nettleseren eller start Syncthing på nytt dersom problemet vedvarer.",
"Take me back": "Take me back",
"Take me back": "Gå tilbake",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"The Syncthing admin interface is configured to allow remote access without a password.": "Grensesnittet for administrering av Syncthing er satt til å tillate ekstern tilgang uten et passord.",
"The aggregated statistics are publicly available at the URL below.": "Innsamlet statistikk er åpent tilgjengelig via nettadressen angitt nedenfor.",
@@ -316,7 +327,7 @@
"The folder path cannot be blank.": "Mappeplasseringen kan ikke være tom.",
"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.": "Følgende intervall blir brukt: Den første timen blir en versjon lagret hvert 30. sekund, den første dagen blir en versjon lagret hver time, de første 30 dagene blir en versjon lagret hver dag, og inntil maksimal levetid blir en versjon lagret hver uke.",
"The following items could not be synchronized.": "Følgende filer kunne ikke synkroniseres.",
"The following items were changed locally.": "The following items were changed locally.",
"The following items were changed locally.": "Følgende elementer ble endret lokalt.",
"The maximum age must be a number and cannot be blank.": "Maksimal levetid må være et tall og kan ikke være tomt.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maksimal tid å beholde en versjon (i dager, sett til 0 for å beholde versjoner på ubegrenset tid).",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Nødvendig ledig diskplass må være et tall mellom 0 og 100.",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Plasseringen kan ikke være tom.",
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsbegrensningen kan ikke være et negativt tall (0: ingen begrensing)",
"The rescan interval must be a non-negative number of seconds.": "Antall sekund for intervallet kan ikke være negativt.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Disse hentes automatisk og vil synkroniseres når feilen er blitt utbedret.",
"This Device": "Denne enheten",
"This can easily give hackers access to read and change any files on your computer.": "Dette kan lett gi hackere tilgang til å lese og endre alle filer på datamaskinen din.",
@@ -339,9 +351,10 @@
"Unavailable": "Utilgjengelig",
"Unavailable/Disabled by administrator or maintainer": "Utilgjengelig/avskrudd av administrator eller vedlikeholder",
"Undecided (will prompt)": "Ikke bestemt (vil spørre)",
"Unignore": "Unignore",
"Unignore": "Ikke ignorer",
"Unknown": "Ukjent",
"Unshared": "Ikke delt",
"Unshared Devices": "Unshared Devices",
"Unused": "Ikke i bruk",
"Up to Date": "Oppdatert",
"Updated": "Oppdatert",
@@ -352,7 +365,7 @@
"Uptime": "Oppetid",
"Usage reporting is always enabled for candidate releases.": "Bruksrapportering er alltid påslått for utgivelseskandidater",
"Use HTTPS for GUI": "Bruk HTTPS for GUI",
"Use notifications from the filesystem to detect changed items.": "Use notifications from the filesystem to detect changed items.",
"Use notifications from the filesystem to detect changed items.": "Bruk varslinger fra filsystemet for å oppdage endrede elementer.",
"Variable Size Blocks": "Variable Size Blocks",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Variable size blocks (also \"large blocks\") are more efficient for large files.",
"Version": "Versjon",
@@ -374,8 +387,8 @@
"You can also select one of these nearby devices:": "Du kan også velge en av disse enhetene i nærheten:",
"You can change your choice at any time in the Settings dialog.": "Du kan endre ditt valg når som helst i innstillingene.",
"You can read more about the two release channels at the link below.": "Du kan lese mer om de to nye utgivelseskanalene i lenken nedenfor.",
"You have no ignored devices.": "You have no ignored devices.",
"You have no ignored folders.": "You have no ignored folders.",
"You have no ignored devices.": "Du har ingen ignorerte enheter.",
"You have no ignored folders.": "Du har ingen ignorerte mapper.",
"You have unsaved changes. Do you really want to discard them?": "You have unsaved changes. Do you really want to discard them?",
"You must keep at least one version.": "Du må beholde minst én versjon",
"days": "dager",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Weet u zeker dat u apparaat {{name}} wilt verwijderen?",
"Are you sure you want to remove folder {%label%}?": "Weet u zeker dat u map {{label}} wilt verwijderen?",
"Are you sure you want to restore {%count%} files?": "Weet u zeker dat u {{count}} bestanden wilt herstellen?",
"Are you sure you want to upgrade?": "Weet u zeker dat u wilt upgraden?",
"Auto Accept": "Automatisch aanvaarden",
"Automatic Crash Reporting": "Automatische crashrapportage",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisch bijwerken biedt nu de keuze tussen stabiele releases en release canditates.",
"Automatic upgrades": "Automatische upgrades",
"Automatic upgrades are always enabled for candidate releases.": "Automatische upgrades zijn altijd ingeschakeld voor kandidaat-releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Auteursrecht © 2014-2017 voor de volgende bijdragers:",
"Copyright © 2014-2019 the following Contributors:": "Auteursrecht © 2014-2019 voor de volgende bijdragers:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Negeerpatronen worden aangemaakt, bestaand bestand wordt overschreven op {{path}}.",
"Currently Shared With Devices": "Momenteel gedeeld met apparaten",
"Danger!": "Let op!",
"Debugging Facilities": "Debugmogelijkheden",
"Default Folder Path": "Standaardmaplocatie",
"Deleted": "Verwijderd",
"Deselect All": "Alles deselecteren",
"Deselect devices to stop sharing this folder with.": "Deselecteer apparaten om er deze map niet meer mee te delen.",
"Device": "Apparaat",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" ({{device}} op {{address}}) wil verbinden. Nieuw apparaat toevoegen?",
"Device ID": "Apparaat-ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Snelheidsbegrenzingen apparaat",
"Device that last modified the item": "Apparaat dat het item laatst gewijzigd heeft",
"Devices": "Apparaten",
"Disable Crash Reporting": "Crashrapportage uitschakelen",
"Disabled": "Uitgeschakeld",
"Disabled periodic scanning and disabled watching for changes": "Periodiek scannen uitgeschakeld en opvolgen van wijzigingen uitgeschakeld",
"Disabled periodic scanning and enabled watching for changes": "Periodiek scannen uitgeschakeld en opvolgen van wijzigingen ingeschakeld",
@@ -92,6 +97,7 @@
"Edit Folder": "Map bewerken",
"Editing": "Bewerken",
"Editing {%path%}.": "{{path}} bewerken.",
"Enable Crash Reporting": "Crashrapportage inschakelen",
"Enable NAT traversal": "NAT traversal inschakelen",
"Enable Relaying": "Relaying inschakelen",
"Enabled": "Ingeschakeld",
@@ -141,6 +147,7 @@
"Global State": "Globale status",
"Help": "Help",
"Home page": "Startpagina",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Uw huidige instellingen geven echter aan dat u het misschien niet wilt inschakelen. We hebben de automatische crashrapportage voor u uitgeschakeld.",
"Ignore": "Negeren",
"Ignore Patterns": "Negeerpatronen",
"Ignore Permissions": "Machtigingen negeren",
@@ -164,6 +171,7 @@
"Listeners": "Luisteraars",
"Loading data...": "Gegevens laden...",
"Loading...": "Laden...",
"Local Additions": "Lokale toevoegingen",
"Local Discovery": "Lokale ontdekking",
"Local State": "Lokale status",
"Local State (Total)": "Lokale status (totaal)",
@@ -220,6 +228,7 @@
"Please wait": "Even geduld",
"Prefix indicating that the file can be deleted if preventing directory removal": "Voorvoegsel dat aangeeft dat het bestand kan verwijderd worden als het bestand het verwijderen van een map voorkomt",
"Prefix indicating that the pattern should be matched without case sensitivity": "Voorvoegsel dat aangeeft dat het patroon niet hoofdlettergevoelig moet overeenkomen",
"Preparing to Sync": "Synchronisatie voorbereiden",
"Preview": "Voorbeeld",
"Preview Usage Report": "Voorbeeld van gebruiksrapport",
"Quick guide to supported patterns": "Snelgids voor ondersteunde patronen",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Bekijk de documentatie van extern versiebeheer voor ondersteunde sjabloon-opdrachtregelparameters.",
"Select All": "Alles selecteren",
"Select a version": "Selecteer een versie",
"Select additional devices to share this folder with.": "Selecteer extra apparaten om deze map mee te delen.",
"Select latest version": "Laatste versie selecteren",
"Select oldest version": "Oudste versie selecteren",
"Select the devices to share this folder with.": "Selecteer de apparaten om deze map mee te delen.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is gratis en opensource software onder licentie van MPL v2.0.",
"Syncthing is restarting.": "Syncthing is aan het herstarten.",
"Syncthing is upgrading.": "Syncthing is aan het bijwerken.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ondersteunt nu automatisch rapporteren van crashes naar de ontwikkelaars. De functie is standaard ingeschakeld.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt gestopt te zijn, of er is een probleem met uw internetverbinding. Opnieuw proberen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing lijkt een probleem te ondervinden met het verwerken van uw verzoek. Vernieuw de pagina of start Syncthing opnieuw als het probleem zich blijft voordoen. ",
"Take me back": "Neem me terug",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Het pad mag niet leeg zijn.",
"The rate limit must be a non-negative number (0: no limit)": "De snelheidsbegrenzing moet een positief nummer zijn (0: geen begrenzing)",
"The rescan interval must be a non-negative number of seconds.": "Het interval voor opnieuw scannen moet een positief getal in seconden zijn.",
"There are no devices to share this folder with.": "Er zijn geen apparaten om deze map mee te delen.",
"They are retried automatically and will be synced when the error is resolved.": "Ze worden automatisch opnieuw geprobeerd en zullen gesynchroniseerd worden wanneer de fout opgelost is.",
"This Device": "Dit apparaat",
"This can easily give hackers access to read and change any files on your computer.": "Dit kan hackers eenvoudig toegang geven om bestanden op uw computer te lezen en te wijzigen.",
@@ -342,6 +354,7 @@
"Unignore": "Niet negeren",
"Unknown": "Onbekend",
"Unshared": "Niet gedeeld",
"Unshared Devices": "Niet-gedeelde apparaten",
"Unused": "Niet gebruikt",
"Up to Date": "Bijgewerkt",
"Updated": "Bijgewerkt",
@@ -383,6 +396,6 @@
"files": "bestanden",
"full documentation": "volledige documentatie",
"items": "items",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil de map \"{{folder}}\" delen.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wil de map \"{{folderlabel}}\" ({{folder}}) delen."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil map \"{{folder}}\" delen.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wil map \"{{folderlabel}}\" ({{folder}}) delen."
}

View File

@@ -31,10 +31,12 @@
"Are you sure you want to remove device {%name%}?": "Czy na pewno chcesz usunąć urządzenie {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Czy na pewno chcesz usunąć folder {{label}}?",
"Are you sure you want to restore {%count%} files?": "Czy na pewno chcesz przywrócić {{count}} plików?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Autoakceptacja",
"Automatic Crash Reporting": "Automatyczne raportowanie awarii",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatyczne aktualizacje pozwalają teraz wybrać pomiędzy wydaniami stabilnymi a wersjami kandydującymi.",
"Automatic upgrades": "Automatyczne aktualizacje",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
"Automatic upgrades are always enabled for candidate releases.": "Aktualizacje automatyczne są zawsze włączone dla wydań kandydujących programu",
"Automatically create or share folders that this device advertises at the default path.": "Automatycznie utwórz lub udostępniaj katalogi udostępniane przez te urządzenie w domyślnej ścieżce",
"Available debug logging facilities:": "Dostępne narzędzia logowania debugowego",
"Be careful!": "Uważaj!",
@@ -56,13 +58,15 @@
"Copied from original": "Skopiowane z oryginału",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016: ",
"Copyright © 2014-2017 the following Contributors:": "Prawa autorskie © 2014-2017 dla następujących autorów:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Copyright © 2014-2019 the following Contributors:": "Wszelkie prawa zastrzeżone © 2014-2019 dla twórców:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Ustawienie wzorów ignorowania, nadpisze istniejący plik w {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Niebezpieczne!",
"Debugging Facilities": "Odpluskwianie",
"Default Folder Path": "Domyślna ścieżka folderu",
"Deleted": "Usunięto",
"Deselect All": "Odznacz wszystko",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Urządzenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Urządzenie \"{{name}}\" {{device}} ({{address}}) chce się połączyć. Dodać nowe urządzenie?",
"Device ID": "ID urządzenia",
@@ -71,6 +75,7 @@
"Device rate limits": "Limity transferu urządzenia",
"Device that last modified the item": "Urządzenie, które jako ostatnie zmodyfikowało element",
"Devices": "Urządzenia",
"Disable Crash Reporting": "Wyłącz raportowanie błędów",
"Disabled": "Wyłączone",
"Disabled periodic scanning and disabled watching for changes": "Wyłączono okresowe skanowanie i wyłączono obserwowanie zmian",
"Disabled periodic scanning and enabled watching for changes": "Wyłączono okresowe skanowanie i włączono obserwowanie zmian",
@@ -92,6 +97,7 @@
"Edit Folder": "Edytuj folder",
"Editing": "Edytowanie",
"Editing {%path%}.": "Edytowanie {{path}}.",
"Enable Crash Reporting": "Włącz raportowanie błędów",
"Enable NAT traversal": "Włącz trawersowanie NAT",
"Enable Relaying": "Włącz przekazywanie",
"Enabled": "Włączone",
@@ -141,6 +147,7 @@
"Global State": "Status globalny",
"Help": "Pomoc",
"Home page": "Strona domowa",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Twoje aktualne ustawienia wskazują iż być może nie chciał(a)byś wysyłać raportów. Wyłączyliśmy automatyczne raportowanie awarii.",
"Ignore": "Ignoruj",
"Ignore Patterns": "Wzorce ignorowania",
"Ignore Permissions": "Ignoruj uprawnienia",
@@ -164,6 +171,7 @@
"Listeners": "Nasłuchujący",
"Loading data...": "Ładowanie danych...",
"Loading...": "Ładowanie...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokalne odnajdywanie",
"Local State": "Status lokalny",
"Local State (Total)": "Status lokalny (suma)",
@@ -220,6 +228,7 @@
"Please wait": "Proszę czekać",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefiks wskazujący, że plik może zostać usunięty w przypadku zapobiegania usunięciu katalogu",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefiks wskazujący, że wzorzec powinien być dopasowany bez rozróżniania wielkości liter",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Podgląd",
"Preview Usage Report": "Podgląd raportu użycia.",
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Dostępne zmienne dla polecenia opisane są w dokumentacji w sekcji Zewnętrzne wersjonowanie plików.",
"Select All": "Zaznacz wszystko",
"Select a version": "Wybierz wersję",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Wybierz najnowszą wersję",
"Select oldest version": "Wybierz najstarszą wersję",
"Select the devices to share this folder with.": "Wybierz urządzenie, któremu udostępnić folder.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing to wolne oprogramowanie na licencji MPL 2.0",
"Syncthing is restarting.": "Restart Syncthing",
"Syncthing is upgrading.": "Aktualizowanie Syncthing",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing zawiera teraz automatyczne wysyłanie raportów o awariach do autorów. Ta funkcja jest domyślnie włączona.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing wydaje się być wyłączony lub jest problem z twoim połączeniem internetowym. Próbuje ponownie...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing nie może przetworzyć twojego zapytania. Proszę przeładuj stronę lub zrestartuj Syncthing, jeśli problem pozostanie.",
"Take me back": "Zabierz mnie z powrotem",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Ścieżka nie może być pusta.",
"The rate limit must be a non-negative number (0: no limit)": "Ograniczenie prędkości powinno być nieujemną liczbą całkowitą (0: brak ograniczeń)",
"The rescan interval must be a non-negative number of seconds.": "Interwał skanowania musi być niezerową liczbą sekund.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Ponowne próby zachodzą automatycznie, synchronizacja nastąpi po usunięciu usterki.",
"This Device": "To urządzenie",
"This can easily give hackers access to read and change any files on your computer.": "Może to umożliwić osobom trzecim dostęp do odczytu i zmian dowolnych plików na urządzeniu.",
@@ -342,6 +354,7 @@
"Unignore": "Wyłącz ignorowanie",
"Unknown": "Nieznany",
"Unshared": "Nieudostępnione",
"Unshared Devices": "Unshared Devices",
"Unused": "Nieużywane",
"Up to Date": "Aktualny",
"Updated": "Zaktualizowano",
@@ -352,9 +365,9 @@
"Uptime": "Czas działania",
"Usage reporting is always enabled for candidate releases.": "Raportowanie użycia dla wydań kandydujących jest zawsze włączone.",
"Use HTTPS for GUI": "Używaj HTTPS",
"Use notifications from the filesystem to detect changed items.": "Use notifications from the filesystem to detect changed items.",
"Variable Size Blocks": "Bloki różnych wielkości",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Variable size blocks (also \"large blocks\") are more efficient for large files.",
"Use notifications from the filesystem to detect changed items.": "Używaj mechanizmów systemu plików do wykrywania zmienionych elementów.",
"Variable Size Blocks": "Bloki o zmiennym rozmiarze",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Bloki o zmiennym rozmiarze (nazywane również \"dużymi blokami\") są bardziej wydajne przy przesyłaniu dużych plików.",
"Version": "Wersja",
"Versions": "Wersje",
"Versions Path": "Ścieżka wersji",
@@ -367,7 +380,7 @@
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Uwaga: Jeśli korzystasz z zewnętrznego obserwatora takiego jak {{syncthingInotify}}, upewnij się, że jest on dezaktywowany.",
"Watch for Changes": "Obserwuj zmiany",
"Watching for Changes": "Obserwowanie zmian",
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
"Watching for changes discovers most changes without periodic scanning.": "Obserwowanie wykrywa większość zmian bez potrzeby okresowego skanowania.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Gdy dodajesz nowe urządzenie, pamiętaj że urządzenie musi zostać dodane także po drugiej stronie.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Przy dodawaniu nowego folderu, pamiętaj, że ID użyte jest do łączenia folderów pomiędzy urządzeniami. Wielkość liter ciągu ma znaczenie musi zgadzać się na wszystkich urządzeniach.",
"Yes": "Tak",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Tem certeza de que deseja remover o dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Tem certeza de que deseja remover a pasta {{name}}?",
"Are you sure you want to restore {%count%} files?": "Tem certeza de que deseja restaurar {{count}} arquivo(s)?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Aceitar automaticamente",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "A atualização automática agora oferece a escolha entre versões estáveis e candidatas ao lançamento.",
"Automatic upgrades": "Atualizações automáticas",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 dos seguintes Colaboradores:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Criando filtros, sobrescrevendo o arquivo {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Perigo!",
"Debugging Facilities": "Facilidades de depuração",
"Default Folder Path": "Caminho padrão da pasta",
"Deleted": "Apagado",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Dispositivo \"{{name}}\" ({{device}} em {{address}}) quer se conectar. Adicionar novo dispositivo?",
"Device ID": "ID do dispositivo",
@@ -71,6 +75,7 @@
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Dispositivo que modificou o item pela última vez",
"Devices": "Dispositivos",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Desabilitado",
"Disabled periodic scanning and disabled watching for changes": "Verificação periódica e verificação automática de mudanças desabilitadas",
"Disabled periodic scanning and enabled watching for changes": "Verificação periódica desabilitada. Verificação automática de mudanças habilitada.",
@@ -92,6 +97,7 @@
"Edit Folder": "Editar pasta",
"Editing": "Editando",
"Editing {%path%}.": "Editando {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Habilitar NAT",
"Enable Relaying": "Habilitar retransmissão",
"Enabled": "Habilitado",
@@ -141,6 +147,7 @@
"Global State": "Estado global",
"Help": "Ajuda",
"Home page": "Página inicial",
"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": "Ignorar",
"Ignore Patterns": "Filtros",
"Ignore Permissions": "Ignorar permissões",
@@ -164,6 +171,7 @@
"Listeners": "Escutadores",
"Loading data...": "Carregando dados...",
"Loading...": "Carregando",
"Local Additions": "Local Additions",
"Local Discovery": "Descoberta local",
"Local State": "Estado local",
"Local State (Total)": "Estado local (total)",
@@ -220,6 +228,7 @@
"Please wait": "Aguarde",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefixo indicando que o arquivo pode ser removido caso esteja impedindo a remoção do seu diretório",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefixo indicando que o filtro deve ser igualado sem distinção entre maiúsculas e minúsculas",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Visualizar",
"Preview Usage Report": "Visualizar relatório de uso",
"Quick guide to supported patterns": "Guia rápido dos padrões suportados",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Consulte a ajuda sobre versionamento externo para modelos de parâmetros de linha de comando aceitos.",
"Select All": "Select All",
"Select a version": "Selecione uma versão",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Escolher a última versão",
"Select oldest version": "Escolher a versão mais antiga",
"Select the devices to share this folder with.": "Selecione os dispositivos com os quais esta pasta será compartilhada.",
@@ -299,6 +309,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.": "O Syncthing está sendo reiniciado.",
"Syncthing is upgrading.": "O Syncthing está sendo atualizado.",
"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…": "Parece que o Syncthing está desligado ou há um problema com a sua conexão de internet. Tentando novamente...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Parece que o Syncthing está tendo problemas no processamento da requisição. Por favor, atualize a página ou reinicie o Syncthing caso o problema persista.",
"Take me back": "Take me back",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "O caminho não pode ficar vazio.",
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade deve ser um número positivo (0: sem limite)",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações deve ser um número positivo de segundos.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "Serão tentadas automaticamente e sincronizadas após o erro ter sido resolvido.",
"This Device": "Este dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Isto pode dar a hackers poder de leitura e escrita de qualquer arquivo em seu dispositivo.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Desconhecida",
"Unshared": "Não compartilhada",
"Unshared Devices": "Unshared Devices",
"Unused": "Não utilizado",
"Up to Date": "Em sincronia",
"Updated": "Atualizado",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Tem a certeza que quer remover o dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Tem a certeza que quer remover a pasta {{label}}?",
"Are you sure you want to restore {%count%} files?": "Tem a certeza que quer restaurar {{count}} ficheiros?",
"Are you sure you want to upgrade?": "Tem a certeza que quer actualizar?",
"Auto Accept": "Aceitar automaticamente",
"Automatic Crash Reporting": "Relatório Automático de Estouro",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "A actualização automática agora oferece a escolha entre versões estáveis e candidatas a lançamento.",
"Automatic upgrades": "Actualizações automáticas",
"Automatic upgrades are always enabled for candidate releases.": "As actualizações automáticas estão sempre activadas nas versões candidatas a lançamento.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 dos seguintes contribuidores:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 dos seguintes contribuidores:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Criando padrões de exclusão, sobrescrevendo um ficheiro existente em {{path}}.",
"Currently Shared With Devices": "Dispositivos com os quais está partilhada",
"Danger!": "Perigo!",
"Debugging Facilities": "Recursos de depuração",
"Default Folder Path": "Caminho da pasta predefinida",
"Deleted": "Eliminado",
"Deselect All": "Desseleccionar tudo",
"Deselect All": "Retirar todas as selecções",
"Deselect devices to stop sharing this folder with.": "Retire a selecção para deixar de partilhar a pasta com esses dispositivos.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "O dispositivo \"{{name}}\" ({{device}} em {{address}}) quer conectar-se. Adiciono este novo dispositivo?",
"Device ID": "ID do dispositivo",
@@ -71,6 +75,7 @@
"Device rate limits": "Limites de velocidade do dispositivo",
"Device that last modified the item": "Último dispositivo a modificar o item",
"Devices": "Dispositivos",
"Disable Crash Reporting": "Desactivar Relatório de Estouro",
"Disabled": "Desactivada",
"Disabled periodic scanning and disabled watching for changes": "Desactivada a verificação periódica e desactivada a vigilância de alterações",
"Disabled periodic scanning and enabled watching for changes": "Desactivada a verificação periódica e desactivada a vigilância de alterações",
@@ -92,6 +97,7 @@
"Edit Folder": "Editar pasta",
"Editing": "Editando",
"Editing {%path%}.": "Editando {{path}}.",
"Enable Crash Reporting": "Activar Relatório de Estouro",
"Enable NAT traversal": "Activar travessia de NAT",
"Enable Relaying": "Permitir retransmissão",
"Enabled": "Activada",
@@ -112,7 +118,7 @@
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Os ficheiros são movidos para a pasta .stversions ao serem substituídos ou eliminados pelo Syncthing.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Os ficheiros são movidos para a pasta .stversions quando substituídos ou eliminados pelo Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Os ficheiros são movidos para versões marcadas com data e hora numa pasta .stversions, ao serem substituídos ou eliminados pelo Syncthing.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Os ficheiros são movidos para versões carimbadas com o tempo numa pasta .stversions, ao serem substituídos ou apagados pelo Syncthing.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Os ficheiros são movidos para versões marcadas com data e hora numa pasta .stversions, ao serem substituídos ou apagados pelo Syncthing.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Os ficheiros estão protegidos contra alterações feitas noutros dispositivos, mas alterações feitas neste dispositivo serão enviadas ao resto do grupo.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Os ficheiros são sincronizados a partir do agrupamento, mas as alterações feitas localmente não serão enviadas para outros dispositivos.",
"Filesystem Notifications": "Notificações do sistema de ficheiros",
@@ -141,6 +147,7 @@
"Global State": "Estado global",
"Help": "Ajuda",
"Home page": "Página do projecto",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Contudo, a sua configuração actual indica que pode não a querer activada. Nós desactivámos automaticamente o relatório de estouro para si.",
"Ignore": "Ignorar",
"Ignore Patterns": "Padrões de exclusão",
"Ignore Permissions": "Ignorar permissões",
@@ -164,6 +171,7 @@
"Listeners": "Auscultadores",
"Loading data...": "Carregando dados...",
"Loading...": "Carregando...",
"Local Additions": "Adições locais",
"Local Discovery": "Pesquisa local",
"Local State": "Estado local",
"Local State (Total)": "Estado local (total)",
@@ -220,6 +228,7 @@
"Please wait": "Aguarde",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefixo para indicar que o ficheiro pode ser eliminado se estiver a impedir a eliminação da pasta",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefixo para indicar que o padrão não diferencia entre maiúsculas e minúsculas",
"Preparing to Sync": "Preparando para sincronizar",
"Preview": "Previsão",
"Preview Usage Report": "Pré-visualizar relatório de utilização",
"Quick guide to supported patterns": "Guia rápido dos padrões suportados",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Veja a ajuda externa sobre gestão de versões para ver os modelos suportados de parâmetros para a linha de comandos.",
"Select All": "Seleccionar tudo",
"Select a version": "Seleccione uma versão",
"Select additional devices to share this folder with.": "Seleccione outros dispositivos com os quais também pretende partilhar a pasta.",
"Select latest version": "Seleccionar a última versão",
"Select oldest version": "Seleccionar a versão mais antiga",
"Select the devices to share this folder with.": "Seleccione os dispositivos com os quais vai partilhar esta pasta.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing é Software Livre e de Código Aberto licenciado como MPL v2.0.",
"Syncthing is restarting.": "O Syncthing está a reiniciar.",
"Syncthing is upgrading.": "O Syncthing está a actualizar-se.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "O Syncthing agora suporta o envio automático de relatórios de estouros para os programadores. Esta funcionalidade vem inicialmente activada.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "O Syncthing parece estar em baixo, ou então existe um problema com a sua ligação à Internet. Tentando novamente...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "O Syncthing parece estar com problemas em processar o seu pedido. Tente recarregar a página ou reiniciar o Syncthing, se o problema persistir.",
"Take me back": "Voltar atrás",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "O caminho não pode estar vazio.",
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade tem que ser um número que não seja negativo (0: sem limite)",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações tem que ser um valor não negativo de segundos.",
"There are no devices to share this folder with.": "Não existem quaisquer dispositivos com os quais se possa partilhar esta pasta.",
"They are retried automatically and will be synced when the error is resolved.": "Será tentado automaticamente e os itens serão sincronizados assim que o erro seja resolvido.",
"This Device": "Este dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Isso facilmente dará acesso aos piratas informáticos para lerem e modificarem quaisquer ficheiros no seu computador.",
@@ -342,6 +354,7 @@
"Unignore": "Deixar de ignorar",
"Unknown": "Desconhecido",
"Unshared": "Não partilhada",
"Unshared Devices": "Dispositivos sem partilhas",
"Unused": "Não utilizado",
"Up to Date": "Em sincronia",
"Updated": "Actualizado",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Вы уверены, что хотите удалить устройство {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Вы уверены, что хотите удалить папку {{label}}?",
"Are you sure you want to restore {%count%} files?": "Вы уверены, что хотите восстановить {{count}} файлов?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Автопринятие",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматическое обновление теперь предлагает выбор между стабильными выпусками и кандидатами в релизы.",
"Automatic upgrades": "Автообновление",
"Automatic upgrades are always enabled for candidate releases.": "Автоматическое обновление всегда включено для кандидатов в релизы.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Авторские права © 2014—2017 следующие участники:",
"Copyright © 2014-2019 the following Contributors:": "Авторские права © 20142019 принадлежат:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Создание шаблонов игнорирования, существующий файл {{path}} будет перезаписан.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Опасно!",
"Debugging Facilities": "Средства отладки",
"Default Folder Path": "Путь для папок",
"Deleted": "Удалено",
"Deselect All": "Снять выделение",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство «{{name}}» ({{device}} на {{address}}) хочет подключиться. Добавить новое устройство?",
"Device ID": "ID устройства",
@@ -71,6 +75,7 @@
"Device rate limits": "Ограничения скорости для устройства",
"Device that last modified the item": "Устройство, последним изменившее объект",
"Devices": "Устройства",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Отключено",
"Disabled periodic scanning and disabled watching for changes": "Периодическое сканирование и отслеживание изменений отключено",
"Disabled periodic scanning and enabled watching for changes": "Периодическое сканирование отключено, отслеживание изменений включено",
@@ -92,6 +97,7 @@
"Edit Folder": "Редактирование папки",
"Editing": "Редактирование",
"Editing {%path%}.": "Правка {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Включить NAT traversal",
"Enable Relaying": "Включить релеи",
"Enabled": "Включено",
@@ -141,6 +147,7 @@
"Global State": "Глобальное состояние",
"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.",
"Ignore": "Игнорировать",
"Ignore Patterns": "Шаблоны игнорирования",
"Ignore Permissions": "Игнорировать файловые права доступа",
@@ -164,6 +171,7 @@
"Listeners": "Прослушиватель",
"Loading data...": "Загрузка данных...",
"Loading...": "Загрузка...",
"Local Additions": "Local Additions",
"Local Discovery": "Локальное обнаружение",
"Local State": "Локальное состояние",
"Local State (Total)": "Локальное состояние (всего)",
@@ -220,6 +228,7 @@
"Please wait": "Пожалуйста, подождите",
"Prefix indicating that the file can be deleted if preventing directory removal": "Префикс указывает, что файл может быть удалён, если он мешает удалить папку",
"Prefix indicating that the pattern should be matched without case sensitivity": "Префикс указывает, что регистр букв (заглавные/строчные) игнорируется",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Предварительный просмотр",
"Preview Usage Report": "Посмотреть отчёт об использовании",
"Quick guide to supported patterns": "Краткое руководство по поддерживаемым шаблонам",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Поддерживаемые шаблонные параметры командной строки см. в документации сторонней программы контроля версий",
"Select All": "Выбрать все",
"Select a version": "Выберите версию",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Выбрать последнюю версию",
"Select oldest version": "Выбрать самую старую версию",
"Select the devices to share this folder with.": "Выберите устройства, для которых будет доступна эта папка.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing — свободное программное обеспечение с открытым кодом под лицензией MPL v2.0.",
"Syncthing is restarting.": "Перезапуск Syncthing.",
"Syncthing is upgrading.": "Обновление Syncthing.",
"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 не запущен или есть проблемы с подключением к Интернету. Переподключаюсь...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing столкнулся с проблемой при обработке Вашего запроса. Пожалуйста, обновите страницу или перезапустите Syncthing если проблема повторится.",
"Take me back": "Вернуться к редактированию",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Путь не может быть пустым.",
"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 devices to share this folder with.",
"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.": "Это может дать доступ хакерам для чтения и изменения любых файлов на вашем компьютере.",
@@ -342,6 +354,7 @@
"Unignore": "Не игнорировать",
"Unknown": "Неизвестно",
"Unshared": "Необщедоступно",
"Unshared Devices": "Unshared Devices",
"Unused": "Не используется",
"Up to Date": "В актуальном состоянии",
"Updated": "Обновлено",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Určite chcete odobrať zariadenie {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Určite chcete odobrať adresár {{label}}?",
"Are you sure you want to restore {%count%} files?": "Určite chcete obnoviť {{count}} súborov?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Automatické prijatie",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatická aktualizácia teraz ponúka voľbu medzi stabilnými vydaniami a kandidátmi na vydanie.",
"Automatic upgrades": "Automatické aktualizácie",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 následujúci prispivatelia:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 nasledujúci prispievatelia:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Vytváranie vzorov ignorovania, prepísanie existujúceho súboru v {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Pozor!",
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Predvolená adresárová cesta",
"Deleted": "Zmazané",
"Deselect All": "Odznačiť všetko",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Zariadenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zariadenie \"{{name}}\" ({{device}} na {{address}}) sa chce pripojiť. Pridať nové zariadenie?",
"Device ID": "ID zariadenia",
@@ -71,6 +75,7 @@
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Zariadenie, ktoré naposledy pozmenilo položku",
"Devices": "Zariadenia",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Odpojené",
"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 +97,7 @@
"Edit Folder": "Upraviť Adresár",
"Editing": "Úprava",
"Editing {%path%}.": "Úprava {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Povoliť priechod NAT",
"Enable Relaying": "Povoliť prenosové uzly",
"Enabled": "Povolené",
@@ -141,6 +147,7 @@
"Global State": "Globálny status",
"Help": "Pomoc",
"Home page": "Domovská stránka",
"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": "Ignorovať",
"Ignore Patterns": "Ignorované vzory",
"Ignore Permissions": "Ignorované práva",
@@ -164,6 +171,7 @@
"Listeners": "Načúvajúci",
"Loading data...": "Načítavanie údajov...",
"Loading...": "Načítavanie...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokálne vyhľadávanie",
"Local State": "Lokálny status",
"Local State (Total)": "Lokálny status (celkový)",
@@ -220,6 +228,7 @@
"Please wait": "Prosím čakajte",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix označujúci, že súbor môže byť odstránený, ak bráni odstráneniu adresára.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix označujúci, že vzory by mali ignorovať veľkosť písma.",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Náhľad",
"Preview Usage Report": "Náhľad záznamu o používaní",
"Quick guide to supported patterns": "Rýchly sprievodca podporovanými vzormi",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Vybrať všetko",
"Select a version": "Zvoliť verziu",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Zvoliť najnovšiu verziu",
"Select oldest version": "Zvoliť najstaršiu verziu",
"Select the devices to share this folder with.": "Vyberte zariadenia s ktorými chcete zdieľať tento adresár.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing je otvorený softvér s licenciou MPL v2.0.",
"Syncthing is restarting.": "Syncthing sa reštartuje.",
"Syncthing is upgrading.": "Syncthing sa aktualizuje.",
"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 se zdá byť nefunkčný, alebo je problém s internetovým pripojením. Opakujem...",
"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": "Späť",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Cesta nemôže byť prázdna.",
"The rate limit must be a non-negative number (0: no limit)": "Limit rýchlosti musí byť kladné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"This Device": "Toto zariadenie",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Neznáme",
"Unshared": "Nezdieľané",
"Unshared Devices": "Unshared Devices",
"Unused": "Nepoužité",
"Up to Date": "Aktuálne",
"Updated": "Aktualizované",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Är du säker på att du vill ta bort enheten {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Är du säker på att du vill ta bort mappen {{label}}?",
"Are you sure you want to restore {%count%} files?": "Är du säker på att du vill återställa {{count}} filer?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Acceptera automatiskt",
"Automatic Crash Reporting": "Automatisk kraschrapportering",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisk uppgradering erbjuder nu valet mellan stabila utgåvor och utgåvskandidater.",
"Automatic upgrades": "Automatiska uppgraderingar",
"Automatic upgrades are always enabled for candidate releases.": "Automatiska uppgraderingar är alltid aktiverade för kandidatutgåvor.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 följande bidragsgivare:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 följande bidragsgivare:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Skapa ignorera mönster, skriver över en existerande fil på {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Fara!",
"Debugging Facilities": "Felsökningsanläggningar",
"Default Folder Path": "Standard mappsökväg",
"Deleted": "Raderade",
"Deselect All": "Avmarkera alla",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Enhet",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) vill ansluta. Lägg till ny enhet?",
"Device ID": "Enhets-ID",
@@ -71,6 +75,7 @@
"Device rate limits": "Enhetshastighetsgränser",
"Device that last modified the item": "Enhet som senast ändrade objektet",
"Devices": "Enheter",
"Disable Crash Reporting": "Inaktivera kraschrapportering",
"Disabled": "Inaktiverad",
"Disabled periodic scanning and disabled watching for changes": "Inaktiverad periodisk uppdatering och inaktiverad spaning efter ändringar",
"Disabled periodic scanning and enabled watching for changes": "Inaktiverad periodisk uppdatering och aktiverad spaning efter ändringar",
@@ -92,6 +97,7 @@
"Edit Folder": "Redigera mapp",
"Editing": "Redigerar",
"Editing {%path%}.": "Redigerar {{path}}.",
"Enable Crash Reporting": "Aktivera kraschrapportering",
"Enable NAT traversal": "Aktivera NAT traversering",
"Enable Relaying": "Aktivera vidarebefordring",
"Enabled": "Aktiverad",
@@ -141,6 +147,7 @@
"Global State": "Globalt tillstånd",
"Help": "Hjälp",
"Home page": "Webbplats",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar visar dock att du kanske inte vill att den ska aktiveras. Vi har inaktiverat automatisk krasch rapportering för dig.",
"Ignore": "Ignorera",
"Ignore Patterns": "Ignorera mönster",
"Ignore Permissions": "Ignorera rättigheter",
@@ -164,6 +171,7 @@
"Listeners": "Lyssnare",
"Loading data...": "Laddar data...",
"Loading...": "Laddar...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokal annonsering",
"Local State": "Lokalt tillstånd",
"Local State (Total)": "Lokalt tillstånd (totalt)",
@@ -220,6 +228,7 @@
"Please wait": "Var god vänta",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix som indikerar att filen kan raderas om det förhindrar radering av katalog",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix som indikerar att mönstret ska matchas utan skiftlägeskänslighet",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Förhandsgranska",
"Preview Usage Report": "Förhandsgranska statistik",
"Quick guide to supported patterns": "Snabb handledning till mönster som stöds",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Se hjälp för extern version för stödda mallade kommandoradsparametrar.",
"Select All": "Markera alla",
"Select a version": "Välj en version",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Välj senaste versionen",
"Select oldest version": "Välj äldsta versionen",
"Select the devices to share this folder with.": "Ange enheterna som den här mappen ska delas med.",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing har fri och öppen källkod licensierad som MPL v2.0.",
"Syncthing is restarting.": "Syncthing startar om.",
"Syncthing is upgrading.": "Syncthing uppgraderas.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing stöder nu automatiskt kraschrapportering till utvecklarna. Den här funktionen är aktiverad som standard.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar avstängd eller så är det problem med din Internetanslutning. Försöker igen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem med behandlingen av din begäran. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
"Take me back": "Ta mig tillbaka",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Sökvägen kan inte vara tom.",
"The rate limit must be a non-negative number (0: no limit)": "Frekvensgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
"The rescan interval must be a non-negative number of seconds.": "Förnyelseintervallet måste vara ett positivt antal sekunder",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"They are retried automatically and will be synced when the error is resolved.": "De omprövas automatiskt och kommer att synkroniseras när felet är löst.",
"This Device": "Denna enhet",
"This can easily give hackers access to read and change any files on your computer.": "Detta kan lätt ge hackare tillgång till att läsa och ändra några filer på datorn.",
@@ -342,6 +354,7 @@
"Unignore": "Sluta ignorera",
"Unknown": "Okänd",
"Unshared": "Inte delad",
"Unshared Devices": "Unshared Devices",
"Unused": "Oanvänd",
"Up to Date": "Uppdaterad",
"Updated": "Uppdaterade",
@@ -353,8 +366,8 @@
"Usage reporting is always enabled for candidate releases.": "Användningsrapportering är alltid aktiverad för kandidatutgåvor.",
"Use HTTPS for GUI": "Använd HTTPS för gränssnittet",
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
"Variable Size Blocks": "Variable Size Blocks",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Variable size blocks (also \"large blocks\") are more efficient for large files.",
"Variable Size Blocks": "Varierande blockstorlek",
"Variable size blocks (also \"large blocks\") are more efficient for large files.": "Varierande blockstorlek (även \"stora block\") är mer effektiva för stora filer.",
"Version": "Version",
"Versions": "Versioner",
"Versions Path": "Sökväg för versioner",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "Чи ви впевнені в необхідності видалити пристрій {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Чи ви впевнені в необхідності видалити директорію {{label}}?",
"Are you sure you want to restore {%count%} files?": "Чи ви впевнені в необхідності відновити наступну к-сть файлів: {{count}} ?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Затверджувати автоматично пропоновані віддаленим пристроєм каталоги",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматиче оновлення зараз дозволяє обирати між стабільними випусками та реліз-кандидатами.",
"Automatic upgrades": "Автоматичні оновлення",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "© 2014-2017 Всі права застережено, вклад внесли:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Створення шаблонів винятків з перезаписом існуючого файлу {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Небезпечно!",
"Debugging Facilities": "Засоби відладки",
"Default Folder Path": "Шлях до директорії по замовчанню",
"Deleted": "Видалене",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "Пристрій",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Пристрій \"{{name}}\" ({{device}} за адресою {{address}}) намагається під’єднатися. Додати новий пристрій?",
"Device ID": "ID пристрою",
@@ -71,6 +75,7 @@
"Device rate limits": "Обмеження пристрою",
"Device that last modified the item": "Пристрій, що останнім змінив елемент",
"Devices": "Пристрої",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Вимкнено",
"Disabled periodic scanning and disabled watching for changes": "Відключено періодичне сканування та відключено відстеження змін",
"Disabled periodic scanning and enabled watching for changes": "Відключено періодичне сканування та увімкнене стеження за змінами",
@@ -92,6 +97,7 @@
"Edit Folder": "Налаштування директорії",
"Editing": "Редагування",
"Editing {%path%}.": "Редагування {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Увімкнути NAT traversal",
"Enable Relaying": "Увімкнути ретрансляцію (relaying)",
"Enabled": "Увімкнено",
@@ -141,6 +147,7 @@
"Global State": "Глобальний статус",
"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.",
"Ignore": "Ігнорувати",
"Ignore Patterns": "Шаблони винятків",
"Ignore Permissions": "Ігнорувати права доступу до файлів",
@@ -164,6 +171,7 @@
"Listeners": "Приймачі (TCP & Relay)",
"Loading data...": "Дані завантажуються...",
"Loading...": "Завантаження...",
"Local Additions": "Local Additions",
"Local Discovery": "Локальне виявлення (LAN)",
"Local State": "Локальний статус",
"Local State (Total)": "Локальний статус (загалом)",
@@ -220,6 +228,7 @@
"Please wait": "Будь ласка, зачекайте",
"Prefix indicating that the file can be deleted if preventing directory removal": "Префікс означає, що файл може бути видалений при запобіганні видаленню директорії",
"Prefix indicating that the pattern should be matched without case sensitivity": "Префікс означає, що шаблон має збігатися без чутливості до регістру",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Попередній перегляд",
"Preview Usage Report": "Попередній перегляд статистичного звіту",
"Quick guide to supported patterns": "Швидкий посібник по шаблонам, що підтримуються",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "Переглянути допомогу по зовнішньому версіонуванню для підтримуваних шаблонних параметрів командного рядка.",
"Select All": "Select All",
"Select a version": "Обрати версію",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "Обрати найновішу версію",
"Select oldest version": "Обрати найстарішу версію",
"Select the devices to share this folder with.": "Оберіть пристрої, які матимуть доступ до цієї директорії.",
@@ -299,6 +309,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 перезавантажується.",
"Syncthing is upgrading.": "Syncthing оновлюється.",
"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 закритий, або виникла проблема із Інтернет-з’єднанням. Проводиться повторна спроба з’єднання…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Схоже на те, що Syncthing стикнувся з проблемою оброблюючи ваш запит. Будь ласка перезавантажте сторінку в браузері або перезапустіть Syncthing.",
"Take me back": "Take me back",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "Шлях не може бути порожнім.",
"The rate limit must be a non-negative number (0: no limit)": "Швидкість має бути додатнім числом.",
"The rescan interval must be a non-negative number of seconds.": "Інтервал повторного сканування повинен бути неід’ємною величиною.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"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.": "Це легко може дати хакерам доступ до читання та зміни будь-яких файлів на вашому комп'ютері.",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "Невідомо",
"Unshared": "Не розповсюджується",
"Unshared Devices": "Unshared Devices",
"Unused": "Не використовується",
"Up to Date": "Актуальна версія",
"Updated": "Оновлено",

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": "关于",
@@ -12,7 +12,7 @@
"Add Remote Device": "添加远程设备",
"Add devices from the introducer to our device list, for mutually shared folders.": "将此新设备上拥有的“远程设备”都自动添加到您这边的“远程设备”列表中(如果它们跟您存在相同的文件夹的话)",
"Add new folder?": "添加新文件夹?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整重新扫描的间隔将增大(时间 60以新的默认 1 小时为例)。你也可以在选择“否”后手动配置每个文件的时间。",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整重新扫描的间隔将增大(时间 60以新的默认 1 小时为例)。你也可以在选择“否”后手动配置每个文件的时间。",
"Address": "地址",
"Addresses": "地址列表",
"Advanced": "高级",
@@ -23,18 +23,20 @@
"Allowed Networks": "允许的网络",
"Alphabetic": "字母顺序",
"An external command handles the versioning. It has to remove the file from the shared folder.": "使用外部命令接管版本控制。该命令必须自行从共享文件夹中删除该文件。",
"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.": "外部命令接管了版本控制。它需要从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
"An external command handles the versioning. It has to remove the file from the synced folder.": "外部命令接管了版本控制。它需要从同步文件夹中删除该文件。",
"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.": "外部命令接管了版本控制。该外部命令必须自行从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
"An external command handles the versioning. It has to remove the file from the synced folder.": "外部命令接管了版本控制。该外部命令必须自行从同步文件夹中删除该文件。",
"Anonymous Usage Reporting": "匿名使用报告",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情况的报告格式已经变更。是否要迁移到新的格式?",
"Any devices configured on an introducer device will be added to this device as well.": "在中介设备上添加的任何“远程设备”,也会被自动添加到本机的“远程设备”列表。",
"Are you sure you want to remove device {%name%}?": "您确定要移除设备 {{name}} 吗?",
"Are you sure you want to remove folder {%label%}?": "您确定要移除文件夹 {{label}} 吗?",
"Are you sure you want to restore {%count%} files?": "您确定要恢复这 {{count}} 个文件吗?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "自动接受",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自动升级现在提供了稳定版本和发布候选版之间的选择。",
"Automatic Crash Reporting": "自动发送崩溃报告",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自动升级现在提供了稳定版本和候选发布版的选项。",
"Automatic upgrades": "自动升级",
"Automatic upgrades are always enabled for candidate releases.": "候选版会一直启用自动升级。",
"Automatic upgrades are always enabled for candidate releases.": "候选发布版会一直启用自动升级。",
"Automatically create or share folders that this device advertises at the default path.": "自动地创建或共享这个设备在默认路径通告的文件夹。",
"Available debug logging facilities:": "可用的调试日志功能:",
"Be careful!": "小心!",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 以下贡献者:",
"Copyright © 2014-2019 the following Contributors:": "版权所有 © 2014-2019 以下贡献者:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "正在创建忽略模式,覆盖位于 {{path}} 的已有文件。",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "危险!",
"Debugging Facilities": "调试功能",
"Default Folder Path": "默认文件夹路径",
"Deleted": "已删除",
"Deselect All": "取消全选",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "设备",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "设备 \"{{name}}\"(位于 {{address}} 的 {{device}})请求连接。是否添加新设备?",
"Device ID": "设备 ID",
@@ -71,6 +75,7 @@
"Device rate limits": "设备速率限制",
"Device that last modified the item": "最近修改该项的设备",
"Devices": "设备",
"Disable Crash Reporting": "禁用自动发送崩溃报告",
"Disabled": "已禁用",
"Disabled periodic scanning and disabled watching for changes": "已禁用定期扫描和更改监视",
"Disabled periodic scanning and enabled watching for changes": "已禁用定期扫描并启用更改监视",
@@ -92,6 +97,7 @@
"Edit Folder": "编辑文件夹",
"Editing": "正在编辑",
"Editing {%path%}.": "正在编辑 {{path}}。",
"Enable Crash Reporting": "启用自动发送崩溃报告",
"Enable NAT traversal": "启用 NAT 遍历",
"Enable Relaying": "开启中继",
"Enabled": "已启用",
@@ -141,6 +147,7 @@
"Global State": "全局状态",
"Help": "帮助",
"Home page": "主页",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "我们已经为您关闭了自动崩溃报告发送功能,因为您当前的设置显示您可能并不想启用该功能。",
"Ignore": "忽略",
"Ignore Patterns": "忽略模式",
"Ignore Permissions": "忽略文件权限",
@@ -164,6 +171,7 @@
"Listeners": "侦听程序",
"Loading data...": "正在载入数据…",
"Loading...": "正在载入…",
"Local Additions": "Local Additions",
"Local Discovery": "本地发现",
"Local State": "本地状态",
"Local State (Total)": "本地状态汇总",
@@ -220,6 +228,7 @@
"Please wait": "请稍候",
"Prefix indicating that the file can be deleted if preventing directory removal": "表示如果删除了阻止目录则文件可被删除的前缀",
"Prefix indicating that the pattern should be matched without case sensitivity": "表示该模式匹配忽略了大小写差异的前缀",
"Preparing to Sync": "Preparing to Sync",
"Preview": "预览",
"Preview Usage Report": "预览使用报告",
"Quick guide to supported patterns": "支持的通配符的简单教程:",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "有关受支持的模板命令行参数,请参阅外部版本控制帮助。",
"Select All": "全选",
"Select a version": "选择版本",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "选择最新的版本",
"Select oldest version": "选择最旧的版本",
"Select the devices to share this folder with.": "选择将本文件夹共享给哪些设备。",
@@ -299,6 +309,7 @@
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 是个以 MPL v2.0 授权的免费开源软件。",
"Syncthing is restarting.": "Syncthing 正在重启。",
"Syncthing is upgrading.": "Syncthing 正在升级。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 现在已经支持将崩溃报告自动发送给开发者。该功能默认开启。",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing 似乎关闭了,或者您的网络连接存在故障。重试中…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing 在处理您的请求时似乎遇到了问题。如果问题持续,请刷新页面,或重启 Syncthing。",
"Take me back": "带我回去",
@@ -327,6 +338,7 @@
"The path cannot be blank.": "路径不能为空。",
"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 devices to share this folder with.",
"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.": "这会让骇客能够轻而易举地访问及修改您的文件。",
@@ -342,6 +354,7 @@
"Unignore": "解除忽略",
"Unknown": "未知",
"Unshared": "未共享",
"Unshared Devices": "Unshared Devices",
"Unused": "未使用",
"Up to Date": "同步完成",
"Updated": "已更新",

View File

@@ -31,7 +31,9 @@
"Are you sure you want to remove device {%name%}?": "確定要移除 {{name}} 裝置?",
"Are you sure you want to remove folder {%label%}?": "確定要移除 {{label}} 資料夾?",
"Are you sure you want to restore {%count%} files?": "確定想要還原 {{count}} 個檔案?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "自動接受",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動更新目前有穩定發行版及發行候選版可供選擇。",
"Automatic upgrades": "自動升級",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
@@ -58,11 +60,13 @@
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017 下列貢獻者:",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 下列貢獻者:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "建立忽略樣式,覆蓋已存在的 {{path}}。",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "危險!",
"Debugging Facilities": "除錯工具",
"Default Folder Path": "預設資料夾路徑",
"Deleted": "已刪除",
"Deselect All": "取消選取全部",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Device": "裝置",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "裝置 \"{{name}}\" ({{device}} 位於 {{address}}) 想要連線。要增加新裝置嗎?",
"Device ID": "裝置識別碼",
@@ -71,6 +75,7 @@
"Device rate limits": "裝置速率限制",
"Device that last modified the item": "前次修改裝置",
"Devices": "裝置",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "停用",
"Disabled periodic scanning and disabled watching for changes": "已停用定期掃描及觀察變動",
"Disabled periodic scanning and enabled watching for changes": "已停用定期掃描及啟用觀察變動",
@@ -92,6 +97,7 @@
"Edit Folder": "編輯資料夾",
"Editing": "正在編輯",
"Editing {%path%}.": "正在編輯 {{path}} 。",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "啟用 NAT 穿透",
"Enable Relaying": "啟用中繼",
"Enabled": "啟用",
@@ -141,6 +147,7 @@
"Global State": "全域狀態",
"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.",
"Ignore": "忽略",
"Ignore Patterns": "忽略樣式",
"Ignore Permissions": "忽略權限",
@@ -164,6 +171,7 @@
"Listeners": "監聽者",
"Loading data...": "正在載入資料...",
"Loading...": "正在載入...",
"Local Additions": "Local Additions",
"Local Discovery": "本機探索",
"Local State": "本機狀態",
"Local State (Total)": "本機狀態 (總結)",
@@ -220,6 +228,7 @@
"Please wait": "請稍後",
"Prefix indicating that the file can be deleted if preventing directory removal": "前綴表示當此檔案阻礙了資料夾刪除時,可一併刪除此檔",
"Prefix indicating that the pattern should be matched without case sensitivity": "前綴表示此樣式不區分大小寫",
"Preparing to Sync": "Preparing to Sync",
"Preview": "預覽",
"Preview Usage Report": "預覽使用資訊報告",
"Quick guide to supported patterns": "可支援樣式的快速指南",
@@ -256,6 +265,7 @@
"See external versioning help for supported templated command line parameters.": "查看關於命令列模板參數請參閱外部版本管理說明。",
"Select All": "Select All",
"Select a version": "選擇一個版本",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select latest version": "選擇最新的版本",
"Select oldest version": "選擇最舊的版本",
"Select the devices to share this folder with.": "選擇要共享這個資料夾的裝置。",
@@ -299,6 +309,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 正在重新啟動。",
"Syncthing is upgrading.": "Syncthing 正在進行升級。",
"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 似乎離線了,或者您的網際網路連線出現問題。正在重試...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing 在處理您的請求時似乎遇到了問題。請重新整理本頁面,若問題持續發生,請重新啟動 Syncthing。",
"Take me back": "Take me back",
@@ -327,7 +338,8 @@
"The path cannot be blank.": "路徑不能空白。",
"The rate limit must be a non-negative number (0: no limit)": "限制速率必須為非負的數字 (0: 不設限制)",
"The rescan interval must be a non-negative number of seconds.": "重新掃描間隔必須為一個非負數的秒數。",
"They are retried automatically and will be synced when the error is resolved.": "解決間題後,將會自動重試和同步。",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"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.": "這能給駭客輕易的來讀取、變更電腦中的任何檔案。",
"This is a major version upgrade.": "這是一個重大版本更新。",
@@ -342,6 +354,7 @@
"Unignore": "Unignore",
"Unknown": "未知",
"Unshared": "未共享",
"Unshared Devices": "Unshared Devices",
"Unused": "未使用",
"Up to Date": "最新",
"Updated": "已更新",

View File

@@ -42,7 +42,7 @@
<p class="navbar-text hidden-xs" ng-class="{'hidden-sm':upgradeInfo && upgradeInfo.newer}">{{thisDeviceName()}}</p>
<ul class="nav navbar-nav navbar-right">
<li ng-if="upgradeInfo && upgradeInfo.newer" class="upgrade-newer">
<button type="button" class="btn navbar-btn btn-primary btn-sm" ng-click="upgrade()">
<button type="button" class="btn navbar-btn btn-primary btn-sm" data-toggle="modal" data-target="#upgrade">
<span class="fas fa-arrow-circle-up"></span>
<span class="hidden-xs" translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
</button>
@@ -67,7 +67,7 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="" ng-click="editSettings()"><span class="fas fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" ng-click="showSettings()"><span class="fas fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" data-toggle="modal" data-target="#idqr" ng-click="currentDevice=thisDevice()"><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="shutdown()"><span class="fas fa-fw fa-power-off"></span>&nbsp;<span translate>Shutdown</span></a></li>
@@ -113,7 +113,7 @@
</p>
</div>
<div class="panel-footer">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="editSettings()">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="showSettings()">
<span class="fas fa-cog"></span>&nbsp;<span translate>Settings</span>
</button>
<div class="clearfix"></div>
@@ -841,6 +841,7 @@
<ng-include src="'syncthing/transfer/failedFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/transfer/remoteNeededFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/transfer/localChangedFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/core/upgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/majorUpgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/aboutModalView.html'"></ng-include>
<ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>

View File

@@ -1,7 +1,7 @@
<modal id="about" status="info" icon="far fa-heart" heading="{{'About' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<h1 class="text-center">
<img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366" />
<img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="max-width: 366px; vertical-align: -16px" />
<br />
<small>{{versionString()}}</small>
<br />
@@ -14,7 +14,7 @@
<p translate>Copyright &copy; 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, 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, 佛跳墙
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, dependabot-preview[bot], Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alessandro G., Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, André Colomb, 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, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, 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, 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, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jacob, 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, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, 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, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, 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, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, 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, boomsquared, chenrui, chucic, dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, otbutz, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
</div>
</div>
<hr />

View File

@@ -8,7 +8,7 @@
<div id="log-viewer-log" class="tab-pane in active">
<label translate ng-if="logging.logEntries.length == 0">Loading...</label>
<textarea id="logViewerText" class="form-control" rows="20" ng-if="logging.logEntries.length != 0" readonly style="font-family: Consolas; font-size: 11px; overflow: auto;">{{ logging.content() }}</textarea>
<textarea id="logViewerText" class="form-control" rows="20" ng-if="logging.logEntries.length != 0" readonly style="font-family: Consolas, monospace; font-size: 11px; overflow: auto;">{{ logging.content() }}</textarea>
<p translate class="help-block" ng-style="{'visibility': logging.paused ? 'visible' : 'hidden'}">Log tailing paused. Scroll to the bottom to continue.</p>
</div>

View File

@@ -31,7 +31,7 @@
<p><a href="https://docs.syncthing.net/users/releases.html"><span class="fas fa-info-circle"></span>&nbsp;<span translate>Learn more</span></a></p>
</div>
<div class="panel-footer">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="editSettings(); dismissNotification('channelNotification')">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="showSettings(); dismissNotification('channelNotification')">
<span class="fas fa-cog"></span>&nbsp;<span translate>Settings</span>
</button>
<button type="button" class="btn btn-sm btn-default" ng-click="dismissNotification('channelNotification')">

View File

@@ -105,7 +105,7 @@ angular.module('syncthing.core')
$scope.needIcons = {
'rm': 'far fa-fw fa-trash-alt',
'rmdir': 'far fa-fw fa-trash-alt',
'sync': 'far fa-fw arrow-alt-circle-down',
'sync': 'far fa-fw fa-arrow-alt-circle-down',
'touch': 'fas fa-fw fa-asterisk'
};
@@ -690,7 +690,7 @@ angular.module('syncthing.core')
};
$scope.refreshFailed = function (page, perpage) {
if (!$scope.failed) {
if (!$scope.failed || !$scope.failed.folder) {
return;
}
var url = urlbase + '/folder/errors?folder=' + encodeURIComponent($scope.failed.folder);
@@ -849,10 +849,15 @@ angular.module('syncthing.core')
if (typeof $scope.model[folder] === 'undefined') {
return 100;
}
if ($scope.model[folder].globalBytes === 0) {
if ($scope.model[folder].needTotalItems === 0) {
return 100;
}
if ($scope.model[folder].needBytes == 0 && $scope.model[folder].needDeletes > 0) {
// We don't need any data, but we have deletes that we need
// to do. Drop down the completion percentage to indicate
// that we have stuff to do.
return 95;
}
var pct = 100 * $scope.model[folder].inSyncBytes / $scope.model[folder].globalBytes;
return Math.floor(pct);
};
@@ -1220,7 +1225,12 @@ angular.module('syncthing.core')
}
};
$scope.editSettings = function () {
$scope.discardChangedSettings = function () {
$("#discard-changes-confirmation").modal("hide");
$("#settings").off("hide.bs.modal").modal("hide");
};
$scope.showSettings = function () {
// Make a working copy
$scope.tmpOptions = angular.copy($scope.config.options);
$scope.tmpOptions.deviceName = $scope.thisDevice().name;
@@ -1234,26 +1244,19 @@ angular.module('syncthing.core')
$scope.tmpGUI = angular.copy($scope.config.gui);
$scope.tmpRemoteIgnoredDevices = angular.copy($scope.config.remoteIgnoredDevices);
$scope.tmpDevices = angular.copy($scope.config.devices);
var settingsModal = $('#settings').modal();
settingsModal.one('hidden.bs.modal', function () {
$('.nav-tabs a[href="#settings-general"]').tab('show');
window.location.hash = "";
settingsModal.off('hide.bs.modal');
}).on('hide.bs.modal', function (e) {
$('#settings').modal("show");
$("#settings a[href='#settings-general']").tab("show");
$("#settings").on('hide.bs.modal', function (event) {
if ($scope.settingsModified()) {
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function () {
if (!$scope.settingsModified()) {
settingsModal.modal('hide');
}
});
e.preventDefault();
e.stopImmediatePropagation();
return false;
event.preventDefault();
$("#discard-changes-confirmation").modal("show");
} else {
$("#settings").off("hide.bs.modal");
}
});
};
$scope.saveConfig = function (cb) {
$scope.saveConfig = function (callback) {
var cfg = JSON.stringify($scope.config);
var opts = {
headers: {
@@ -1262,8 +1265,9 @@ angular.module('syncthing.core')
};
$http.post(urlbase + '/system/config', cfg, opts).success(function () {
refreshConfig();
if (cb) {
cb();
if (callback) {
callback();
}
}).error(function (data, status, headers, config) {
refreshConfig();
@@ -1320,6 +1324,7 @@ angular.module('syncthing.core')
$scope.tmpOptions.upgradeToPreReleases = false;
} else {
$scope.tmpOptions.autoUpgradeIntervalH = 0;
$scope.tmpOptions.upgradeToPreReleases = false;
}
// Check if protocol will need to be changed on restart
@@ -1354,7 +1359,7 @@ angular.module('syncthing.core')
});
}
$('#settings').modal("hide");
$("#settings").off("hide.bs.modal").modal("hide");
};
$scope.saveAdvanced = function () {
@@ -1387,6 +1392,7 @@ angular.module('syncthing.core')
$scope.upgrade = function () {
restarting = true;
$('#upgrade').modal('hide');
$('#majorUpgrade').modal('hide');
$('#upgrading').modal();
$http.post(urlbase + '/system/upgrade').success(function () {
@@ -2055,6 +2061,9 @@ angular.module('syncthing.core')
value.modTime = new Date(value.modTime);
value.versionTime = new Date(value.versionTime);
});
values.sort(function (a, b) {
return b.versionTime - a.versionTime;
});
});
if (closed) return;
$scope.restoreVersions.versions = data;

View File

@@ -0,0 +1,18 @@
<modal id="upgrade" status="warning" icon="fas fa-arrow-circle-up" heading="{{'Upgrade' | translate}}" large="no" closeable="yes">
<div class="modal-body">
<p>
<span translate>Are you sure you want to upgrade?</span>
</p>
<p>
<a ng-href="https://github.com/syncthing/syncthing/releases/tag/{{upgradeInfo.latest}}" target="_blank" translate>Release Notes</a>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" ng-click="upgrade()">
<span class="fas fa-check"></span>&nbsp;<span translate>Upgrade</span>
</button>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div>
</modal>

View File

@@ -15,10 +15,10 @@
<datalist id="discovery-list">
<option ng-repeat="id in discovery" value="{{id}}" />
</datalist>
<p class="help-block" ng-if="discovery">
<p class="help-block" ng-if="discovery && discovery.length !== 0">
<span translate>You can also select one of these nearby devices:</span>
<ul>
<li ng-repeat="id in discovery"><a ng-click="currentDevice.deviceID = id">{{id}}</a></li>
<li ng-repeat="id in discovery"><a href="#" ng-click="currentDevice.deviceID = id">{{id}}</a></li>
</ul>
</p>
<p class="help-block">

View File

@@ -162,8 +162,8 @@
<dd><span translate>Comment, when used at the start of a line</span></dd>
</dl>
<hr />
<div class="pull-left" ng-show="editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span></div>
<div class="pull-left" ng-show="!editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Creating ignore patterns, overwriting an existing file at {%path%}.</span></div>
<span translate ng-show="editingExisting" translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span>
<span translate ng-show="!editingExisting" translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Creating ignore patterns, overwriting an existing file at {%path%}.</span>
</div>
<div id="folder-advanced" class="tab-pane">
<div class="row form-group" ng-class="{'has-error': folderEditor.rescanIntervalS.$invalid && folderEditor.rescanIntervalS.$dirty}">

View File

@@ -5,8 +5,8 @@
</p>
</div>
<div class="modal-footer">
<!-- editSettings() repopulates temp variables, which then makes the settingsModified() check pass -->
<button type="button" class="btn btn-warning pull-left btn-sm" data-dismiss="modal" ng-click="editSettings()">
<!-- showSettings() repopulates temp variables, which then makes the settingsModified() check pass -->
<button type="button" class="btn btn-warning pull-left btn-sm" ng-click="discardChangedSettings()">
<span class="fas fa-check"></span>&nbsp;<span translate>Discard</span>
</button>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">

View File

@@ -8,6 +8,7 @@ package api
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
@@ -136,7 +137,7 @@ func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonNam
configChanged: make(chan struct{}),
startedOnce: make(chan struct{}),
}
s.Service = util.AsService(s.serve)
s.Service = util.AsService(s.serve, s.String())
return s
}
@@ -207,7 +208,7 @@ func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
fmt.Fprintf(w, "%s\n", bs)
}
func (s *service) serve(stop chan struct{}) {
func (s *service) serve(ctx context.Context) {
listener, err := s.getListener(s.cfg.GUI())
if err != nil {
select {
@@ -360,7 +361,10 @@ func (s *service) serve(stop chan struct{}) {
l.Infoln("Access the GUI via the following URL:", guiCfg.URL())
if s.started != nil {
// only set when run by the tests
s.started <- listener.Addr().String()
select {
case <-ctx.Done(): // Shouldn't return directly due to cleanup below
case s.started <- listener.Addr().String():
}
}
// Indicate successful initial startup, to ourselves and to interested
@@ -375,13 +379,16 @@ func (s *service) serve(stop chan struct{}) {
serveError := make(chan error, 1)
go func() {
serveError <- srv.Serve(listener)
select {
case serveError <- srv.Serve(listener):
case <-ctx.Done():
}
}()
// Wait for stop, restart or error signals
select {
case <-stop:
case <-ctx.Done():
// Shutting down permanently
l.Debugln("shutting down (stop)")
case <-s.configChanged:
@@ -768,11 +775,21 @@ func (s *service) getSystemConnections(w http.ResponseWriter, r *http.Request) {
}
func (s *service) getDeviceStats(w http.ResponseWriter, r *http.Request) {
sendJSON(w, s.model.DeviceStatistics())
stats, err := s.model.DeviceStatistics()
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
sendJSON(w, stats)
}
func (s *service) getFolderStats(w http.ResponseWriter, r *http.Request) {
sendJSON(w, s.model.FolderStatistics())
stats, err := s.model.FolderStatistics()
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
sendJSON(w, stats)
}
func (s *service) getDBFile(w http.ResponseWriter, r *http.Request) {

View File

@@ -239,10 +239,11 @@ func TestAPIServiceRequests(t *testing.T) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
cases := []httpTestCase{
// /rest/db
@@ -451,10 +452,11 @@ func TestHTTPLogin(t *testing.T) {
cfg := new(mockedConfig)
cfg.gui.User = "üser"
cfg.gui.Password = "$2a$10$IdIZTxTg/dCNuNEGlmLynOjqg4B1FvDKuIV5e0BB3pnWVHNb8.GSq" // bcrypt of "räksmörgås" in UTF-8
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
// Verify rejection when not using authorization
@@ -512,7 +514,7 @@ func TestHTTPLogin(t *testing.T) {
}
}
func startHTTP(cfg *mockedConfig) (string, error) {
func startHTTP(cfg *mockedConfig) (string, *suture.Supervisor, error) {
m := new(mockedModel)
assetDir := "../../gui"
eventSub := new(mockedEventSub)
@@ -542,7 +544,8 @@ func startHTTP(cfg *mockedConfig) (string, error) {
addr := <-addrChan
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return "", fmt.Errorf("Weird address from API service: %v", err)
supervisor.Stop()
return "", nil, fmt.Errorf("Weird address from API service: %v", err)
}
host, _, _ := net.SplitHostPort(cfg.gui.RawAddress)
@@ -551,7 +554,7 @@ func startHTTP(cfg *mockedConfig) (string, error) {
}
baseURL := fmt.Sprintf("http://%s", net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port)))
return baseURL, nil
return baseURL, supervisor, nil
}
func TestCSRFRequired(t *testing.T) {
@@ -560,10 +563,11 @@ func TestCSRFRequired(t *testing.T) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal("Unexpected error from getting base URL:", err)
}
defer sup.Stop()
cli := &http.Client{
Timeout: time.Minute,
@@ -635,10 +639,11 @@ func TestRandomString(t *testing.T) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
cli := &http.Client{
Timeout: time.Second,
}
@@ -727,10 +732,11 @@ func testConfigPost(data io.Reader) (*http.Response, error) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
return nil, err
}
defer sup.Stop()
cli := &http.Client{
Timeout: time.Second,
}
@@ -747,10 +753,11 @@ func TestHostCheck(t *testing.T) {
cfg := new(mockedConfig)
cfg.gui.RawAddress = "127.0.0.1:0"
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
// A normal HTTP get to the localhost-bound service should succeed
@@ -807,10 +814,11 @@ func TestHostCheck(t *testing.T) {
cfg = new(mockedConfig)
cfg.gui.RawAddress = "127.0.0.1:0"
cfg.gui.InsecureSkipHostCheck = true
baseURL, err = startHTTP(cfg)
baseURL, sup, err = startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
// A request with a suspicious Host header should be allowed
@@ -830,10 +838,11 @@ func TestHostCheck(t *testing.T) {
cfg = new(mockedConfig)
cfg.gui.RawAddress = "0.0.0.0:0"
cfg.gui.InsecureSkipHostCheck = true
baseURL, err = startHTTP(cfg)
baseURL, sup, err = startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
// A request with a suspicious Host header should be allowed
@@ -857,10 +866,11 @@ func TestHostCheck(t *testing.T) {
cfg = new(mockedConfig)
cfg.gui.RawAddress = "[::1]:0"
baseURL, err = startHTTP(cfg)
baseURL, sup, err = startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
// A normal HTTP get to the localhost-bound service should succeed
@@ -951,10 +961,11 @@ func TestAccessControlAllowOriginHeader(t *testing.T) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
cli := &http.Client{
Timeout: time.Second,
}
@@ -981,10 +992,11 @@ func TestOptionsRequest(t *testing.T) {
const testAPIKey = "foobarbaz"
cfg := new(mockedConfig)
cfg.gui.APIKey = testAPIKey
baseURL, err := startHTTP(cfg)
baseURL, sup, err := startHTTP(cfg)
if err != nil {
t.Fatal(err)
}
defer sup.Stop()
cli := &http.Client{
Timeout: time.Second,
}

View File

@@ -10,7 +10,6 @@ import (
"net"
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/model"
@@ -49,12 +48,12 @@ func (m *mockedModel) ConnectionStats() map[string]interface{} {
return nil
}
func (m *mockedModel) DeviceStatistics() map[string]stats.DeviceStatistics {
return nil
func (m *mockedModel) DeviceStatistics() (map[string]stats.DeviceStatistics, error) {
return nil, nil
}
func (m *mockedModel) FolderStatistics() map[string]stats.FolderStatistics {
return nil
func (m *mockedModel) FolderStatistics() (map[string]stats.FolderStatistics, error) {
return nil, nil
}
func (m *mockedModel) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) {
@@ -153,21 +152,29 @@ func (m *mockedModel) LocalChangedFiles(folder string, page, perpage int) []db.F
return nil
}
func (m *mockedModel) Serve() {}
func (m *mockedModel) Stop() {}
func (m *mockedModel) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {}
func (m *mockedModel) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {
func (m *mockedModel) Serve() {}
func (m *mockedModel) Stop() {}
func (m *mockedModel) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) error {
return nil
}
func (m *mockedModel) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) error {
return nil
}
func (m *mockedModel) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (protocol.RequestResponse, error) {
return nil, nil
}
func (m *mockedModel) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfig) {}
func (m *mockedModel) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfig) error {
return nil
}
func (m *mockedModel) Closed(conn protocol.Connection, err error) {}
func (m *mockedModel) DownloadProgress(deviceID protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
func (m *mockedModel) DownloadProgress(deviceID protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) error {
return nil
}
func (m *mockedModel) AddConnection(conn connections.Connection, hello protocol.HelloResult) {}
@@ -180,10 +187,4 @@ func (m *mockedModel) GetHello(protocol.DeviceID) protocol.HelloIntf {
return nil
}
func (m *mockedModel) AddFolder(cfg config.FolderConfiguration) {}
func (m *mockedModel) RestartFolder(from, to config.FolderConfiguration) {}
func (m *mockedModel) StartFolder(folder string) {}
func (m *mockedModel) StartDeadlockDetector(timeout time.Duration) {}

View File

@@ -7,6 +7,7 @@
package beacon
import (
"context"
"fmt"
"net"
"time"
@@ -63,23 +64,23 @@ func newCast(name string) *cast {
}
}
func (c *cast) addReader(svc func(chan struct{}) error) {
func (c *cast) addReader(svc func(context.Context) error) {
c.reader = c.createService(svc, "reader")
c.Add(c.reader)
}
func (c *cast) addWriter(svc func(stop chan struct{}) error) {
func (c *cast) addWriter(svc func(ctx context.Context) error) {
c.writer = c.createService(svc, "writer")
c.Add(c.writer)
}
func (c *cast) createService(svc func(chan struct{}) error, suffix string) util.ServiceWithError {
return util.AsServiceWithError(func(stop chan struct{}) error {
func (c *cast) createService(svc func(context.Context) error, suffix string) util.ServiceWithError {
return util.AsServiceWithError(func(ctx context.Context) error {
l.Debugln("Starting", c.name, suffix)
err := svc(stop)
err := svc(ctx)
l.Debugf("Stopped %v %v: %v", c.name, suffix, err)
return err
})
}, fmt.Sprintf("%s/%s", c, suffix))
}
func (c *cast) Stop() {

View File

@@ -7,34 +7,32 @@
package beacon
import (
"context"
"net"
"time"
)
func NewBroadcast(port int) Interface {
c := newCast("broadcastBeacon")
c.addReader(func(stop chan struct{}) error {
return readBroadcasts(c.outbox, port, stop)
c.addReader(func(ctx context.Context) error {
return readBroadcasts(ctx, c.outbox, port)
})
c.addWriter(func(stop chan struct{}) error {
return writeBroadcasts(c.inbox, port, stop)
c.addWriter(func(ctx context.Context) error {
return writeBroadcasts(ctx, c.inbox, port)
})
return c
}
func writeBroadcasts(inbox <-chan []byte, port int, stop chan struct{}) error {
func writeBroadcasts(ctx context.Context, inbox <-chan []byte, port int) error {
conn, err := net.ListenUDP("udp4", nil)
if err != nil {
l.Debugln(err)
return err
}
done := make(chan struct{})
defer close(done)
doneCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-stop:
case <-done:
}
<-doneCtx.Done()
conn.Close()
}()
@@ -42,7 +40,7 @@ func writeBroadcasts(inbox <-chan []byte, port int, stop chan struct{}) error {
var bs []byte
select {
case bs = <-inbox:
case <-stop:
case <-doneCtx.Done():
return nil
}
@@ -99,19 +97,17 @@ func writeBroadcasts(inbox <-chan []byte, port int, stop chan struct{}) error {
}
}
func readBroadcasts(outbox chan<- recv, port int, stop chan struct{}) error {
func readBroadcasts(ctx context.Context, outbox chan<- recv, port int) error {
conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: port})
if err != nil {
l.Debugln(err)
return err
}
done := make(chan struct{})
defer close(done)
doneCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-stop:
case <-done:
}
<-doneCtx.Done()
conn.Close()
}()
@@ -129,7 +125,7 @@ func readBroadcasts(outbox chan<- recv, port int, stop chan struct{}) error {
copy(c, bs)
select {
case outbox <- recv{c, addr}:
case <-stop:
case <-doneCtx.Done():
return nil
default:
l.Debugln("dropping message")

View File

@@ -7,6 +7,7 @@
package beacon
import (
"context"
"errors"
"net"
"time"
@@ -16,16 +17,16 @@ import (
func NewMulticast(addr string) Interface {
c := newCast("multicastBeacon")
c.addReader(func(stop chan struct{}) error {
return readMulticasts(c.outbox, addr, stop)
c.addReader(func(ctx context.Context) error {
return readMulticasts(ctx, c.outbox, addr)
})
c.addWriter(func(stop chan struct{}) error {
return writeMulticasts(c.inbox, addr, stop)
c.addWriter(func(ctx context.Context) error {
return writeMulticasts(ctx, c.inbox, addr)
})
return c
}
func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error {
func writeMulticasts(ctx context.Context, inbox <-chan []byte, addr string) error {
gaddr, err := net.ResolveUDPAddr("udp6", addr)
if err != nil {
l.Debugln(err)
@@ -37,13 +38,10 @@ func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error
l.Debugln(err)
return err
}
done := make(chan struct{})
defer close(done)
doneCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-stop:
case <-done:
}
<-doneCtx.Done()
conn.Close()
}()
@@ -57,7 +55,7 @@ func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error
var bs []byte
select {
case bs = <-inbox:
case <-stop:
case <-doneCtx.Done():
return nil
}
@@ -84,7 +82,7 @@ func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error
success++
select {
case <-stop:
case <-doneCtx.Done():
return nil
default:
}
@@ -96,7 +94,7 @@ func writeMulticasts(inbox <-chan []byte, addr string, stop chan struct{}) error
}
}
func readMulticasts(outbox chan<- recv, addr string, stop chan struct{}) error {
func readMulticasts(ctx context.Context, outbox chan<- recv, addr string) error {
gaddr, err := net.ResolveUDPAddr("udp6", addr)
if err != nil {
l.Debugln(err)
@@ -108,13 +106,10 @@ func readMulticasts(outbox chan<- recv, addr string, stop chan struct{}) error {
l.Debugln(err)
return err
}
done := make(chan struct{})
defer close(done)
doneCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-stop:
case <-done:
}
<-doneCtx.Done()
conn.Close()
}()
@@ -144,7 +139,7 @@ func readMulticasts(outbox chan<- recv, addr string, stop chan struct{}) error {
bs := make([]byte, 65536)
for {
select {
case <-stop:
case <-doneCtx.Done():
return nil
default:
}

View File

@@ -10,7 +10,6 @@ package config
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
@@ -22,6 +21,8 @@ import (
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
@@ -120,18 +121,18 @@ func NewWithFreePorts(myID protocol.DeviceID) (Configuration, error) {
port, err := getFreePort("127.0.0.1", DefaultGUIPort)
if err != nil {
return Configuration{}, fmt.Errorf("get free port (GUI): %v", err)
return Configuration{}, errors.Wrap(err, "get free port (GUI)")
}
cfg.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%d", port)
port, err = getFreePort("0.0.0.0", DefaultTCPPort)
if err != nil {
return Configuration{}, fmt.Errorf("get free port (BEP): %v", err)
return Configuration{}, errors.Wrap(err, "get free port (BEP)")
}
if port == DefaultTCPPort {
cfg.Options.ListenAddresses = []string{"default"}
cfg.Options.RawListenAddresses = []string{"default"}
} else {
cfg.Options.ListenAddresses = []string{
cfg.Options.RawListenAddresses = []string{
fmt.Sprintf("tcp://%s", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
"dynamic+https://relays.syncthing.net/endpoint",
}
@@ -304,8 +305,8 @@ func (cfg *Configuration) clean() error {
existingFolders[folder.ID] = folder
}
cfg.Options.ListenAddresses = util.UniqueTrimmedStrings(cfg.Options.ListenAddresses)
cfg.Options.GlobalAnnServers = util.UniqueTrimmedStrings(cfg.Options.GlobalAnnServers)
cfg.Options.RawListenAddresses = util.UniqueTrimmedStrings(cfg.Options.RawListenAddresses)
cfg.Options.RawGlobalAnnServers = util.UniqueTrimmedStrings(cfg.Options.RawGlobalAnnServers)
if cfg.Version > 0 && cfg.Version < OldestHandledVersion {
l.Warnf("Configuration version %d is deprecated. Attempting best effort conversion, but please verify manually.", cfg.Version)
@@ -395,7 +396,7 @@ nextPendingDevice:
// Deprecated protocols are removed from the list of listeners and
// device addresses. So far just kcp*.
for _, prefix := range []string{"kcp"} {
cfg.Options.ListenAddresses = filterURLSchemePrefix(cfg.Options.ListenAddresses, prefix)
cfg.Options.RawListenAddresses = filterURLSchemePrefix(cfg.Options.RawListenAddresses, prefix)
for i := range cfg.Devices {
dev := &cfg.Devices[i]
dev.Addresses = filterURLSchemePrefix(dev.Addresses, prefix)

View File

@@ -37,8 +37,8 @@ func init() {
func TestDefaultValues(t *testing.T) {
expected := OptionsConfiguration{
ListenAddresses: []string{"default"},
GlobalAnnServers: []string{"default"},
RawListenAddresses: []string{"default"},
RawGlobalAnnServers: []string{"default"},
GlobalAnnEnabled: true,
LocalAnnEnabled: true,
LocalAnnPort: 21027,
@@ -74,7 +74,7 @@ func TestDefaultValues(t *testing.T) {
CREnabled: true,
StunKeepaliveStartS: 180,
StunKeepaliveMinS: 20,
StunServers: []string{"default"},
RawStunServers: []string{"default"},
}
cfg := New(device1)
@@ -175,16 +175,16 @@ func TestNoListenAddresses(t *testing.T) {
}
expected := []string{""}
actual := cfg.Options().ListenAddresses
actual := cfg.Options().RawListenAddresses
if diff, equal := messagediff.PrettyDiff(expected, actual); !equal {
t.Errorf("Unexpected ListenAddresses. Diff:\n%s", diff)
t.Errorf("Unexpected RawListenAddresses. Diff:\n%s", diff)
}
}
func TestOverriddenValues(t *testing.T) {
expected := OptionsConfiguration{
ListenAddresses: []string{"tcp://:23000"},
GlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"},
RawListenAddresses: []string{"tcp://:23000"},
RawGlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"},
GlobalAnnEnabled: false,
LocalAnnEnabled: false,
LocalAnnPort: 42123,
@@ -222,7 +222,7 @@ func TestOverriddenValues(t *testing.T) {
CREnabled: false,
StunKeepaliveStartS: 9000,
StunKeepaliveMinS: 900,
StunServers: []string{"foo"},
RawStunServers: []string{"foo"},
}
os.Unsetenv("STNOUPGRADE")
@@ -424,20 +424,20 @@ func TestIssue1750(t *testing.T) {
t.Fatal(err)
}
if cfg.Options().ListenAddresses[0] != "tcp://:23000" {
t.Errorf("%q != %q", cfg.Options().ListenAddresses[0], "tcp://:23000")
if cfg.Options().RawListenAddresses[0] != "tcp://:23000" {
t.Errorf("%q != %q", cfg.Options().RawListenAddresses[0], "tcp://:23000")
}
if cfg.Options().ListenAddresses[1] != "tcp://:23001" {
t.Errorf("%q != %q", cfg.Options().ListenAddresses[1], "tcp://:23001")
if cfg.Options().RawListenAddresses[1] != "tcp://:23001" {
t.Errorf("%q != %q", cfg.Options().RawListenAddresses[1], "tcp://:23001")
}
if cfg.Options().GlobalAnnServers[0] != "udp4://syncthing.nym.se:22026" {
t.Errorf("%q != %q", cfg.Options().GlobalAnnServers[0], "udp4://syncthing.nym.se:22026")
if cfg.Options().RawGlobalAnnServers[0] != "udp4://syncthing.nym.se:22026" {
t.Errorf("%q != %q", cfg.Options().RawGlobalAnnServers[0], "udp4://syncthing.nym.se:22026")
}
if cfg.Options().GlobalAnnServers[1] != "udp4://syncthing.nym.se:22027" {
t.Errorf("%q != %q", cfg.Options().GlobalAnnServers[1], "udp4://syncthing.nym.se:22027")
if cfg.Options().RawGlobalAnnServers[1] != "udp4://syncthing.nym.se:22027" {
t.Errorf("%q != %q", cfg.Options().RawGlobalAnnServers[1], "udp4://syncthing.nym.se:22027")
}
}
@@ -553,13 +553,13 @@ func TestNewSaveLoad(t *testing.T) {
func TestPrepare(t *testing.T) {
var cfg Configuration
if cfg.Folders != nil || cfg.Devices != nil || cfg.Options.ListenAddresses != nil {
if cfg.Folders != nil || cfg.Devices != nil || cfg.Options.RawListenAddresses != nil {
t.Error("Expected nil")
}
cfg.prepare(device1)
if cfg.Folders == nil || cfg.Devices == nil || cfg.Options.ListenAddresses == nil {
if cfg.Folders == nil || cfg.Devices == nil || cfg.Options.RawListenAddresses == nil {
t.Error("Unexpected nil")
}
}
@@ -580,7 +580,7 @@ func TestCopy(t *testing.T) {
cfg.Devices[0].Addresses[0] = "wrong"
cfg.Folders[0].Devices[0].DeviceID = protocol.DeviceID{0, 1, 2, 3}
cfg.Options.ListenAddresses[0] = "wrong"
cfg.Options.RawListenAddresses[0] = "wrong"
cfg.GUI.APIKey = "wrong"
bsChanged, err := json.MarshalIndent(cfg, "", " ")
@@ -771,7 +771,7 @@ func TestV14ListenAddressesMigration(t *testing.T) {
cfg := Configuration{
Version: 13,
Options: OptionsConfiguration{
ListenAddresses: tc[0],
RawListenAddresses: tc[0],
DeprecatedRelayServers: tc[1],
},
}
@@ -781,8 +781,8 @@ func TestV14ListenAddressesMigration(t *testing.T) {
}
sort.Strings(tc[2])
if !reflect.DeepEqual(cfg.Options.ListenAddresses, tc[2]) {
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.ListenAddresses, tc[2])
if !reflect.DeepEqual(cfg.Options.RawListenAddresses, tc[2]) {
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.RawListenAddresses, tc[2])
}
}
}

View File

@@ -18,7 +18,6 @@ import (
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
var (
@@ -105,18 +104,6 @@ func (f FolderConfiguration) Filesystem() fs.Filesystem {
return f.cachedFilesystem
}
func (f FolderConfiguration) Versioner() versioner.Versioner {
if f.Versioning.Type == "" {
return nil
}
versionerFactory, ok := versioner.Factories[f.Versioning.Type]
if !ok {
panic(fmt.Sprintf("Requested versioning type %q that does not exist", f.Versioning.Type))
}
return versionerFactory(f.ID, f.Filesystem(), f.Versioning.Params)
}
func (f FolderConfiguration) ModTimeWindow() time.Duration {
return f.cachedModTimeWindow
}

View File

@@ -216,12 +216,12 @@ func migrateToConfigV18(cfg *Configuration) {
func migrateToConfigV15(cfg *Configuration) {
// Undo v0.13.0 broken migration
for i, addr := range cfg.Options.GlobalAnnServers {
for i, addr := range cfg.Options.RawGlobalAnnServers {
switch addr {
case "default-v4v2/":
cfg.Options.GlobalAnnServers[i] = "default-v4"
cfg.Options.RawGlobalAnnServers[i] = "default-v4"
case "default-v6v2/":
cfg.Options.GlobalAnnServers[i] = "default-v6"
cfg.Options.RawGlobalAnnServers[i] = "default-v6"
}
}
}
@@ -248,9 +248,9 @@ func migrateToConfigV14(cfg *Configuration) {
hasDefault := false
for _, raddr := range cfg.Options.DeprecatedRelayServers {
if raddr == "dynamic+https://relays.syncthing.net/endpoint" {
for i, addr := range cfg.Options.ListenAddresses {
for i, addr := range cfg.Options.RawListenAddresses {
if addr == "tcp://0.0.0.0:22000" {
cfg.Options.ListenAddresses[i] = "default"
cfg.Options.RawListenAddresses[i] = "default"
hasDefault = true
break
}
@@ -269,16 +269,16 @@ func migrateToConfigV14(cfg *Configuration) {
if addr == "" {
continue
}
cfg.Options.ListenAddresses = append(cfg.Options.ListenAddresses, addr)
cfg.Options.RawListenAddresses = append(cfg.Options.RawListenAddresses, addr)
}
cfg.Options.DeprecatedRelayServers = nil
// For consistency
sort.Strings(cfg.Options.ListenAddresses)
sort.Strings(cfg.Options.RawListenAddresses)
var newAddrs []string
for _, addr := range cfg.Options.GlobalAnnServers {
for _, addr := range cfg.Options.RawGlobalAnnServers {
uri, err := url.Parse(addr)
if err != nil {
// That's odd. Skip the broken address.
@@ -291,7 +291,7 @@ func migrateToConfigV14(cfg *Configuration) {
newAddrs = append(newAddrs, addr)
}
cfg.Options.GlobalAnnServers = newAddrs
cfg.Options.RawGlobalAnnServers = newAddrs
for i, fcfg := range cfg.Folders {
if fcfg.DeprecatedReadOnly {
@@ -315,9 +315,9 @@ func migrateToConfigV13(cfg *Configuration) {
func migrateToConfigV12(cfg *Configuration) {
// Change listen address schema
for i, addr := range cfg.Options.ListenAddresses {
for i, addr := range cfg.Options.RawListenAddresses {
if len(addr) > 0 && !strings.HasPrefix(addr, "tcp://") {
cfg.Options.ListenAddresses[i] = util.Address("tcp", addr)
cfg.Options.RawListenAddresses[i] = util.Address("tcp", addr)
}
}
@@ -332,7 +332,7 @@ func migrateToConfigV12(cfg *Configuration) {
// Use new discovery server
var newDiscoServers []string
var useDefault bool
for _, addr := range cfg.Options.GlobalAnnServers {
for _, addr := range cfg.Options.RawGlobalAnnServers {
if addr == "udp4://announce.syncthing.net:22026" {
useDefault = true
} else if addr == "udp6://announce-v6.syncthing.net:22026" {
@@ -344,7 +344,7 @@ func migrateToConfigV12(cfg *Configuration) {
if useDefault {
newDiscoServers = append(newDiscoServers, "default")
}
cfg.Options.GlobalAnnServers = newDiscoServers
cfg.Options.RawGlobalAnnServers = newDiscoServers
// Use new multicast group
if cfg.Options.LocalAnnMCAddr == "[ff32::5222]:21026" {

View File

@@ -9,12 +9,13 @@ package config
import (
"fmt"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/util"
)
type OptionsConfiguration struct {
ListenAddresses []string `xml:"listenAddress" json:"listenAddresses" default:"default"`
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" default:"default" restart:"true"`
RawListenAddresses []string `xml:"listenAddress" json:"listenAddresses" default:"default"`
RawGlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" default:"default" restart:"true"`
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true" restart:"true"`
LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true" restart:"true"`
LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21027" restart:"true"`
@@ -56,7 +57,7 @@ type OptionsConfiguration struct {
CREnabled bool `xml:"crashReportingEnabled" json:"crashReportingEnabled" default:"true" restart:"true"`
StunKeepaliveStartS int `xml:"stunKeepaliveStartS" json:"stunKeepaliveStartS" default:"180"` // 0 for off
StunKeepaliveMinS int `xml:"stunKeepaliveMinS" json:"stunKeepaliveMinS" default:"20"` // 0 for off
StunServers []string `xml:"stunServer" json:"stunServers" default:"default"`
RawStunServers []string `xml:"stunServer" json:"stunServers" default:"default"`
DatabaseTuning Tuning `xml:"databaseTuning" json:"databaseTuning" restart:"true"`
DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
@@ -69,10 +70,10 @@ type OptionsConfiguration struct {
func (opts OptionsConfiguration) Copy() OptionsConfiguration {
optsCopy := opts
optsCopy.ListenAddresses = make([]string, len(opts.ListenAddresses))
copy(optsCopy.ListenAddresses, opts.ListenAddresses)
optsCopy.GlobalAnnServers = make([]string, len(opts.GlobalAnnServers))
copy(optsCopy.GlobalAnnServers, opts.GlobalAnnServers)
optsCopy.RawListenAddresses = make([]string, len(opts.RawListenAddresses))
copy(optsCopy.RawListenAddresses, opts.RawListenAddresses)
optsCopy.RawGlobalAnnServers = make([]string, len(opts.RawGlobalAnnServers))
copy(optsCopy.RawGlobalAnnServers, opts.RawGlobalAnnServers)
optsCopy.AlwaysLocalNets = make([]string, len(opts.AlwaysLocalNets))
copy(optsCopy.AlwaysLocalNets, opts.AlwaysLocalNets)
optsCopy.UnackedNotificationIDs = make([]string, len(opts.UnackedNotificationIDs))
@@ -97,3 +98,57 @@ func (opts OptionsConfiguration) RequiresRestartOnly() OptionsConfiguration {
func (opts OptionsConfiguration) IsStunDisabled() bool {
return opts.StunKeepaliveMinS < 1 || opts.StunKeepaliveStartS < 1 || !opts.NATEnabled
}
func (opts OptionsConfiguration) ListenAddresses() []string {
var addresses []string
for _, addr := range opts.RawListenAddresses {
switch addr {
case "default":
addresses = append(addresses, DefaultListenAddresses...)
default:
addresses = append(addresses, addr)
}
}
return util.UniqueTrimmedStrings(addresses)
}
func (opts OptionsConfiguration) StunServers() []string {
var addresses []string
for _, addr := range opts.RawStunServers {
switch addr {
case "default":
defaultPrimaryAddresses := make([]string, len(DefaultPrimaryStunServers))
copy(defaultPrimaryAddresses, DefaultPrimaryStunServers)
rand.Shuffle(defaultPrimaryAddresses)
addresses = append(addresses, defaultPrimaryAddresses...)
defaultSecondaryAddresses := make([]string, len(DefaultSecondaryStunServers))
copy(defaultSecondaryAddresses, DefaultSecondaryStunServers)
rand.Shuffle(defaultSecondaryAddresses)
addresses = append(addresses, defaultSecondaryAddresses...)
default:
addresses = append(addresses, addr)
}
}
addresses = util.UniqueTrimmedStrings(addresses)
return addresses
}
func (opts OptionsConfiguration) GlobalDiscoveryServers() []string {
var servers []string
for _, srv := range opts.RawGlobalAnnServers {
switch srv {
case "default":
servers = append(servers, DefaultDiscoveryServers...)
case "default-v4":
servers = append(servers, DefaultDiscoveryServersV4...)
case "default-v6":
servers = append(servers, DefaultDiscoveryServersV6...)
default:
servers = append(servers, srv)
}
}
return util.UniqueTrimmedStrings(servers)
}

View File

@@ -10,17 +10,17 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
)
func TestTuningMatches(t *testing.T) {
if int(config.TuningAuto) != int(db.TuningAuto) {
if int(config.TuningAuto) != int(backend.TuningAuto) {
t.Error("mismatch for TuningAuto")
}
if int(config.TuningSmall) != int(db.TuningSmall) {
if int(config.TuningSmall) != int(backend.TuningSmall) {
t.Error("mismatch for TuningSmall")
}
if int(config.TuningLarge) != int(db.TuningLarge) {
if int(config.TuningLarge) != int(backend.TuningLarge) {
t.Error("mismatch for TuningLarge")
}
}

View File

@@ -14,9 +14,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
)
// The Committer interface is implemented by objects that need to know about
@@ -87,10 +85,6 @@ type Wrapper interface {
IgnoredDevice(id protocol.DeviceID) bool
IgnoredFolder(device protocol.DeviceID, folder string) bool
ListenAddresses() []string
GlobalDiscoveryServers() []string
StunServers() []string
Subscribe(c Committer)
Unsubscribe(c Committer)
}
@@ -100,6 +94,7 @@ type wrapper struct {
path string
evLogger events.Logger
waiter Waiter // Latest ongoing config change
deviceMap map[protocol.DeviceID]DeviceConfiguration
folderMap map[string]FolderConfiguration
subs []Committer
@@ -108,30 +103,6 @@ type wrapper struct {
requiresRestart uint32 // an atomic bool
}
func (w *wrapper) StunServers() []string {
var addresses []string
for _, addr := range w.cfg.Options.StunServers {
switch addr {
case "default":
defaultPrimaryAddresses := make([]string, len(DefaultPrimaryStunServers))
copy(defaultPrimaryAddresses, DefaultPrimaryStunServers)
rand.Shuffle(defaultPrimaryAddresses)
addresses = append(addresses, defaultPrimaryAddresses...)
defaultSecondaryAddresses := make([]string, len(DefaultSecondaryStunServers))
copy(defaultSecondaryAddresses, DefaultSecondaryStunServers)
rand.Shuffle(defaultSecondaryAddresses)
addresses = append(addresses, defaultSecondaryAddresses...)
default:
addresses = append(addresses, addr)
}
}
addresses = util.UniqueTrimmedStrings(addresses)
return addresses
}
// Wrap wraps an existing Configuration structure and ties it to a file on
// disk.
func Wrap(path string, cfg Configuration, evLogger events.Logger) Wrapper {
@@ -139,6 +110,7 @@ func Wrap(path string, cfg Configuration, evLogger events.Logger) Wrapper {
cfg: cfg,
path: path,
evLogger: evLogger,
waiter: noopWaiter{}, // Noop until first config change
mut: sync.NewMutex(),
}
return w
@@ -174,7 +146,8 @@ func (w *wrapper) Subscribe(c Committer) {
}
// Unsubscribe de-registers the given handler from any future calls to
// configuration changes
// configuration changes and only returns after a potential ongoing config
// change is done.
func (w *wrapper) Unsubscribe(c Committer) {
w.mut.Lock()
for i := range w.subs {
@@ -185,7 +158,11 @@ func (w *wrapper) Unsubscribe(c Committer) {
break
}
}
waiter := w.waiter
w.mut.Unlock()
// Waiting mustn't be done under lock, as the goroutines in notifyListener
// may dead-lock when trying to access lock on config read operations.
waiter.Wait()
}
// RawCopy returns a copy of the currently wrapped Configuration object.
@@ -221,7 +198,9 @@ func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) {
w.deviceMap = nil
w.folderMap = nil
return w.notifyListeners(from.Copy(), to.Copy()), nil
w.waiter = w.notifyListeners(from.Copy(), to.Copy())
return w.waiter, nil
}
func (w *wrapper) notifyListeners(from, to Configuration) Waiter {
@@ -456,36 +435,6 @@ func (w *wrapper) Save() error {
return nil
}
func (w *wrapper) GlobalDiscoveryServers() []string {
var servers []string
for _, srv := range w.Options().GlobalAnnServers {
switch srv {
case "default":
servers = append(servers, DefaultDiscoveryServers...)
case "default-v4":
servers = append(servers, DefaultDiscoveryServersV4...)
case "default-v6":
servers = append(servers, DefaultDiscoveryServersV6...)
default:
servers = append(servers, srv)
}
}
return util.UniqueTrimmedStrings(servers)
}
func (w *wrapper) ListenAddresses() []string {
var addresses []string
for _, addr := range w.Options().ListenAddresses {
switch addr {
case "default":
addresses = append(addresses, DefaultListenAddresses...)
default:
addresses = append(addresses, addr)
}
}
return util.UniqueTrimmedStrings(addresses)
}
func (w *wrapper) RequiresRestart() bool {
return atomic.LoadUint32(&w.requiresRestart) != 0
}

View File

@@ -11,12 +11,12 @@ package connections
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/url"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
@@ -39,11 +39,10 @@ func init() {
}
type quicDialer struct {
cfg config.Wrapper
tlsCfg *tls.Config
commonDialer
}
func (d *quicDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, error) {
func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL) (internalConn, error) {
uri = fixupPort(uri, config.DefaultQUICPort)
addr, err := net.ResolveUDPAddr("udp", uri.Host)
@@ -67,7 +66,7 @@ func (d *quicDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, erro
}
}
ctx, cancel := context.WithTimeout(context.Background(), quicOperationTimeout)
ctx, cancel := context.WithTimeout(ctx, quicOperationTimeout)
defer cancel()
session, err := quic.DialContext(ctx, conn, addr, uri.Host, d.tlsCfg, quicConfig)
@@ -75,7 +74,7 @@ func (d *quicDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, erro
if createdConn != nil {
_ = createdConn.Close()
}
return internalConn{}, fmt.Errorf("dial: %v", err)
return internalConn{}, errors.Wrap(err, "dial")
}
stream, err := session.OpenStreamSync(ctx)
@@ -85,26 +84,22 @@ func (d *quicDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, erro
if createdConn != nil {
_ = createdConn.Close()
}
return internalConn{}, fmt.Errorf("open stream: %v", err)
return internalConn{}, errors.Wrap(err, "open stream")
}
return internalConn{&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority}, nil
}
func (d *quicDialer) RedialFrequency() time.Duration {
return time.Duration(d.cfg.Options().ReconnectIntervalS) * time.Second
}
type quicDialerFactory struct {
cfg config.Wrapper
tlsCfg *tls.Config
}
func (quicDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer {
return &quicDialer{
cfg: cfg,
tlsCfg: tlsCfg,
}
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
return &quicDialer{commonDialer{
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
}}
}
func (quicDialerFactory) Priority() int {

View File

@@ -78,13 +78,9 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string)
}
}
func (t *quicListener) serve(stop chan struct{}) error {
func (t *quicListener) serve(ctx context.Context) error {
network := strings.Replace(t.uri.Scheme, "quic", "udp", -1)
// Convert the stop channel into a context
ctx, cancel := context.WithCancel(context.Background())
go func() { <-stop; cancel() }()
packetConn, err := net.ListenPacket(network, t.uri.Host)
if err != nil {
l.Infoln("Listen (BEP/quic):", err)
@@ -205,7 +201,7 @@ func (f *quicListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.
conns: conns,
factory: f,
}
l.ServiceWithError = util.AsServiceWithError(l.serve)
l.ServiceWithError = util.AsServiceWithError(l.serve, l.String())
l.nat.Store(stun.NATUnknown)
return l
}

View File

@@ -7,6 +7,7 @@
package connections
import (
"context"
"crypto/tls"
"net/url"
"time"
@@ -24,17 +25,16 @@ func init() {
}
type relayDialer struct {
cfg config.Wrapper
tlsCfg *tls.Config
commonDialer
}
func (d *relayDialer) Dial(id protocol.DeviceID, uri *url.URL) (internalConn, error) {
inv, err := client.GetInvitationFromRelay(uri, id, d.tlsCfg.Certificates, 10*time.Second)
func (d *relayDialer) Dial(ctx context.Context, id protocol.DeviceID, uri *url.URL) (internalConn, error) {
inv, err := client.GetInvitationFromRelay(ctx, uri, id, d.tlsCfg.Certificates, 10*time.Second)
if err != nil {
return internalConn{}, err
}
conn, err := client.JoinSession(inv)
conn, err := client.JoinSession(ctx, inv)
if err != nil {
return internalConn{}, err
}
@@ -45,7 +45,7 @@ func (d *relayDialer) Dial(id protocol.DeviceID, uri *url.URL) (internalConn, er
return internalConn{}, err
}
err = dialer.SetTrafficClass(conn, d.cfg.Options().TrafficClass)
err = dialer.SetTrafficClass(conn, d.trafficClass)
if err != nil {
l.Debugln("Dial (BEP/relay): setting traffic class:", err)
}
@@ -66,17 +66,14 @@ func (d *relayDialer) Dial(id protocol.DeviceID, uri *url.URL) (internalConn, er
return internalConn{tc, connTypeRelayClient, relayPriority}, nil
}
func (d *relayDialer) RedialFrequency() time.Duration {
return time.Duration(d.cfg.Options().RelayReconnectIntervalM) * time.Minute
}
type relayDialerFactory struct{}
func (relayDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer {
return &relayDialer{
cfg: cfg,
tlsCfg: tlsCfg,
}
func (relayDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
return &relayDialer{commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.RelayReconnectIntervalM) * time.Minute,
tlsCfg: tlsCfg,
}}
}
func (relayDialerFactory) Priority() int {

View File

@@ -7,11 +7,14 @@
package connections
import (
"context"
"crypto/tls"
"net/url"
"sync"
"time"
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/nat"
@@ -40,7 +43,7 @@ type relayListener struct {
mut sync.RWMutex
}
func (t *relayListener) serve(stop chan struct{}) error {
func (t *relayListener) serve(ctx context.Context) error {
clnt, err := client.NewClient(t.uri, t.tlsCfg.Certificates, nil, 10*time.Second)
if err != nil {
l.Infoln("Listen (BEP/relay):", err)
@@ -69,9 +72,11 @@ func (t *relayListener) serve(stop chan struct{}) error {
return err
}
conn, err := client.JoinSession(inv)
conn, err := client.JoinSession(ctx, inv)
if err != nil {
l.Infoln("Listen (BEP/relay): joining session:", err)
if errors.Cause(err) != context.Canceled {
l.Infoln("Listen (BEP/relay): joining session:", err)
}
continue
}
@@ -112,7 +117,7 @@ func (t *relayListener) serve(stop chan struct{}) error {
t.notifyAddressesChanged(t)
}
case <-stop:
case <-ctx.Done():
return nil
}
}
@@ -178,7 +183,7 @@ func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls
conns: conns,
factory: f,
}
t.ServiceWithError = util.AsServiceWithError(t.serve)
t.ServiceWithError = util.AsServiceWithError(t.serve, t.String())
return t
}

View File

@@ -7,8 +7,8 @@
package connections
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/url"
@@ -30,6 +30,7 @@ import (
_ "github.com/syncthing/syncthing/lib/pmp"
_ "github.com/syncthing/syncthing/lib/upnp"
"github.com/pkg/errors"
"github.com/thejerf/suture"
"golang.org/x/time/rate"
)
@@ -185,18 +186,24 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
// the common handling regardless of whether the connection was
// incoming or outgoing.
service.Add(util.AsService(service.connect))
service.Add(util.AsService(service.handle))
service.Add(util.AsService(service.connect, fmt.Sprintf("%s/connect", service)))
service.Add(util.AsService(service.handle, fmt.Sprintf("%s/handle", service)))
service.Add(service.listenerSupervisor)
return service
}
func (s *service) handle(stop chan struct{}) {
func (s *service) Stop() {
s.cfg.Unsubscribe(s.limiter)
s.cfg.Unsubscribe(s)
s.Supervisor.Stop()
}
func (s *service) handle(ctx context.Context) {
var c internalConn
for {
select {
case <-stop:
case <-ctx.Done():
return
case c = <-s.conns:
}
@@ -324,7 +331,7 @@ func (s *service) handle(stop chan struct{}) {
}
}
func (s *service) connect(stop chan struct{}) {
func (s *service) connect(ctx context.Context) {
nextDial := make(map[string]time.Time)
// Used as delay for the first few connection attempts, increases
@@ -441,7 +448,7 @@ func (s *service) connect(stop chan struct{}) {
continue
}
dialer := dialerFactory.New(s.cfg, s.tlsCfg)
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg)
nextDial[nextDialKey] = now.Add(dialer.RedialFrequency())
// For LAN addresses, increase the priority so that we
@@ -462,7 +469,7 @@ func (s *service) connect(stop chan struct{}) {
})
}
conn, ok := s.dialParallel(deviceCfg.DeviceID, dialTargets)
conn, ok := s.dialParallel(ctx, deviceCfg.DeviceID, dialTargets)
if ok {
s.conns <- conn
}
@@ -480,7 +487,7 @@ func (s *service) connect(stop chan struct{}) {
select {
case <-time.After(sleep):
case <-stop:
case <-ctx.Done():
return
}
}
@@ -581,7 +588,7 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
s.listenersMut.Lock()
seen := make(map[string]struct{})
for _, addr := range config.Wrap("", to, s.evLogger).ListenAddresses() {
for _, addr := range to.Options.ListenAddresses() {
if addr == "" {
// We can get an empty address if there is an empty listener
// element in the config, indicating no listeners should be
@@ -700,6 +707,10 @@ func (s *service) ConnectionStatus() map[string]ConnectionStatusEntry {
}
func (s *service) setConnectionStatus(address string, err error) {
if errors.Cause(err) != context.Canceled {
return
}
status := ConnectionStatusEntry{When: time.Now().UTC().Truncate(time.Second)}
if err != nil {
errStr := err.Error()
@@ -827,7 +838,7 @@ func IsAllowedNetwork(host string, allowed []string) bool {
return false
}
func (s *service) dialParallel(deviceID protocol.DeviceID, dialTargets []dialTarget) (internalConn, bool) {
func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, dialTargets []dialTarget) (internalConn, bool) {
// Group targets into buckets by priority
dialTargetBuckets := make(map[int][]dialTarget, len(dialTargets))
for _, tgt := range dialTargets {
@@ -850,7 +861,7 @@ func (s *service) dialParallel(deviceID protocol.DeviceID, dialTargets []dialTar
for _, tgt := range tgts {
wg.Add(1)
go func(tgt dialTarget) {
conn, err := tgt.Dial()
conn, err := tgt.Dial(ctx)
if err == nil {
// Closes the connection on error
err = s.validateIdentity(conn, deviceID)

View File

@@ -7,6 +7,7 @@
package connections
import (
"context"
"crypto/tls"
"fmt"
"io"
@@ -146,15 +147,25 @@ func (c internalConn) String() string {
}
type dialerFactory interface {
New(config.Wrapper, *tls.Config) genericDialer
New(config.OptionsConfiguration, *tls.Config) genericDialer
Priority() int
AlwaysWAN() bool
Valid(config.Configuration) error
String() string
}
type commonDialer struct {
trafficClass int
reconnectInterval time.Duration
tlsCfg *tls.Config
}
func (d *commonDialer) RedialFrequency() time.Duration {
return d.reconnectInterval
}
type genericDialer interface {
Dial(protocol.DeviceID, *url.URL) (internalConn, error)
Dial(context.Context, protocol.DeviceID, *url.URL) (internalConn, error)
RedialFrequency() time.Duration
}
@@ -213,7 +224,7 @@ type dialTarget struct {
deviceID protocol.DeviceID
}
func (t dialTarget) Dial() (internalConn, error) {
func (t dialTarget) Dial(ctx context.Context) (internalConn, error) {
l.Debugln("dialing", t.deviceID, t.uri, "prio", t.priority)
return t.dialer.Dial(t.deviceID, t.uri)
return t.dialer.Dial(ctx, t.deviceID, t.uri)
}

View File

@@ -7,6 +7,7 @@
package connections
import (
"context"
"crypto/tls"
"net/url"
"time"
@@ -26,14 +27,15 @@ func init() {
}
type tcpDialer struct {
cfg config.Wrapper
tlsCfg *tls.Config
commonDialer
}
func (d *tcpDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, error) {
func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL) (internalConn, error) {
uri = fixupPort(uri, config.DefaultTCPPort)
conn, err := dialer.DialTimeout(uri.Scheme, uri.Host, 10*time.Second)
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
conn, err := dialer.DialContext(timeoutCtx, uri.Scheme, uri.Host)
if err != nil {
return internalConn{}, err
}
@@ -43,7 +45,7 @@ func (d *tcpDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, error
l.Debugln("Dial (BEP/tcp): setting tcp options:", err)
}
err = dialer.SetTrafficClass(conn, d.cfg.Options().TrafficClass)
err = dialer.SetTrafficClass(conn, d.trafficClass)
if err != nil {
l.Debugln("Dial (BEP/tcp): setting traffic class:", err)
}
@@ -58,17 +60,14 @@ func (d *tcpDialer) Dial(_ protocol.DeviceID, uri *url.URL) (internalConn, error
return internalConn{tc, connTypeTCPClient, tcpPriority}, nil
}
func (d *tcpDialer) RedialFrequency() time.Duration {
return time.Duration(d.cfg.Options().ReconnectIntervalS) * time.Second
}
type tcpDialerFactory struct{}
func (tcpDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer {
return &tcpDialer{
cfg: cfg,
tlsCfg: tlsCfg,
}
func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
return &tcpDialer{commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
}}
}
func (tcpDialerFactory) Priority() int {

View File

@@ -7,6 +7,7 @@
package connections
import (
"context"
"crypto/tls"
"net"
"net/url"
@@ -42,7 +43,7 @@ type tcpListener struct {
mut sync.RWMutex
}
func (t *tcpListener) serve(stop chan struct{}) error {
func (t *tcpListener) serve(ctx context.Context) error {
tcaddr, err := net.ResolveTCPAddr(t.uri.Scheme, t.uri.Host)
if err != nil {
l.Infoln("Listen (BEP/tcp):", err)
@@ -76,7 +77,7 @@ func (t *tcpListener) serve(stop chan struct{}) error {
listener.SetDeadline(time.Now().Add(time.Second))
conn, err := listener.Accept()
select {
case <-stop:
case <-ctx.Done():
if err == nil {
conn.Close()
}
@@ -183,7 +184,7 @@ func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.C
natService: natService,
factory: f,
}
l.ServiceWithError = util.AsServiceWithError(l.serve)
l.ServiceWithError = util.AsServiceWithError(l.serve, l.String())
return l
}

170
lib/db/backend/backend.go Normal file
View File

@@ -0,0 +1,170 @@
// Copyright (C) 2019 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 backend
import (
"sync"
)
// The Reader interface specifies the read-only operations available on the
// main database and on read-only transactions (snapshots). Note that when
// called directly on the database handle these operations may take implicit
// transactions and performance may suffer.
type Reader interface {
Get(key []byte) ([]byte, error)
NewPrefixIterator(prefix []byte) (Iterator, error)
NewRangeIterator(first, last []byte) (Iterator, error)
}
// The Writer interface specifies the mutating operations available on the
// main database and on writable transactions. Note that when called
// directly on the database handle these operations may take implicit
// transactions and performance may suffer.
type Writer interface {
Put(key, val []byte) error
Delete(key []byte) error
}
// The ReadTransaction interface specifies the operations on read-only
// transactions. Every ReadTransaction must be released when no longer
// required.
type ReadTransaction interface {
Reader
Release()
}
// The WriteTransaction interface specifies the operations on writable
// transactions. Every WriteTransaction must be either committed or released
// (i.e., discarded) when no longer required. No further operations must be
// performed after release or commit (regardless of whether commit succeeded),
// with one exception -- it's fine to release an already committed or released
// transaction.
//
// A Checkpoint is a potential partial commit of the transaction so far, for
// purposes of saving memory when transactions are in-RAM. Note that
// transactions may be checkpointed *anyway* even if this is not called, due to
// resource constraints, but this gives you a chance to decide when.
type WriteTransaction interface {
ReadTransaction
Writer
Checkpoint() error
Commit() error
}
// The Iterator interface specifies the operations available on iterators
// returned by NewPrefixIterator and NewRangeIterator. The iterator pattern
// is to loop while Next returns true, then check Error after the loop. Next
// will return false when iteration is complete (Error() == nil) or when
// there is an error preventing iteration, which is then returned by
// Error(). For example:
//
// it, err := db.NewPrefixIterator(nil)
// if err != nil {
// // problem preventing iteration
// }
// defer it.Release()
// for it.Next() {
// // ...
// }
// if err := it.Error(); err != nil {
// // there was a database problem while iterating
// }
//
// An iterator must be Released when no longer required. The Error method
// can be called either before or after Release with the same results. If an
// iterator was created in a transaction (whether read-only or write) it
// must be released before the transaction is released (or committed).
type Iterator interface {
Next() bool
Key() []byte
Value() []byte
Error() error
Release()
}
// The Backend interface represents the main database handle. It supports
// both read/write operations and opening read-only or writable
// transactions. Depending on the actual implementation, individual
// read/write operations may be implicitly wrapped in transactions, making
// them perform quite badly when used repeatedly. For bulk operations,
// consider always using a transaction of the appropriate type. The
// transaction isolation level is "read committed" - there are no dirty
// reads.
type Backend interface {
Reader
Writer
NewReadTransaction() (ReadTransaction, error)
NewWriteTransaction() (WriteTransaction, error)
Close() error
}
type Tuning int
const (
// N.b. these constants must match those in lib/config.Tuning!
TuningAuto Tuning = iota
TuningSmall
TuningLarge
)
func Open(path string, tuning Tuning) (Backend, error) {
return OpenLevelDB(path, tuning)
}
func OpenMemory() Backend {
return OpenLevelDBMemory()
}
type errClosed struct{}
func (errClosed) Error() string { return "database is closed" }
type errNotFound struct{}
func (errNotFound) Error() string { return "key not found" }
func IsClosed(err error) bool {
if _, ok := err.(errClosed); ok {
return true
}
if _, ok := err.(*errClosed); ok {
return true
}
return false
}
func IsNotFound(err error) bool {
if _, ok := err.(errNotFound); ok {
return true
}
if _, ok := err.(*errNotFound); ok {
return true
}
return false
}
// releaser manages counting on top of a waitgroup
type releaser struct {
wg *sync.WaitGroup
once *sync.Once
}
func newReleaser(wg *sync.WaitGroup) *releaser {
wg.Add(1)
return &releaser{
wg: wg,
once: new(sync.Once),
}
}
func (r releaser) Release() {
// We use the Once because we may get called multiple times from
// Commit() and deferred Release().
r.once.Do(func() {
r.wg.Done()
})
}

View File

@@ -0,0 +1,76 @@
// Copyright (C) 2019 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 backend
import "testing"
// testBackendBehavior is the generic test suite that must be fulfilled by
// every backend implementation. It should be called by each implementation
// as (part of) their test suite.
func testBackendBehavior(t *testing.T, open func() Backend) {
t.Run("WriteIsolation", func(t *testing.T) { testWriteIsolation(t, open) })
t.Run("DeleteNonexisten", func(t *testing.T) { testDeleteNonexistent(t, open) })
t.Run("IteratorClosedDB", func(t *testing.T) { testIteratorClosedDB(t, open) })
}
func testWriteIsolation(t *testing.T, open func() Backend) {
// Values written during a transaction should not be read back, our
// updateGlobal depends on this.
db := open()
defer db.Close()
// Sanity check
_ = db.Put([]byte("a"), []byte("a"))
v, _ := db.Get([]byte("a"))
if string(v) != "a" {
t.Fatal("read back should work")
}
// Now in a transaction we should still see the old value
tx, _ := db.NewWriteTransaction()
defer tx.Release()
_ = tx.Put([]byte("a"), []byte("b"))
v, _ = tx.Get([]byte("a"))
if string(v) != "a" {
t.Fatal("read in transaction should read the old value")
}
}
func testDeleteNonexistent(t *testing.T, open func() Backend) {
// Deleting a non-existent key is not an error
db := open()
defer db.Close()
err := db.Delete([]byte("a"))
if err != nil {
t.Error(err)
}
}
// Either creating the iterator or the .Error() method of the returned iterator
// should return an error and IsClosed(err) == true.
func testIteratorClosedDB(t *testing.T, open func() Backend) {
db := open()
_ = db.Put([]byte("a"), []byte("a"))
db.Close()
it, err := db.NewPrefixIterator(nil)
if err != nil {
if !IsClosed(err) {
t.Error("NewPrefixIterator: IsClosed(err) == false:", err)
}
return
}
it.Next()
if err := it.Error(); !IsClosed(err) {
t.Error("Next: IsClosed(err) == false:", err)
}
}

15
lib/db/backend/debug.go Normal file
View File

@@ -0,0 +1,15 @@
// Copyright (C) 2019 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 backend
import (
"github.com/syncthing/syncthing/lib/logger"
)
var (
l = logger.DefaultLogger.NewFacility("backend", "The database backend")
)

View File

@@ -0,0 +1,182 @@
// Copyright (C) 2018 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 backend
import (
"sync"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/util"
)
const (
// Never flush transactions smaller than this, even on Checkpoint()
dbFlushBatchMin = 1 << MiB
// Once a transaction reaches this size, flush it unconditionally.
dbFlushBatchMax = 128 << MiB
)
// leveldbBackend implements Backend on top of a leveldb
type leveldbBackend struct {
ldb *leveldb.DB
closeWG sync.WaitGroup
}
func (b *leveldbBackend) NewReadTransaction() (ReadTransaction, error) {
return b.newSnapshot()
}
func (b *leveldbBackend) newSnapshot() (leveldbSnapshot, error) {
snap, err := b.ldb.GetSnapshot()
if err != nil {
return leveldbSnapshot{}, wrapLeveldbErr(err)
}
return leveldbSnapshot{
snap: snap,
rel: newReleaser(&b.closeWG),
}, nil
}
func (b *leveldbBackend) NewWriteTransaction() (WriteTransaction, error) {
snap, err := b.newSnapshot()
if err != nil {
return nil, err // already wrapped
}
return &leveldbTransaction{
leveldbSnapshot: snap,
ldb: b.ldb,
batch: new(leveldb.Batch),
rel: newReleaser(&b.closeWG),
}, nil
}
func (b *leveldbBackend) Close() error {
b.closeWG.Wait()
return wrapLeveldbErr(b.ldb.Close())
}
func (b *leveldbBackend) Get(key []byte) ([]byte, error) {
val, err := b.ldb.Get(key, nil)
return val, wrapLeveldbErr(err)
}
func (b *leveldbBackend) NewPrefixIterator(prefix []byte) (Iterator, error) {
return &leveldbIterator{b.ldb.NewIterator(util.BytesPrefix(prefix), nil)}, nil
}
func (b *leveldbBackend) NewRangeIterator(first, last []byte) (Iterator, error) {
return &leveldbIterator{b.ldb.NewIterator(&util.Range{Start: first, Limit: last}, nil)}, nil
}
func (b *leveldbBackend) Put(key, val []byte) error {
return wrapLeveldbErr(b.ldb.Put(key, val, nil))
}
func (b *leveldbBackend) Delete(key []byte) error {
return wrapLeveldbErr(b.ldb.Delete(key, nil))
}
// leveldbSnapshot implements backend.ReadTransaction
type leveldbSnapshot struct {
snap *leveldb.Snapshot
rel *releaser
}
func (l leveldbSnapshot) Get(key []byte) ([]byte, error) {
val, err := l.snap.Get(key, nil)
return val, wrapLeveldbErr(err)
}
func (l leveldbSnapshot) NewPrefixIterator(prefix []byte) (Iterator, error) {
return l.snap.NewIterator(util.BytesPrefix(prefix), nil), nil
}
func (l leveldbSnapshot) NewRangeIterator(first, last []byte) (Iterator, error) {
return l.snap.NewIterator(&util.Range{Start: first, Limit: last}, nil), nil
}
func (l leveldbSnapshot) Release() {
l.snap.Release()
l.rel.Release()
}
// leveldbTransaction implements backend.WriteTransaction using a batch (not
// an actual leveldb transaction)
type leveldbTransaction struct {
leveldbSnapshot
ldb *leveldb.DB
batch *leveldb.Batch
rel *releaser
}
func (t *leveldbTransaction) Delete(key []byte) error {
t.batch.Delete(key)
return t.checkFlush(dbFlushBatchMax)
}
func (t *leveldbTransaction) Put(key, val []byte) error {
t.batch.Put(key, val)
return t.checkFlush(dbFlushBatchMax)
}
func (t *leveldbTransaction) Checkpoint() error {
return t.checkFlush(dbFlushBatchMin)
}
func (t *leveldbTransaction) Commit() error {
err := wrapLeveldbErr(t.flush())
t.leveldbSnapshot.Release()
t.rel.Release()
return err
}
func (t *leveldbTransaction) Release() {
t.leveldbSnapshot.Release()
t.rel.Release()
}
// checkFlush flushes and resets the batch if its size exceeds the given size.
func (t *leveldbTransaction) checkFlush(size int) error {
if len(t.batch.Dump()) < size {
return nil
}
return t.flush()
}
func (t *leveldbTransaction) flush() error {
if t.batch.Len() == 0 {
return nil
}
if err := t.ldb.Write(t.batch, nil); err != nil {
return wrapLeveldbErr(err)
}
t.batch.Reset()
return nil
}
type leveldbIterator struct {
iterator.Iterator
}
func (it *leveldbIterator) Error() error {
return wrapLeveldbErr(it.Iterator.Error())
}
// wrapLeveldbErr wraps errors so that the backend package can recognize them
func wrapLeveldbErr(err error) error {
if err == nil {
return nil
}
if err == leveldb.ErrClosed {
return errClosed{}
}
if err == leveldb.ErrNotFound {
return errNotFound{}
}
return err
}

View File

@@ -0,0 +1,226 @@
// Copyright (C) 2018 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 backend
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
)
const (
dbMaxOpenFiles = 100
// A large database is > 200 MiB. It's a mostly arbitrary value, but
// it's also the case that each file is 2 MiB by default and when we
// have dbMaxOpenFiles of them we will need to start thrashing fd:s.
// Switching to large database settings causes larger files to be used
// when compacting, reducing the number.
dbLargeThreshold = dbMaxOpenFiles * (2 << MiB)
KiB = 10
MiB = 20
)
// Open attempts to open the database at the given location, and runs
// recovery on it if opening fails. Worst case, if recovery is not possible,
// the database is erased and created from scratch.
func OpenLevelDB(location string, tuning Tuning) (Backend, error) {
opts := optsFor(location, tuning)
ldb, err := open(location, opts)
if err != nil {
return nil, err
}
return &leveldbBackend{ldb: ldb}, nil
}
// OpenRO attempts to open the database at the given location, read only.
func OpenLevelDBRO(location string) (Backend, error) {
opts := &opt.Options{
OpenFilesCacheCapacity: dbMaxOpenFiles,
ReadOnly: true,
}
ldb, err := open(location, opts)
if err != nil {
return nil, err
}
return &leveldbBackend{ldb: ldb}, nil
}
// OpenMemory returns a new Backend referencing an in-memory database.
func OpenLevelDBMemory() Backend {
ldb, _ := leveldb.Open(storage.NewMemStorage(), nil)
return &leveldbBackend{ldb: ldb}
}
// optsFor returns the database options to use when opening a database with
// the given location and tuning. Settings can be overridden by debug
// environment variables.
func optsFor(location string, tuning Tuning) *opt.Options {
large := false
switch tuning {
case TuningLarge:
large = true
case TuningAuto:
large = dbIsLarge(location)
}
var (
// Set defaults used for small databases.
defaultBlockCacheCapacity = 0 // 0 means let leveldb use default
defaultBlockSize = 0
defaultCompactionTableSize = 0
defaultCompactionTableSizeMultiplier = 0
defaultWriteBuffer = 16 << MiB // increased from leveldb default of 4 MiB
defaultCompactionL0Trigger = opt.DefaultCompactionL0Trigger // explicit because we use it as base for other stuff
)
if large {
// Change the parameters for better throughput at the price of some
// RAM and larger files. This results in larger batches of writes
// and compaction at a lower frequency.
l.Infoln("Using large-database tuning")
defaultBlockCacheCapacity = 64 << MiB
defaultBlockSize = 64 << KiB
defaultCompactionTableSize = 16 << MiB
defaultCompactionTableSizeMultiplier = 20 // 2.0 after division by ten
defaultWriteBuffer = 64 << MiB
defaultCompactionL0Trigger = 8 // number of l0 files
}
opts := &opt.Options{
BlockCacheCapacity: debugEnvValue("BlockCacheCapacity", defaultBlockCacheCapacity),
BlockCacheEvictRemoved: debugEnvValue("BlockCacheEvictRemoved", 0) != 0,
BlockRestartInterval: debugEnvValue("BlockRestartInterval", 0),
BlockSize: debugEnvValue("BlockSize", defaultBlockSize),
CompactionExpandLimitFactor: debugEnvValue("CompactionExpandLimitFactor", 0),
CompactionGPOverlapsFactor: debugEnvValue("CompactionGPOverlapsFactor", 0),
CompactionL0Trigger: debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger),
CompactionSourceLimitFactor: debugEnvValue("CompactionSourceLimitFactor", 0),
CompactionTableSize: debugEnvValue("CompactionTableSize", defaultCompactionTableSize),
CompactionTableSizeMultiplier: float64(debugEnvValue("CompactionTableSizeMultiplier", defaultCompactionTableSizeMultiplier)) / 10.0,
CompactionTotalSize: debugEnvValue("CompactionTotalSize", 0),
CompactionTotalSizeMultiplier: float64(debugEnvValue("CompactionTotalSizeMultiplier", 0)) / 10.0,
DisableBufferPool: debugEnvValue("DisableBufferPool", 0) != 0,
DisableBlockCache: debugEnvValue("DisableBlockCache", 0) != 0,
DisableCompactionBackoff: debugEnvValue("DisableCompactionBackoff", 0) != 0,
DisableLargeBatchTransaction: debugEnvValue("DisableLargeBatchTransaction", 0) != 0,
NoSync: debugEnvValue("NoSync", 0) != 0,
NoWriteMerge: debugEnvValue("NoWriteMerge", 0) != 0,
OpenFilesCacheCapacity: debugEnvValue("OpenFilesCacheCapacity", dbMaxOpenFiles),
WriteBuffer: debugEnvValue("WriteBuffer", defaultWriteBuffer),
// The write slowdown and pause can be overridden, but even if they
// are not and the compaction trigger is overridden we need to
// adjust so that we don't pause writes for L0 compaction before we
// even *start* L0 compaction...
WriteL0SlowdownTrigger: debugEnvValue("WriteL0SlowdownTrigger", 2*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)),
WriteL0PauseTrigger: debugEnvValue("WriteL0SlowdownTrigger", 3*debugEnvValue("CompactionL0Trigger", defaultCompactionL0Trigger)),
}
return opts
}
func open(location string, opts *opt.Options) (*leveldb.DB, error) {
db, err := leveldb.OpenFile(location, opts)
if leveldbIsCorrupted(err) {
db, err = leveldb.RecoverFile(location, opts)
}
if leveldbIsCorrupted(err) {
// The database is corrupted, and we've tried to recover it but it
// didn't work. At this point there isn't much to do beyond dropping
// the database and reindexing...
l.Infoln("Database corruption detected, unable to recover. Reinitializing...")
if err := os.RemoveAll(location); err != nil {
return nil, errorSuggestion{err, "failed to delete corrupted database"}
}
db, err = leveldb.OpenFile(location, opts)
}
if err != nil {
return nil, errorSuggestion{err, "is another instance of Syncthing running?"}
}
if debugEnvValue("CompactEverything", 0) != 0 {
if err := db.CompactRange(util.Range{}); err != nil {
l.Warnln("Compacting database:", err)
}
}
return db, nil
}
func debugEnvValue(key string, def int) int {
v, err := strconv.ParseInt(os.Getenv("STDEBUG_"+key), 10, 63)
if err != nil {
return def
}
return int(v)
}
// A "better" version of leveldb's errors.IsCorrupted.
func leveldbIsCorrupted(err error) bool {
switch {
case err == nil:
return false
case errors.IsCorrupted(err):
return true
case strings.Contains(err.Error(), "corrupted"):
return true
}
return false
}
// dbIsLarge returns whether the estimated size of the database at location
// is large enough to warrant optimization for large databases.
func dbIsLarge(location string) bool {
if ^uint(0)>>63 == 0 {
// We're compiled for a 32 bit architecture. We've seen trouble with
// large settings there.
// (https://forum.syncthing.net/t/many-small-ldb-files-with-database-tuning/13842)
return false
}
dir, err := os.Open(location)
if err != nil {
return false
}
fis, err := dir.Readdir(-1)
if err != nil {
return false
}
var size int64
for _, fi := range fis {
if fi.Name() == "LOG" {
// don't count the size
continue
}
size += fi.Size()
}
return size > dbLargeThreshold
}
type errorSuggestion struct {
inner error
suggestion string
}
func (e errorSuggestion) Error() string {
return fmt.Sprintf("%s (%s)", e.inner.Error(), e.suggestion)
}

View File

@@ -0,0 +1,13 @@
// Copyright (C) 2019 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 backend
import "testing"
func TestLevelDBBackendBehavior(t *testing.T) {
testBackendBehavior(t, OpenLevelDBMemory)
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -40,7 +41,7 @@ func lazyInitBenchFiles() {
func getBenchFileSet() (*db.Lowlevel, *db.FileSet) {
lazyInitBenchFiles()
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
benchS := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
replace(benchS, remoteDevice0, files)
replace(benchS, protocol.LocalDeviceID, firstHalf)
@@ -49,7 +50,7 @@ func getBenchFileSet() (*db.Lowlevel, *db.FileSet) {
}
func BenchmarkReplaceAll(b *testing.B) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
defer ldb.Close()
b.ResetTimer()
@@ -157,7 +158,7 @@ func BenchmarkNeedHalf(b *testing.B) {
}
func BenchmarkNeedHalfRemote(b *testing.B) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
defer ldb.Close()
fset := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
replace(fset, remoteDevice0, firstHalf)

View File

@@ -11,14 +11,12 @@ import (
"fmt"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syndtr/goleveldb/leveldb/util"
)
var blockFinder *BlockFinder
type BlockFinder struct {
db *instance
db *Lowlevel
}
func NewBlockFinder(db *Lowlevel) *BlockFinder {
@@ -27,7 +25,7 @@ func NewBlockFinder(db *Lowlevel) *BlockFinder {
}
return &BlockFinder{
db: newInstance(db),
db: db,
}
}
@@ -41,13 +39,22 @@ func (f *BlockFinder) String() string {
// reason. The iterator finally returns the result, whether or not a
// satisfying block was eventually found.
func (f *BlockFinder) Iterate(folders []string, hash []byte, iterFn func(string, string, int32) bool) bool {
t := f.db.newReadOnlyTransaction()
t, err := f.db.newReadOnlyTransaction()
if err != nil {
return false
}
defer t.close()
var key []byte
for _, folder := range folders {
key = f.db.keyer.GenerateBlockMapKey(key, []byte(folder), hash, nil)
iter := t.NewIterator(util.BytesPrefix(key), nil)
key, err = f.db.keyer.GenerateBlockMapKey(key, []byte(folder), hash, nil)
if err != nil {
return false
}
iter, err := t.NewPrefixIterator(key)
if err != nil {
return false
}
for iter.Next() && iter.Error() == nil {
file := string(f.db.keyer.NameFromBlockMapKey(iter.Key()))

View File

@@ -10,23 +10,10 @@ import (
"encoding/binary"
"testing"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syndtr/goleveldb/leveldb/util"
)
func genBlocks(n int) []protocol.BlockInfo {
b := make([]protocol.BlockInfo, n)
for i := range b {
h := make([]byte, 32)
for j := range h {
h[j] = byte(i + j)
}
b[i].Size = int32(i)
b[i].Hash = h
}
return b
}
var f1, f2, f3 protocol.FileInfo
var folders = []string{"folder1", "folder2"}
@@ -49,21 +36,27 @@ func init() {
}
}
func setup() (*instance, *BlockFinder) {
func setup() (*Lowlevel, *BlockFinder) {
// Setup
db := OpenMemory()
return newInstance(db), NewBlockFinder(db)
db := NewLowlevel(backend.OpenMemory())
return db, NewBlockFinder(db)
}
func dbEmpty(db *instance) bool {
iter := db.NewIterator(util.BytesPrefix([]byte{KeyTypeBlock}), nil)
func dbEmpty(db *Lowlevel) bool {
iter, err := db.NewPrefixIterator([]byte{KeyTypeBlock})
if err != nil {
panic(err)
}
defer iter.Release()
return !iter.Next()
}
func addToBlockMap(db *instance, folder []byte, fs []protocol.FileInfo) {
t := db.newReadWriteTransaction()
func addToBlockMap(db *Lowlevel, folder []byte, fs []protocol.FileInfo) error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var keyBuf []byte
@@ -73,15 +66,24 @@ func addToBlockMap(db *instance, folder []byte, fs []protocol.FileInfo) {
name := []byte(f.Name)
for i, block := range f.Blocks {
binary.BigEndian.PutUint32(blockBuf, uint32(i))
keyBuf = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
t.Put(keyBuf, blockBuf)
keyBuf, err = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
if err != nil {
return err
}
if err := t.Put(keyBuf, blockBuf); err != nil {
return err
}
}
}
}
return t.commit()
}
func discardFromBlockMap(db *instance, folder []byte, fs []protocol.FileInfo) {
t := db.newReadWriteTransaction()
func discardFromBlockMap(db *Lowlevel, folder []byte, fs []protocol.FileInfo) error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var keyBuf []byte
@@ -89,11 +91,17 @@ func discardFromBlockMap(db *instance, folder []byte, fs []protocol.FileInfo) {
if !ef.IsDirectory() && !ef.IsDeleted() && !ef.IsInvalid() {
name := []byte(ef.Name)
for _, block := range ef.Blocks {
keyBuf = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
t.Delete(keyBuf)
keyBuf, err = t.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
if err != nil {
return err
}
if err := t.Delete(keyBuf); err != nil {
return err
}
}
}
}
return t.commit()
}
func TestBlockMapAddUpdateWipe(t *testing.T) {
@@ -107,7 +115,9 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
f3.Type = protocol.FileInfoTypeDirectory
addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3})
if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil {
t.Fatal(err)
}
f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
if folder != "folder1" || file != "f1" || index != 0 {
@@ -128,12 +138,16 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
return true
})
discardFromBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3})
if err := discardFromBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil {
t.Fatal(err)
}
f1.Deleted = true
f2.LocalFlags = protocol.FlagLocalMustRescan // one of the invalid markers
addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3})
if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2, f3}); err != nil {
t.Fatal(err)
}
f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
t.Fatal("Unexpected block")
@@ -152,14 +166,18 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
return true
})
db.dropFolder(folder)
if err := db.dropFolder(folder); err != nil {
t.Fatal(err)
}
if !dbEmpty(db) {
t.Fatal("db not empty")
}
// Should not add
addToBlockMap(db, folder, []protocol.FileInfo{f1, f2})
if err := addToBlockMap(db, folder, []protocol.FileInfo{f1, f2}); err != nil {
t.Fatal(err)
}
if !dbEmpty(db) {
t.Fatal("db not empty")
@@ -179,8 +197,12 @@ func TestBlockFinderLookup(t *testing.T) {
folder1 := []byte("folder1")
folder2 := []byte("folder2")
addToBlockMap(db, folder1, []protocol.FileInfo{f1})
addToBlockMap(db, folder2, []protocol.FileInfo{f1})
if err := addToBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil {
t.Fatal(err)
}
if err := addToBlockMap(db, folder2, []protocol.FileInfo{f1}); err != nil {
t.Fatal(err)
}
counter := 0
f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
@@ -204,11 +226,15 @@ func TestBlockFinderLookup(t *testing.T) {
t.Fatal("Incorrect count", counter)
}
discardFromBlockMap(db, folder1, []protocol.FileInfo{f1})
if err := discardFromBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil {
t.Fatal(err)
}
f1.Deleted = true
addToBlockMap(db, folder1, []protocol.FileInfo{f1})
if err := addToBlockMap(db, folder1, []protocol.FileInfo{f1}); err != nil {
t.Fatal(err)
}
counter = 0
f.Iterate(folders, f1.Blocks[0].Hash, func(folder, file string, index int32) bool {

View File

@@ -1,237 +0,0 @@
// Copyright (C) 2014 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/.
// this is a really tedious test for an old issue
// +build ignore
package db_test
import (
"crypto/rand"
"log"
"os"
"testing"
"time"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
)
var keys [][]byte
func init() {
for i := 0; i < nItems; i++ {
keys = append(keys, randomData(1))
}
}
const nItems = 10000
func randomData(prefix byte) []byte {
data := make([]byte, 1+32+64+32)
_, err := rand.Reader.Read(data)
if err != nil {
panic(err)
}
return append([]byte{prefix}, data...)
}
func setItems(db *leveldb.DB) error {
batch := new(leveldb.Batch)
for _, k1 := range keys {
k2 := randomData(2)
// k2 -> data
batch.Put(k2, randomData(42))
// k1 -> k2
batch.Put(k1, k2)
}
if testing.Verbose() {
log.Printf("batch write (set) %p", batch)
}
return db.Write(batch, nil)
}
func clearItems(db *leveldb.DB) error {
snap, err := db.GetSnapshot()
if err != nil {
return err
}
defer snap.Release()
// Iterate over k2
it := snap.NewIterator(util.BytesPrefix([]byte{1}), nil)
defer it.Release()
batch := new(leveldb.Batch)
for it.Next() {
k1 := it.Key()
k2 := it.Value()
// k2 should exist
_, err := snap.Get(k2, nil)
if err != nil {
return err
}
// Delete the k1 => k2 mapping first
batch.Delete(k1)
// Then the k2 => data mapping
batch.Delete(k2)
}
if testing.Verbose() {
log.Printf("batch write (clear) %p", batch)
}
return db.Write(batch, nil)
}
func scanItems(db *leveldb.DB) error {
snap, err := db.GetSnapshot()
if testing.Verbose() {
log.Printf("snap create %p", snap)
}
if err != nil {
return err
}
defer func() {
if testing.Verbose() {
log.Printf("snap release %p", snap)
}
snap.Release()
}()
// Iterate from the start of k2 space to the end
it := snap.NewIterator(util.BytesPrefix([]byte{1}), nil)
defer it.Release()
i := 0
for it.Next() {
// k2 => k1 => data
k1 := it.Key()
k2 := it.Value()
_, err := snap.Get(k2, nil)
if err != nil {
log.Printf("k1: %x", k1)
log.Printf("k2: %x (missing)", k2)
return err
}
i++
}
if testing.Verbose() {
log.Println("scanned", i)
}
return nil
}
func TestConcurrentSetClear(t *testing.T) {
if testing.Short() {
return
}
dur := 30 * time.Second
t0 := time.Now()
wg := sync.NewWaitGroup()
os.RemoveAll("testdata/concurrent-set-clear.db")
db, err := leveldb.OpenFile("testdata/concurrent-set-clear.db", &opt.Options{OpenFilesCacheCapacity: 10})
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll("testdata/concurrent-set-clear.db")
errChan := make(chan error, 3)
wg.Add(1)
go func() {
defer wg.Done()
for time.Since(t0) < dur {
if err := setItems(db); err != nil {
errChan <- err
return
}
if err := clearItems(db); err != nil {
errChan <- err
return
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for time.Since(t0) < dur {
if err := scanItems(db); err != nil {
errChan <- err
return
}
}
}()
go func() {
wg.Wait()
errChan <- nil
}()
err = <-errChan
if err != nil {
t.Error(err)
}
db.Close()
}
func TestConcurrentSetOnly(t *testing.T) {
if testing.Short() {
return
}
dur := 30 * time.Second
t0 := time.Now()
wg := sync.NewWaitGroup()
os.RemoveAll("testdata/concurrent-set-only.db")
db, err := leveldb.OpenFile("testdata/concurrent-set-only.db", &opt.Options{OpenFilesCacheCapacity: 10})
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll("testdata/concurrent-set-only.db")
errChan := make(chan error, 3)
wg.Add(1)
go func() {
defer wg.Done()
for time.Since(t0) < dur {
if err := setItems(db); err != nil {
errChan <- err
return
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for time.Since(t0) < dur {
if err := scanItems(db); err != nil {
errChan <- err
return
}
}
}()
go func() {
wg.Wait()
errChan <- nil
}()
err = <-errChan
if err != nil {
t.Error(err)
}
}

View File

@@ -9,17 +9,33 @@ package db
import (
"testing"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
func genBlocks(n int) []protocol.BlockInfo {
b := make([]protocol.BlockInfo, n)
for i := range b {
h := make([]byte, 32)
for j := range h {
h[j] = byte(i + j)
}
b[i].Size = int32(i)
b[i].Hash = h
}
return b
}
func TestIgnoredFiles(t *testing.T) {
ldb, err := openJSONS("testdata/v0.14.48-ignoredfiles.db.jsons")
if err != nil {
t.Fatal(err)
}
db := NewLowlevel(ldb, "<memory>")
UpdateSchema(db)
db := NewLowlevel(ldb)
if err := UpdateSchema(db); err != nil {
t.Fatal(err)
}
fs := NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
@@ -142,25 +158,35 @@ func TestUpdate0to3(t *testing.T) {
t.Fatal(err)
}
db := newInstance(NewLowlevel(ldb, "<memory>"))
db := NewLowlevel(ldb)
updater := schemaUpdater{db}
folder := []byte(update0to3Folder)
updater.updateSchema0to1()
if err := updater.updateSchema0to1(); err != nil {
t.Fatal(err)
}
if _, ok := db.getFileDirty(folder, protocol.LocalDeviceID[:], []byte(slashPrefixed)); ok {
if _, ok, err := db.getFileDirty(folder, protocol.LocalDeviceID[:], []byte(slashPrefixed)); err != nil {
t.Fatal(err)
} else if ok {
t.Error("File prefixed by '/' was not removed during transition to schema 1")
}
if _, err := db.Get(db.keyer.GenerateGlobalVersionKey(nil, folder, []byte(invalid)), nil); err != nil {
key, err := db.keyer.GenerateGlobalVersionKey(nil, folder, []byte(invalid))
if err != nil {
t.Fatal(err)
}
if _, err := db.Get(key); err != nil {
t.Error("Invalid file wasn't added to global list")
}
updater.updateSchema1to2()
if err := updater.updateSchema1to2(); err != nil {
t.Fatal(err)
}
found := false
db.withHaveSequence(folder, 0, func(fi FileIntf) bool {
_ = db.withHaveSequence(folder, 0, func(fi FileIntf) bool {
f := fi.(protocol.FileInfo)
l.Infoln(f)
if found {
@@ -178,14 +204,16 @@ func TestUpdate0to3(t *testing.T) {
t.Error("Local file wasn't added to sequence bucket", err)
}
updater.updateSchema2to3()
if err := updater.updateSchema2to3(); err != nil {
t.Fatal(err)
}
need := map[string]protocol.FileInfo{
haveUpdate0to3[remoteDevice0][0].Name: haveUpdate0to3[remoteDevice0][0],
haveUpdate0to3[remoteDevice1][0].Name: haveUpdate0to3[remoteDevice1][0],
haveUpdate0to3[remoteDevice0][2].Name: haveUpdate0to3[remoteDevice0][2],
}
db.withNeed(folder, protocol.LocalDeviceID[:], false, func(fi FileIntf) bool {
_ = db.withNeed(folder, protocol.LocalDeviceID[:], false, func(fi FileIntf) bool {
e, ok := need[fi.FileName()]
if !ok {
t.Error("Got unexpected needed file:", fi.FileName())
@@ -203,12 +231,17 @@ func TestUpdate0to3(t *testing.T) {
}
func TestDowngrade(t *testing.T) {
db := OpenMemory()
UpdateSchema(db) // sets the min version etc
db := NewLowlevel(backend.OpenMemory())
// sets the min version etc
if err := UpdateSchema(db); err != nil {
t.Fatal(err)
}
// Bump the database version to something newer than we actually support
miscDB := NewMiscDataNamespace(db)
miscDB.PutInt64("dbVersion", dbVersion+1)
if err := miscDB.PutInt64("dbVersion", dbVersion+1); err != nil {
t.Fatal(err)
}
l.Infoln(dbVersion)
// Pretend we just opened the DB and attempt to update it again

View File

@@ -1,568 +0,0 @@
// Copyright (C) 2014 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 (
"bytes"
"encoding/binary"
"fmt"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
)
type instance struct {
*Lowlevel
keyer keyer
}
func newInstance(ll *Lowlevel) *instance {
return &instance{
Lowlevel: ll,
keyer: newDefaultKeyer(ll.folderIdx, ll.deviceIdx),
}
}
// updateRemoteFiles adds a list of fileinfos to the database and updates the
// global versionlist and metadata.
func (db *instance) updateRemoteFiles(folder, device []byte, fs []protocol.FileInfo, meta *metadataTracker) {
t := db.newReadWriteTransaction()
defer t.close()
var dk, gk, keyBuf []byte
devID := protocol.DeviceIDFromBytes(device)
for _, f := range fs {
name := []byte(f.Name)
dk = db.keyer.GenerateDeviceFileKey(dk, folder, device, name)
ef, ok := t.getFileTrunc(dk, true)
if ok && unchanged(f, ef) {
continue
}
if ok {
meta.removeFile(devID, ef)
}
meta.addFile(devID, f)
l.Debugf("insert; folder=%q device=%v %v", folder, devID, f)
t.Put(dk, mustMarshal(&f))
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
keyBuf, _ = t.updateGlobal(gk, keyBuf, folder, device, f, meta)
t.checkFlush()
}
}
// updateLocalFiles adds fileinfos to the db, and updates the global versionlist,
// metadata, sequence and blockmap buckets.
func (db *instance) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta *metadataTracker) {
t := db.newReadWriteTransaction()
defer t.close()
var dk, gk, keyBuf []byte
blockBuf := make([]byte, 4)
for _, f := range fs {
name := []byte(f.Name)
dk = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name)
ef, ok := t.getFileByKey(dk)
if ok && unchanged(f, ef) {
continue
}
if ok {
if !ef.IsDirectory() && !ef.IsDeleted() && !ef.IsInvalid() {
for _, block := range ef.Blocks {
keyBuf = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
t.Delete(keyBuf)
}
}
keyBuf = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo())
t.Delete(keyBuf)
l.Debugf("removing sequence; folder=%q sequence=%v %v", folder, ef.SequenceNo(), ef.FileName())
}
f.Sequence = meta.nextLocalSeq()
if ok {
meta.removeFile(protocol.LocalDeviceID, ef)
}
meta.addFile(protocol.LocalDeviceID, f)
l.Debugf("insert (local); folder=%q %v", folder, f)
t.Put(dk, mustMarshal(&f))
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, []byte(f.Name))
keyBuf, _ = t.updateGlobal(gk, keyBuf, folder, protocol.LocalDeviceID[:], f, meta)
keyBuf = db.keyer.GenerateSequenceKey(keyBuf, folder, f.Sequence)
t.Put(keyBuf, dk)
l.Debugf("adding sequence; folder=%q sequence=%v %v", folder, f.Sequence, f.Name)
if !f.IsDirectory() && !f.IsDeleted() && !f.IsInvalid() {
for i, block := range f.Blocks {
binary.BigEndian.PutUint32(blockBuf, uint32(i))
keyBuf = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
t.Put(keyBuf, blockBuf)
}
}
t.checkFlush()
}
}
func (db *instance) withHave(folder, device, prefix []byte, truncate bool, fn Iterator) {
t := db.newReadOnlyTransaction()
defer t.close()
if len(prefix) > 0 {
unslashedPrefix := prefix
if bytes.HasSuffix(prefix, []byte{'/'}) {
unslashedPrefix = unslashedPrefix[:len(unslashedPrefix)-1]
} else {
prefix = append(prefix, '/')
}
if f, ok := t.getFileTrunc(db.keyer.GenerateDeviceFileKey(nil, folder, device, unslashedPrefix), true); ok && !fn(f) {
return
}
}
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateDeviceFileKey(nil, folder, device, prefix)), nil)
defer dbi.Release()
for dbi.Next() {
name := db.keyer.NameFromDeviceFileKey(dbi.Key())
if len(prefix) > 0 && !bytes.HasPrefix(name, prefix) {
return
}
f, err := unmarshalTrunc(dbi.Value(), truncate)
if err != nil {
l.Debugln("unmarshal error:", err)
continue
}
if !fn(f) {
return
}
}
}
func (db *instance) withHaveSequence(folder []byte, startSeq int64, fn Iterator) {
t := db.newReadOnlyTransaction()
defer t.close()
dbi := t.NewIterator(&util.Range{Start: db.keyer.GenerateSequenceKey(nil, folder, startSeq), Limit: db.keyer.GenerateSequenceKey(nil, folder, maxInt64)}, nil)
defer dbi.Release()
for dbi.Next() {
f, ok := t.getFileByKey(dbi.Value())
if !ok {
l.Debugln("missing file for sequence number", db.keyer.SequenceFromSequenceKey(dbi.Key()))
continue
}
if shouldDebug() {
if seq := db.keyer.SequenceFromSequenceKey(dbi.Key()); f.Sequence != seq {
l.Warnf("Sequence index corruption (folder %v, file %v): sequence %d != expected %d", string(folder), f.Name, f.Sequence, seq)
panic("sequence index corruption")
}
}
if !fn(f) {
return
}
}
}
func (db *instance) withAllFolderTruncated(folder []byte, fn func(device []byte, f FileInfoTruncated) bool) {
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateDeviceFileKey(nil, folder, nil, nil).WithoutNameAndDevice()), nil)
defer dbi.Release()
var gk, keyBuf []byte
for dbi.Next() {
device, ok := db.keyer.DeviceFromDeviceFileKey(dbi.Key())
if !ok {
// Not having the device in the index is bad. Clear it.
t.Delete(dbi.Key())
t.checkFlush()
continue
}
var f FileInfoTruncated
// The iterator function may keep a reference to the unmarshalled
// struct, which in turn references the buffer it was unmarshalled
// from. dbi.Value() just returns an internal slice that it reuses, so
// we need to copy it.
err := f.Unmarshal(append([]byte{}, dbi.Value()...))
if err != nil {
l.Debugln("unmarshal error:", err)
continue
}
switch f.Name {
case "", ".", "..", "/": // A few obviously invalid filenames
l.Infof("Dropping invalid filename %q from database", f.Name)
name := []byte(f.Name)
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
keyBuf = t.removeFromGlobal(gk, keyBuf, folder, device, name, nil)
t.Delete(dbi.Key())
t.checkFlush()
continue
}
if !fn(device, f) {
return
}
}
}
func (db *instance) getFileDirty(folder, device, file []byte) (protocol.FileInfo, bool) {
t := db.newReadOnlyTransaction()
defer t.close()
return t.getFile(folder, device, file)
}
func (db *instance) getGlobalDirty(folder, file []byte, truncate bool) (FileIntf, bool) {
t := db.newReadOnlyTransaction()
defer t.close()
_, f, ok := t.getGlobal(nil, folder, file, truncate)
return f, ok
}
func (db *instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator) {
t := db.newReadOnlyTransaction()
defer t.close()
if len(prefix) > 0 {
unslashedPrefix := prefix
if bytes.HasSuffix(prefix, []byte{'/'}) {
unslashedPrefix = unslashedPrefix[:len(unslashedPrefix)-1]
} else {
prefix = append(prefix, '/')
}
if _, f, ok := t.getGlobal(nil, folder, unslashedPrefix, truncate); ok && !fn(f) {
return
}
}
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateGlobalVersionKey(nil, folder, prefix)), nil)
defer dbi.Release()
var dk []byte
for dbi.Next() {
name := db.keyer.NameFromGlobalVersionKey(dbi.Key())
if len(prefix) > 0 && !bytes.HasPrefix(name, prefix) {
return
}
vl, ok := unmarshalVersionList(dbi.Value())
if !ok {
continue
}
dk = db.keyer.GenerateDeviceFileKey(dk, folder, vl.Versions[0].Device, name)
f, ok := t.getFileTrunc(dk, truncate)
if !ok {
continue
}
if !fn(f) {
return
}
}
}
func (db *instance) availability(folder, file []byte) []protocol.DeviceID {
k := db.keyer.GenerateGlobalVersionKey(nil, folder, file)
bs, err := db.Get(k, nil)
if err == leveldb.ErrNotFound {
return nil
}
if err != nil {
l.Debugln("surprise error:", err)
return nil
}
vl, ok := unmarshalVersionList(bs)
if !ok {
return nil
}
var devices []protocol.DeviceID
for _, v := range vl.Versions {
if !v.Version.Equal(vl.Versions[0].Version) {
break
}
if v.Invalid {
continue
}
n := protocol.DeviceIDFromBytes(v.Device)
devices = append(devices, n)
}
return devices
}
func (db *instance) withNeed(folder, device []byte, truncate bool, fn Iterator) {
if bytes.Equal(device, protocol.LocalDeviceID[:]) {
db.withNeedLocal(folder, truncate, fn)
return
}
t := db.newReadOnlyTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateGlobalVersionKey(nil, folder, nil).WithoutName()), nil)
defer dbi.Release()
var dk []byte
devID := protocol.DeviceIDFromBytes(device)
for dbi.Next() {
vl, ok := unmarshalVersionList(dbi.Value())
if !ok {
continue
}
haveFV, have := vl.Get(device)
// XXX: This marks Concurrent (i.e. conflicting) changes as
// needs. Maybe we should do that, but it needs special
// handling in the puller.
if have && haveFV.Version.GreaterEqual(vl.Versions[0].Version) {
continue
}
name := db.keyer.NameFromGlobalVersionKey(dbi.Key())
needVersion := vl.Versions[0].Version
needDevice := protocol.DeviceIDFromBytes(vl.Versions[0].Device)
for i := range vl.Versions {
if !vl.Versions[i].Version.Equal(needVersion) {
// We haven't found a valid copy of the file with the needed version.
break
}
if vl.Versions[i].Invalid {
// The file is marked invalid, don't use it.
continue
}
dk = db.keyer.GenerateDeviceFileKey(dk, folder, vl.Versions[i].Device, name)
gf, ok := t.getFileTrunc(dk, truncate)
if !ok {
continue
}
if gf.IsDeleted() && !have {
// We don't need deleted files that we don't have
break
}
l.Debugf("need folder=%q device=%v name=%q have=%v invalid=%v haveV=%v globalV=%v globalDev=%v", folder, devID, name, have, haveFV.Invalid, haveFV.Version, needVersion, needDevice)
if !fn(gf) {
return
}
// This file is handled, no need to look further in the version list
break
}
}
}
func (db *instance) withNeedLocal(folder []byte, truncate bool, fn Iterator) {
t := db.newReadOnlyTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateNeedFileKey(nil, folder, nil).WithoutName()), nil)
defer dbi.Release()
var keyBuf []byte
var f FileIntf
var ok bool
for dbi.Next() {
keyBuf, f, ok = t.getGlobal(keyBuf, folder, db.keyer.NameFromGlobalVersionKey(dbi.Key()), truncate)
if !ok {
continue
}
if !fn(f) {
return
}
}
}
func (db *instance) dropFolder(folder []byte) {
t := db.newReadWriteTransaction()
defer t.close()
for _, key := range [][]byte{
// Remove all items related to the given folder from the device->file bucket
db.keyer.GenerateDeviceFileKey(nil, folder, nil, nil).WithoutNameAndDevice(),
// Remove all sequences related to the folder
db.keyer.GenerateSequenceKey(nil, []byte(folder), 0).WithoutSequence(),
// Remove all items related to the given folder from the global bucket
db.keyer.GenerateGlobalVersionKey(nil, folder, nil).WithoutName(),
// Remove all needs related to the folder
db.keyer.GenerateNeedFileKey(nil, folder, nil).WithoutName(),
// Remove the blockmap of the folder
db.keyer.GenerateBlockMapKey(nil, folder, nil, nil).WithoutHashAndName(),
} {
t.deleteKeyPrefix(key)
}
}
func (db *instance) dropDeviceFolder(device, folder []byte, meta *metadataTracker) {
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateDeviceFileKey(nil, folder, device, nil)), nil)
defer dbi.Release()
var gk, keyBuf []byte
for dbi.Next() {
name := db.keyer.NameFromDeviceFileKey(dbi.Key())
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
keyBuf = t.removeFromGlobal(gk, keyBuf, folder, device, name, meta)
t.Delete(dbi.Key())
t.checkFlush()
}
if bytes.Equal(device, protocol.LocalDeviceID[:]) {
t.deleteKeyPrefix(db.keyer.GenerateBlockMapKey(nil, folder, nil, nil).WithoutHashAndName())
}
}
func (db *instance) checkGlobals(folder []byte, meta *metadataTracker) {
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.keyer.GenerateGlobalVersionKey(nil, folder, nil).WithoutName()), nil)
defer dbi.Release()
var dk []byte
for dbi.Next() {
vl, ok := unmarshalVersionList(dbi.Value())
if !ok {
continue
}
// Check the global version list for consistency. An issue in previous
// versions of goleveldb could result in reordered writes so that
// there are global entries pointing to no longer existing files. Here
// we find those and clear them out.
name := db.keyer.NameFromGlobalVersionKey(dbi.Key())
var newVL VersionList
for i, version := range vl.Versions {
dk = db.keyer.GenerateDeviceFileKey(dk, folder, version.Device, name)
_, err := t.Get(dk, nil)
if err == leveldb.ErrNotFound {
continue
}
if err != nil {
l.Debugln("surprise error:", err)
return
}
newVL.Versions = append(newVL.Versions, version)
if i == 0 {
if fi, ok := t.getFileByKey(dk); ok {
meta.addFile(protocol.GlobalDeviceID, fi)
}
}
}
if len(newVL.Versions) != len(vl.Versions) {
t.Put(dbi.Key(), mustMarshal(&newVL))
t.checkFlush()
}
}
l.Debugf("db check completed for %q", folder)
}
func (db *instance) getIndexID(device, folder []byte) protocol.IndexID {
cur, err := db.Get(db.keyer.GenerateIndexIDKey(nil, device, folder), nil)
if err != nil {
return 0
}
var id protocol.IndexID
if err := id.Unmarshal(cur); err != nil {
return 0
}
return id
}
func (db *instance) setIndexID(device, folder []byte, id protocol.IndexID) {
bs, _ := id.Marshal() // marshalling can't fail
if err := db.Put(db.keyer.GenerateIndexIDKey(nil, device, folder), bs, nil); err != nil && err != leveldb.ErrClosed {
panic("storing index ID: " + err.Error())
}
}
func (db *instance) dropMtimes(folder []byte) {
db.dropPrefix(db.keyer.GenerateMtimesKey(nil, folder))
}
func (db *instance) dropFolderMeta(folder []byte) {
db.dropPrefix(db.keyer.GenerateFolderMetaKey(nil, folder))
}
func (db *instance) dropPrefix(prefix []byte) {
t := db.newReadWriteTransaction()
defer t.close()
t.deleteKeyPrefix(prefix)
}
func unmarshalTrunc(bs []byte, truncate bool) (FileIntf, error) {
if truncate {
var tf FileInfoTruncated
err := tf.Unmarshal(bs)
return tf, err
}
var tf protocol.FileInfo
err := tf.Unmarshal(bs)
return tf, err
}
func unmarshalVersionList(data []byte) (VersionList, bool) {
var vl VersionList
if err := vl.Unmarshal(data); err != nil {
l.Debugln("unmarshal error:", err)
return VersionList{}, false
}
if len(vl.Versions) == 0 {
l.Debugln("empty version list")
return VersionList{}, false
}
return vl, true
}
type errorSuggestion struct {
inner error
suggestion string
}
func (e errorSuggestion) Error() string {
return fmt.Sprintf("%s (%s)", e.inner.Error(), e.suggestion)
}
// unchanged checks if two files are the same and thus don't need to be updated.
// Local flags or the invalid bit might change without the version
// being bumped.
func unchanged(nf, ef FileIntf) bool {
return ef.FileVersion().Equal(nf.FileVersion()) && ef.IsInvalid() == nf.IsInvalid() && ef.FileLocalFlags() == nf.FileLocalFlags()
}

View File

@@ -63,36 +63,36 @@ const (
type keyer interface {
// device file key stuff
GenerateDeviceFileKey(key, folder, device, name []byte) deviceFileKey
GenerateDeviceFileKey(key, folder, device, name []byte) (deviceFileKey, error)
NameFromDeviceFileKey(key []byte) []byte
DeviceFromDeviceFileKey(key []byte) ([]byte, bool)
FolderFromDeviceFileKey(key []byte) ([]byte, bool)
// global version key stuff
GenerateGlobalVersionKey(key, folder, name []byte) globalVersionKey
GenerateGlobalVersionKey(key, folder, name []byte) (globalVersionKey, error)
NameFromGlobalVersionKey(key []byte) []byte
FolderFromGlobalVersionKey(key []byte) ([]byte, bool)
// block map key stuff (former BlockMap)
GenerateBlockMapKey(key, folder, hash, name []byte) blockMapKey
GenerateBlockMapKey(key, folder, hash, name []byte) (blockMapKey, error)
NameFromBlockMapKey(key []byte) []byte
// file need index
GenerateNeedFileKey(key, folder, name []byte) needFileKey
GenerateNeedFileKey(key, folder, name []byte) (needFileKey, error)
// file sequence index
GenerateSequenceKey(key, folder []byte, seq int64) sequenceKey
GenerateSequenceKey(key, folder []byte, seq int64) (sequenceKey, error)
SequenceFromSequenceKey(key []byte) int64
// index IDs
GenerateIndexIDKey(key, device, folder []byte) indexIDKey
GenerateIndexIDKey(key, device, folder []byte) (indexIDKey, error)
DeviceFromIndexIDKey(key []byte) ([]byte, bool)
// Mtimes
GenerateMtimesKey(key, folder []byte) mtimesKey
GenerateMtimesKey(key, folder []byte) (mtimesKey, error)
// Folder metadata
GenerateFolderMetaKey(key, folder []byte) folderMetaKey
GenerateFolderMetaKey(key, folder []byte) (folderMetaKey, error)
}
// defaultKeyer implements our key scheme. It needs folder and device
@@ -115,13 +115,21 @@ func (k deviceFileKey) WithoutNameAndDevice() []byte {
return k[:keyPrefixLen+keyFolderLen]
}
func (k defaultKeyer) GenerateDeviceFileKey(key, folder, device, name []byte) deviceFileKey {
func (k defaultKeyer) GenerateDeviceFileKey(key, folder, device, name []byte) (deviceFileKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
deviceID, err := k.deviceIdx.ID(device)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen+keyDeviceLen+len(name))
key[0] = KeyTypeDevice
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
binary.BigEndian.PutUint32(key[keyPrefixLen+keyFolderLen:], k.deviceIdx.ID(device))
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
binary.BigEndian.PutUint32(key[keyPrefixLen+keyFolderLen:], deviceID)
copy(key[keyPrefixLen+keyFolderLen+keyDeviceLen:], name)
return key
return key, nil
}
func (k defaultKeyer) NameFromDeviceFileKey(key []byte) []byte {
@@ -142,12 +150,16 @@ func (k globalVersionKey) WithoutName() []byte {
return k[:keyPrefixLen+keyFolderLen]
}
func (k defaultKeyer) GenerateGlobalVersionKey(key, folder, name []byte) globalVersionKey {
func (k defaultKeyer) GenerateGlobalVersionKey(key, folder, name []byte) (globalVersionKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen+len(name))
key[0] = KeyTypeGlobal
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
copy(key[keyPrefixLen+keyFolderLen:], name)
return key
return key, nil
}
func (k defaultKeyer) NameFromGlobalVersionKey(key []byte) []byte {
@@ -160,13 +172,17 @@ func (k defaultKeyer) FolderFromGlobalVersionKey(key []byte) ([]byte, bool) {
type blockMapKey []byte
func (k defaultKeyer) GenerateBlockMapKey(key, folder, hash, name []byte) blockMapKey {
func (k defaultKeyer) GenerateBlockMapKey(key, folder, hash, name []byte) (blockMapKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen+keyHashLen+len(name))
key[0] = KeyTypeBlock
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
copy(key[keyPrefixLen+keyFolderLen:], hash)
copy(key[keyPrefixLen+keyFolderLen+keyHashLen:], name)
return key
return key, nil
}
func (k defaultKeyer) NameFromBlockMapKey(key []byte) []byte {
@@ -183,12 +199,16 @@ func (k needFileKey) WithoutName() []byte {
return k[:keyPrefixLen+keyFolderLen]
}
func (k defaultKeyer) GenerateNeedFileKey(key, folder, name []byte) needFileKey {
func (k defaultKeyer) GenerateNeedFileKey(key, folder, name []byte) (needFileKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen+len(name))
key[0] = KeyTypeNeed
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
copy(key[keyPrefixLen+keyFolderLen:], name)
return key
return key, nil
}
type sequenceKey []byte
@@ -197,12 +217,16 @@ func (k sequenceKey) WithoutSequence() []byte {
return k[:keyPrefixLen+keyFolderLen]
}
func (k defaultKeyer) GenerateSequenceKey(key, folder []byte, seq int64) sequenceKey {
func (k defaultKeyer) GenerateSequenceKey(key, folder []byte, seq int64) (sequenceKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen+keySequenceLen)
key[0] = KeyTypeSequence
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
binary.BigEndian.PutUint64(key[keyPrefixLen+keyFolderLen:], uint64(seq))
return key
return key, nil
}
func (k defaultKeyer) SequenceFromSequenceKey(key []byte) int64 {
@@ -211,12 +235,20 @@ func (k defaultKeyer) SequenceFromSequenceKey(key []byte) int64 {
type indexIDKey []byte
func (k defaultKeyer) GenerateIndexIDKey(key, device, folder []byte) indexIDKey {
func (k defaultKeyer) GenerateIndexIDKey(key, device, folder []byte) (indexIDKey, error) {
deviceID, err := k.deviceIdx.ID(device)
if err != nil {
return nil, err
}
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyDeviceLen+keyFolderLen)
key[0] = KeyTypeIndexID
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.deviceIdx.ID(device))
binary.BigEndian.PutUint32(key[keyPrefixLen+keyDeviceLen:], k.folderIdx.ID(folder))
return key
binary.BigEndian.PutUint32(key[keyPrefixLen:], deviceID)
binary.BigEndian.PutUint32(key[keyPrefixLen+keyDeviceLen:], folderID)
return key, nil
}
func (k defaultKeyer) DeviceFromIndexIDKey(key []byte) ([]byte, bool) {
@@ -225,20 +257,28 @@ func (k defaultKeyer) DeviceFromIndexIDKey(key []byte) ([]byte, bool) {
type mtimesKey []byte
func (k defaultKeyer) GenerateMtimesKey(key, folder []byte) mtimesKey {
func (k defaultKeyer) GenerateMtimesKey(key, folder []byte) (mtimesKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen)
key[0] = KeyTypeVirtualMtime
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
return key
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
return key, nil
}
type folderMetaKey []byte
func (k defaultKeyer) GenerateFolderMetaKey(key, folder []byte) folderMetaKey {
func (k defaultKeyer) GenerateFolderMetaKey(key, folder []byte) (folderMetaKey, error) {
folderID, err := k.folderIdx.ID(folder)
if err != nil {
return nil, err
}
key = resize(key, keyPrefixLen+keyFolderLen)
key[0] = KeyTypeFolderMeta
binary.BigEndian.PutUint32(key[keyPrefixLen:], k.folderIdx.ID(folder))
return key
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
return key, nil
}
// resize returns a byte slice of the specified size, reusing bs if possible

View File

@@ -9,6 +9,8 @@ package db
import (
"bytes"
"testing"
"github.com/syncthing/syncthing/lib/db/backend"
)
func TestDeviceKey(t *testing.T) {
@@ -16,9 +18,12 @@ func TestDeviceKey(t *testing.T) {
dev := []byte("device67890123456789012345678901")
name := []byte("name")
db := newInstance(OpenMemory())
db := NewLowlevel(backend.OpenMemory())
key := db.keyer.GenerateDeviceFileKey(nil, fld, dev, name)
key, err := db.keyer.GenerateDeviceFileKey(nil, fld, dev, name)
if err != nil {
t.Fatal(err)
}
fld2, ok := db.keyer.FolderFromDeviceFileKey(key)
if !ok {
@@ -44,9 +49,12 @@ func TestGlobalKey(t *testing.T) {
fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
name := []byte("name")
db := newInstance(OpenMemory())
db := NewLowlevel(backend.OpenMemory())
key := db.keyer.GenerateGlobalVersionKey(nil, fld, name)
key, err := db.keyer.GenerateGlobalVersionKey(nil, fld, name)
if err != nil {
t.Fatal(err)
}
fld2, ok := db.keyer.FolderFromGlobalVersionKey(key)
if !ok {
@@ -69,10 +77,13 @@ func TestGlobalKey(t *testing.T) {
func TestSequenceKey(t *testing.T) {
fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
db := newInstance(OpenMemory())
db := NewLowlevel(backend.OpenMemory())
const seq = 1234567890
key := db.keyer.GenerateSequenceKey(nil, fld, seq)
key, err := db.keyer.GenerateSequenceKey(nil, fld, seq)
if err != nil {
t.Fatal(err)
}
outSeq := db.keyer.SequenceFromSequenceKey(key)
if outSeq != seq {
t.Errorf("sequence number mangled, %d != %d", outSeq, seq)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -56,8 +56,11 @@ func (m *metadataTracker) Marshal() ([]byte, error) {
// toDB saves the marshalled metadataTracker to the given db, under the key
// corresponding to the given folder
func (m *metadataTracker) toDB(db *instance, folder []byte) error {
key := db.keyer.GenerateFolderMetaKey(nil, folder)
func (m *metadataTracker) toDB(db *Lowlevel, folder []byte) error {
key, err := db.keyer.GenerateFolderMetaKey(nil, folder)
if err != nil {
return err
}
m.mut.RLock()
defer m.mut.RUnlock()
@@ -70,7 +73,7 @@ func (m *metadataTracker) toDB(db *instance, folder []byte) error {
if err != nil {
return err
}
err = db.Put(key, bs, nil)
err = db.Put(key, bs)
if err == nil {
m.dirty = false
}
@@ -80,9 +83,12 @@ func (m *metadataTracker) toDB(db *instance, folder []byte) error {
// fromDB initializes the metadataTracker from the marshalled data found in
// the database under the key corresponding to the given folder
func (m *metadataTracker) fromDB(db *instance, folder []byte) error {
key := db.keyer.GenerateFolderMetaKey(nil, folder)
bs, err := db.Get(key, nil)
func (m *metadataTracker) fromDB(db *Lowlevel, folder []byte) error {
key, err := db.keyer.GenerateFolderMetaKey(nil, folder)
if err != nil {
return err
}
bs, err := db.Get(key)
if err != nil {
return err
}

View File

@@ -10,7 +10,7 @@ import (
"encoding/binary"
"time"
"github.com/syndtr/goleveldb/leveldb/util"
"github.com/syncthing/syncthing/lib/db/backend"
)
// NamespacedKV is a simple key-value store using a specific namespace within
@@ -34,112 +34,99 @@ func NewNamespacedKV(db *Lowlevel, prefix string) *NamespacedKV {
}
}
// Reset removes all entries in this namespace.
func (n *NamespacedKV) Reset() {
it := n.db.NewIterator(util.BytesPrefix(n.prefix), nil)
defer it.Release()
batch := n.db.newBatch()
for it.Next() {
batch.Delete(it.Key())
batch.checkFlush()
}
batch.flush()
}
// PutInt64 stores a new int64. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutInt64(key string, val int64) {
func (n *NamespacedKV) PutInt64(key string, val int64) error {
var valBs [8]byte
binary.BigEndian.PutUint64(valBs[:], uint64(val))
n.db.Put(n.prefixedKey(key), valBs[:], nil)
return n.db.Put(n.prefixedKey(key), valBs[:])
}
// Int64 returns the stored value interpreted as an int64 and a boolean that
// is false if no value was stored at the key.
func (n *NamespacedKV) Int64(key string) (int64, bool) {
valBs, err := n.db.Get(n.prefixedKey(key), nil)
func (n *NamespacedKV) Int64(key string) (int64, bool, error) {
valBs, err := n.db.Get(n.prefixedKey(key))
if err != nil {
return 0, false
return 0, false, filterNotFound(err)
}
val := binary.BigEndian.Uint64(valBs)
return int64(val), true
return int64(val), true, nil
}
// PutTime stores a new time.Time. Any existing value (even if of another
// type) is overwritten.
func (n *NamespacedKV) PutTime(key string, val time.Time) {
func (n *NamespacedKV) PutTime(key string, val time.Time) error {
valBs, _ := val.MarshalBinary() // never returns an error
n.db.Put(n.prefixedKey(key), valBs, nil)
return n.db.Put(n.prefixedKey(key), valBs)
}
// Time returns the stored value interpreted as a time.Time and a boolean
// that is false if no value was stored at the key.
func (n NamespacedKV) Time(key string) (time.Time, bool) {
func (n NamespacedKV) Time(key string) (time.Time, bool, error) {
var t time.Time
valBs, err := n.db.Get(n.prefixedKey(key), nil)
valBs, err := n.db.Get(n.prefixedKey(key))
if err != nil {
return t, false
return t, false, filterNotFound(err)
}
err = t.UnmarshalBinary(valBs)
return t, err == nil
return t, err == nil, err
}
// PutString stores a new string. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutString(key, val string) {
n.db.Put(n.prefixedKey(key), []byte(val), nil)
func (n *NamespacedKV) PutString(key, val string) error {
return n.db.Put(n.prefixedKey(key), []byte(val))
}
// String returns the stored value interpreted as a string and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) String(key string) (string, bool) {
valBs, err := n.db.Get(n.prefixedKey(key), nil)
func (n NamespacedKV) String(key string) (string, bool, error) {
valBs, err := n.db.Get(n.prefixedKey(key))
if err != nil {
return "", false
return "", false, filterNotFound(err)
}
return string(valBs), true
return string(valBs), true, nil
}
// PutBytes stores a new byte slice. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutBytes(key string, val []byte) {
n.db.Put(n.prefixedKey(key), val, nil)
func (n *NamespacedKV) PutBytes(key string, val []byte) error {
return n.db.Put(n.prefixedKey(key), val)
}
// Bytes returns the stored value as a raw byte slice and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) Bytes(key string) ([]byte, bool) {
valBs, err := n.db.Get(n.prefixedKey(key), nil)
func (n NamespacedKV) Bytes(key string) ([]byte, bool, error) {
valBs, err := n.db.Get(n.prefixedKey(key))
if err != nil {
return nil, false
return nil, false, filterNotFound(err)
}
return valBs, true
return valBs, true, nil
}
// PutBool stores a new boolean. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutBool(key string, val bool) {
func (n *NamespacedKV) PutBool(key string, val bool) error {
if val {
n.db.Put(n.prefixedKey(key), []byte{0x0}, nil)
} else {
n.db.Put(n.prefixedKey(key), []byte{0x1}, nil)
return n.db.Put(n.prefixedKey(key), []byte{0x0})
}
return n.db.Put(n.prefixedKey(key), []byte{0x1})
}
// Bool returns the stored value as a boolean and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) Bool(key string) (bool, bool) {
valBs, err := n.db.Get(n.prefixedKey(key), nil)
func (n NamespacedKV) Bool(key string) (bool, bool, error) {
valBs, err := n.db.Get(n.prefixedKey(key))
if err != nil {
return false, false
return false, false, filterNotFound(err)
}
return valBs[0] == 0x0, true
return valBs[0] == 0x0, true, nil
}
// Delete deletes the specified key. It is allowed to delete a nonexistent
// key.
func (n NamespacedKV) Delete(key string) {
n.db.Delete(n.prefixedKey(key), nil)
func (n NamespacedKV) Delete(key string) error {
return n.db.Delete(n.prefixedKey(key))
}
func (n NamespacedKV) prefixedKey(key string) []byte {
@@ -165,3 +152,10 @@ func NewFolderStatisticsNamespace(db *Lowlevel, folder string) *NamespacedKV {
func NewMiscDataNamespace(db *Lowlevel) *NamespacedKV {
return NewNamespacedKV(db, string(KeyTypeMiscData))
}
func filterNotFound(err error) error {
if backend.IsNotFound(err) {
return nil
}
return err
}

View File

@@ -9,104 +9,167 @@ package db
import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/db/backend"
)
func TestNamespacedInt(t *testing.T) {
ldb := OpenMemory()
ldb := NewLowlevel(backend.OpenMemory())
n1 := NewNamespacedKV(ldb, "foo")
n2 := NewNamespacedKV(ldb, "bar")
// Key is missing to start with
if v, ok := n1.Int64("test"); v != 0 || ok {
if v, ok, err := n1.Int64("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != 0 || ok {
t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok)
}
n1.PutInt64("test", 42)
if err := n1.PutInt64("test", 42); err != nil {
t.Fatal(err)
}
// It should now exist in n1
if v, ok := n1.Int64("test"); v != 42 || !ok {
if v, ok, err := n1.Int64("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != 42 || !ok {
t.Errorf("Incorrect return v %v != 42 || ok %v != true", v, ok)
}
// ... but not in n2, which is in a different namespace
if v, ok := n2.Int64("test"); v != 0 || ok {
if v, ok, err := n2.Int64("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != 0 || ok {
t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok)
}
n1.Delete("test")
if err := n1.Delete("test"); err != nil {
t.Fatal(err)
}
// It should no longer exist
if v, ok := n1.Int64("test"); v != 0 || ok {
if v, ok, err := n1.Int64("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != 0 || ok {
t.Errorf("Incorrect return v %v != 0 || ok %v != false", v, ok)
}
}
func TestNamespacedTime(t *testing.T) {
ldb := OpenMemory()
ldb := NewLowlevel(backend.OpenMemory())
n1 := NewNamespacedKV(ldb, "foo")
if v, ok := n1.Time("test"); !v.IsZero() || ok {
if v, ok, err := n1.Time("test"); err != nil {
t.Error("Unexpected error:", err)
} else if !v.IsZero() || ok {
t.Errorf("Incorrect return v %v != %v || ok %v != false", v, time.Time{}, ok)
}
now := time.Now()
n1.PutTime("test", now)
if err := n1.PutTime("test", now); err != nil {
t.Fatal(err)
}
if v, ok := n1.Time("test"); !v.Equal(now) || !ok {
if v, ok, err := n1.Time("test"); err != nil {
t.Error("Unexpected error:", err)
} else if !v.Equal(now) || !ok {
t.Errorf("Incorrect return v %v != %v || ok %v != true", v, now, ok)
}
}
func TestNamespacedString(t *testing.T) {
ldb := OpenMemory()
ldb := NewLowlevel(backend.OpenMemory())
n1 := NewNamespacedKV(ldb, "foo")
if v, ok := n1.String("test"); v != "" || ok {
if v, ok, err := n1.String("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "" || ok {
t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok)
}
n1.PutString("test", "yo")
if err := n1.PutString("test", "yo"); err != nil {
t.Fatal(err)
}
if v, ok := n1.String("test"); v != "yo" || !ok {
if v, ok, err := n1.String("test"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "yo" || !ok {
t.Errorf("Incorrect return v %q != \"yo\" || ok %v != true", v, ok)
}
}
func TestNamespacedReset(t *testing.T) {
ldb := OpenMemory()
ldb := NewLowlevel(backend.OpenMemory())
n1 := NewNamespacedKV(ldb, "foo")
n1.PutString("test1", "yo1")
n1.PutString("test2", "yo2")
n1.PutString("test3", "yo3")
if err := n1.PutString("test1", "yo1"); err != nil {
t.Fatal(err)
}
if err := n1.PutString("test2", "yo2"); err != nil {
t.Fatal(err)
}
if err := n1.PutString("test3", "yo3"); err != nil {
t.Fatal(err)
}
if v, ok := n1.String("test1"); v != "yo1" || !ok {
if v, ok, err := n1.String("test1"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "yo1" || !ok {
t.Errorf("Incorrect return v %q != \"yo1\" || ok %v != true", v, ok)
}
if v, ok := n1.String("test2"); v != "yo2" || !ok {
if v, ok, err := n1.String("test2"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "yo2" || !ok {
t.Errorf("Incorrect return v %q != \"yo2\" || ok %v != true", v, ok)
}
if v, ok := n1.String("test3"); v != "yo3" || !ok {
if v, ok, err := n1.String("test3"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "yo3" || !ok {
t.Errorf("Incorrect return v %q != \"yo3\" || ok %v != true", v, ok)
}
n1.Reset()
reset(n1)
if v, ok := n1.String("test1"); v != "" || ok {
if v, ok, err := n1.String("test1"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "" || ok {
t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok)
}
if v, ok := n1.String("test2"); v != "" || ok {
if v, ok, err := n1.String("test2"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "" || ok {
t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok)
}
if v, ok := n1.String("test3"); v != "" || ok {
if v, ok, err := n1.String("test3"); err != nil {
t.Error("Unexpected error:", err)
} else if v != "" || ok {
t.Errorf("Incorrect return v %q != \"\" || ok %v != false", v, ok)
}
}
// reset removes all entries in this namespace.
func reset(n *NamespacedKV) {
tr, err := n.db.NewWriteTransaction()
if err != nil {
return
}
defer tr.Release()
it, err := tr.NewPrefixIterator(n.prefix)
if err != nil {
return
}
for it.Next() {
_ = tr.Delete(it.Key())
}
it.Release()
_ = tr.Commit()
}

View File

@@ -11,7 +11,6 @@ import (
"strings"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syndtr/goleveldb/leveldb/util"
)
// List of all dbVersion to dbMinSyncthingVersion pairs for convenience
@@ -39,22 +38,27 @@ func (e databaseDowngradeError) Error() string {
return fmt.Sprintf("Syncthing %s required", e.minSyncthingVersion)
}
func UpdateSchema(ll *Lowlevel) error {
updater := &schemaUpdater{newInstance(ll)}
func UpdateSchema(db *Lowlevel) error {
updater := &schemaUpdater{db}
return updater.updateSchema()
}
type schemaUpdater struct {
*instance
*Lowlevel
}
func (db *schemaUpdater) updateSchema() error {
miscDB := NewMiscDataNamespace(db.Lowlevel)
prevVersion, _ := miscDB.Int64("dbVersion")
prevVersion, _, err := miscDB.Int64("dbVersion")
if err != nil {
return err
}
if prevVersion > dbVersion {
err := databaseDowngradeError{}
if minSyncthingVersion, ok := miscDB.String("dbMinSyncthingVersion"); ok {
if minSyncthingVersion, ok, dbErr := miscDB.String("dbMinSyncthingVersion"); dbErr != nil {
return dbErr
} else if ok {
err.minSyncthingVersion = minSyncthingVersion
}
return err
@@ -65,36 +69,58 @@ func (db *schemaUpdater) updateSchema() error {
}
if prevVersion < 1 {
db.updateSchema0to1()
if err := db.updateSchema0to1(); err != nil {
return err
}
}
if prevVersion < 2 {
db.updateSchema1to2()
if err := db.updateSchema1to2(); err != nil {
return err
}
}
if prevVersion < 3 {
db.updateSchema2to3()
if err := db.updateSchema2to3(); err != nil {
return err
}
}
// This update fixes problems existing in versions 3 and 4
if prevVersion == 3 || prevVersion == 4 {
db.updateSchemaTo5()
if err := db.updateSchemaTo5(); err != nil {
return err
}
}
if prevVersion < 6 {
db.updateSchema5to6()
if err := db.updateSchema5to6(); err != nil {
return err
}
}
if prevVersion < 7 {
db.updateSchema6to7()
if err := db.updateSchema6to7(); err != nil {
return err
}
}
miscDB.PutInt64("dbVersion", dbVersion)
miscDB.PutString("dbMinSyncthingVersion", dbMinSyncthingVersion)
if err := miscDB.PutInt64("dbVersion", dbVersion); err != nil {
return err
}
if err := miscDB.PutString("dbMinSyncthingVersion", dbMinSyncthingVersion); err != nil {
return err
}
return nil
}
func (db *schemaUpdater) updateSchema0to1() {
t := db.newReadWriteTransaction()
func (db *schemaUpdater) updateSchema0to1() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
dbi := t.NewIterator(util.BytesPrefix([]byte{KeyTypeDevice}), nil)
dbi, err := t.NewPrefixIterator([]byte{KeyTypeDevice})
if err != nil {
return err
}
defer dbi.Release()
symlinkConv := 0
@@ -104,18 +130,20 @@ func (db *schemaUpdater) updateSchema0to1() {
var gk, buf []byte
for dbi.Next() {
t.checkFlush()
folder, ok := db.keyer.FolderFromDeviceFileKey(dbi.Key())
if !ok {
// not having the folder in the index is bad; delete and continue
t.Delete(dbi.Key())
if err := t.Delete(dbi.Key()); err != nil {
return err
}
continue
}
device, ok := db.keyer.DeviceFromDeviceFileKey(dbi.Key())
if !ok {
// not having the device in the index is bad; delete and continue
t.Delete(dbi.Key())
if err := t.Delete(dbi.Key()); err != nil {
return err
}
continue
}
name := db.keyer.NameFromDeviceFileKey(dbi.Key())
@@ -125,9 +153,17 @@ func (db *schemaUpdater) updateSchema0to1() {
if _, ok := changedFolders[string(folder)]; !ok {
changedFolders[string(folder)] = struct{}{}
}
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
buf = t.removeFromGlobal(gk, buf, folder, device, nil, nil)
t.Delete(dbi.Key())
gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
if err != nil {
return err
}
buf, err = t.removeFromGlobal(gk, buf, folder, device, nil, nil)
if err != nil {
return err
}
if err := t.Delete(dbi.Key()); err != nil {
return err
}
continue
}
@@ -147,14 +183,21 @@ func (db *schemaUpdater) updateSchema0to1() {
if err != nil {
panic("can't happen: " + err.Error())
}
t.Put(dbi.Key(), bs)
if err := t.Put(dbi.Key(), bs); err != nil {
return err
}
symlinkConv++
}
// Add invalid files to global list
if f.IsInvalid() {
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
if buf, ok = t.updateGlobal(gk, buf, folder, device, f, meta); ok {
gk, err = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
if err != nil {
return err
}
if buf, ok, err = t.updateGlobal(gk, buf, folder, device, f, meta); err != nil {
return err
} else if ok {
if _, ok = changedFolders[string(folder)]; !ok {
changedFolders[string(folder)] = struct{}{}
}
@@ -164,86 +207,139 @@ func (db *schemaUpdater) updateSchema0to1() {
}
for folder := range changedFolders {
db.dropFolderMeta([]byte(folder))
if err := db.dropFolderMeta([]byte(folder)); err != nil {
return err
}
}
return t.commit()
}
// updateSchema1to2 introduces a sequenceKey->deviceKey bucket for local items
// to allow iteration in sequence order (simplifies sending indexes).
func (db *schemaUpdater) updateSchema1to2() {
t := db.newReadWriteTransaction()
func (db *schemaUpdater) updateSchema1to2() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var sk []byte
var dk []byte
for _, folderStr := range db.ListFolders() {
folder := []byte(folderStr)
db.withHave(folder, protocol.LocalDeviceID[:], nil, true, func(f FileIntf) bool {
sk = db.keyer.GenerateSequenceKey(sk, folder, f.SequenceNo())
dk = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], []byte(f.FileName()))
t.Put(sk, dk)
t.checkFlush()
return true
var putErr error
err := db.withHave(folder, protocol.LocalDeviceID[:], nil, true, func(f FileIntf) bool {
sk, putErr = db.keyer.GenerateSequenceKey(sk, folder, f.SequenceNo())
if putErr != nil {
return false
}
dk, putErr = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], []byte(f.FileName()))
if putErr != nil {
return false
}
putErr = t.Put(sk, dk)
return putErr == nil
})
if putErr != nil {
return putErr
}
if err != nil {
return err
}
}
return t.commit()
}
// updateSchema2to3 introduces a needKey->nil bucket for locally needed files.
func (db *schemaUpdater) updateSchema2to3() {
t := db.newReadWriteTransaction()
func (db *schemaUpdater) updateSchema2to3() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var nk []byte
var dk []byte
for _, folderStr := range db.ListFolders() {
folder := []byte(folderStr)
db.withGlobal(folder, nil, true, func(f FileIntf) bool {
var putErr error
err := db.withGlobal(folder, nil, true, func(f FileIntf) bool {
name := []byte(f.FileName())
dk = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name)
dk, putErr = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], name)
if putErr != nil {
return false
}
var v protocol.Vector
haveFile, ok := t.getFileTrunc(dk, true)
haveFile, ok, err := t.getFileTrunc(dk, true)
if err != nil {
putErr = err
return false
}
if ok {
v = haveFile.FileVersion()
}
if !need(f, ok, v) {
return true
}
nk = t.keyer.GenerateNeedFileKey(nk, folder, []byte(f.FileName()))
t.Put(nk, nil)
t.checkFlush()
return true
nk, putErr = t.keyer.GenerateNeedFileKey(nk, folder, []byte(f.FileName()))
if putErr != nil {
return false
}
putErr = t.Put(nk, nil)
return putErr == nil
})
if putErr != nil {
return putErr
}
if err != nil {
return err
}
}
return t.commit()
}
// updateSchemaTo5 resets the need bucket due to bugs existing in the v0.14.49
// release candidates (dbVersion 3 and 4)
// https://github.com/syncthing/syncthing/issues/5007
// https://github.com/syncthing/syncthing/issues/5053
func (db *schemaUpdater) updateSchemaTo5() {
t := db.newReadWriteTransaction()
func (db *schemaUpdater) updateSchemaTo5() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
var nk []byte
for _, folderStr := range db.ListFolders() {
nk = db.keyer.GenerateNeedFileKey(nk, []byte(folderStr), nil)
t.deleteKeyPrefix(nk[:keyPrefixLen+keyFolderLen])
nk, err = db.keyer.GenerateNeedFileKey(nk, []byte(folderStr), nil)
if err != nil {
return err
}
if err := t.deleteKeyPrefix(nk[:keyPrefixLen+keyFolderLen]); err != nil {
return err
}
}
if err := t.commit(); err != nil {
return err
}
t.close()
db.updateSchema2to3()
return db.updateSchema2to3()
}
func (db *schemaUpdater) updateSchema5to6() {
func (db *schemaUpdater) updateSchema5to6() error {
// For every local file with the Invalid bit set, clear the Invalid bit and
// set LocalFlags = FlagLocalIgnored.
t := db.newReadWriteTransaction()
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var dk []byte
for _, folderStr := range db.ListFolders() {
folder := []byte(folderStr)
db.withHave(folder, protocol.LocalDeviceID[:], nil, false, func(f FileIntf) bool {
var putErr error
err := db.withHave(folder, protocol.LocalDeviceID[:], nil, false, func(f FileIntf) bool {
if !f.IsInvalid() {
return true
}
@@ -253,19 +349,31 @@ func (db *schemaUpdater) updateSchema5to6() {
fi.LocalFlags = protocol.FlagLocalIgnored
bs, _ := fi.Marshal()
dk = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], []byte(fi.Name))
t.Put(dk, bs)
dk, putErr = db.keyer.GenerateDeviceFileKey(dk, folder, protocol.LocalDeviceID[:], []byte(fi.Name))
if putErr != nil {
return false
}
putErr = t.Put(dk, bs)
t.checkFlush()
return true
return putErr == nil
})
if putErr != nil {
return putErr
}
if err != nil {
return err
}
}
return t.commit()
}
// updateSchema6to7 checks whether all currently locally needed files are really
// needed and removes them if not.
func (db *schemaUpdater) updateSchema6to7() {
t := db.newReadWriteTransaction()
func (db *schemaUpdater) updateSchema6to7() error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var gk []byte
@@ -273,15 +381,24 @@ func (db *schemaUpdater) updateSchema6to7() {
for _, folderStr := range db.ListFolders() {
folder := []byte(folderStr)
db.withNeedLocal(folder, false, func(f FileIntf) bool {
var delErr error
err := db.withNeedLocal(folder, false, func(f FileIntf) bool {
name := []byte(f.FileName())
global := f.(protocol.FileInfo)
gk = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
svl, err := t.Get(gk, nil)
gk, delErr = db.keyer.GenerateGlobalVersionKey(gk, folder, name)
if delErr != nil {
return false
}
svl, err := t.Get(gk)
if err != nil {
// If there is no global list, we hardly need it.
t.Delete(t.keyer.GenerateNeedFileKey(nk, folder, name))
return true
key, err := t.keyer.GenerateNeedFileKey(nk, folder, name)
if err != nil {
delErr = err
return false
}
delErr = t.Delete(key)
return delErr == nil
}
var fl VersionList
err = fl.Unmarshal(svl)
@@ -291,9 +408,18 @@ func (db *schemaUpdater) updateSchema6to7() {
return true
}
if localFV, haveLocalFV := fl.Get(protocol.LocalDeviceID[:]); !need(global, haveLocalFV, localFV.Version) {
t.Delete(t.keyer.GenerateNeedFileKey(nk, folder, name))
key, err := t.keyer.GenerateNeedFileKey(nk, folder, name)
if err != nil {
delErr = err
return false
}
delErr = t.Delete(key)
}
return true
return delErr == nil
})
if err != nil {
return err
}
}
return t.commit()
}

View File

@@ -16,17 +16,17 @@ import (
"os"
"time"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syndtr/goleveldb/leveldb/util"
)
type FileSet struct {
folder string
fs fs.Filesystem
db *instance
db *Lowlevel
meta *metadataTracker
updateMutex sync.Mutex // protects database updates and the corresponding metadata changes
@@ -70,9 +70,7 @@ func init() {
}
}
func NewFileSet(folder string, fs fs.Filesystem, ll *Lowlevel) *FileSet {
db := newInstance(ll)
func NewFileSet(folder string, fs fs.Filesystem, db *Lowlevel) *FileSet {
var s = FileSet{
folder: folder,
fs: fs,
@@ -83,29 +81,42 @@ func NewFileSet(folder string, fs fs.Filesystem, ll *Lowlevel) *FileSet {
if err := s.meta.fromDB(db, []byte(folder)); err != nil {
l.Infof("No stored folder metadata for %q: recalculating", folder)
s.recalcCounts()
if err := s.recalcCounts(); backend.IsClosed(err) {
return nil
} else if err != nil {
panic(err)
}
} else if age := time.Since(s.meta.Created()); age > databaseRecheckInterval {
l.Infof("Stored folder metadata for %q is %v old; recalculating", folder, age)
s.recalcCounts()
if err := s.recalcCounts(); backend.IsClosed(err) {
return nil
} else if err != nil {
panic(err)
}
}
return &s
}
func (s *FileSet) recalcCounts() {
func (s *FileSet) recalcCounts() error {
s.meta = newMetadataTracker()
s.db.checkGlobals([]byte(s.folder), s.meta)
if err := s.db.checkGlobals([]byte(s.folder), s.meta); err != nil {
return err
}
var deviceID protocol.DeviceID
s.db.withAllFolderTruncated([]byte(s.folder), func(device []byte, f FileInfoTruncated) bool {
err := s.db.withAllFolderTruncated([]byte(s.folder), func(device []byte, f FileInfoTruncated) bool {
copy(deviceID[:], device)
s.meta.addFile(deviceID, f)
return true
})
if err != nil {
return err
}
s.meta.SetCreated()
s.meta.toDB(s.db, []byte(s.folder))
return s.meta.toDB(s.db, []byte(s.folder))
}
func (s *FileSet) Drop(device protocol.DeviceID) {
@@ -114,7 +125,11 @@ func (s *FileSet) Drop(device protocol.DeviceID) {
s.updateMutex.Lock()
defer s.updateMutex.Unlock()
s.db.dropDeviceFolder(device[:], []byte(s.folder), s.meta)
if err := s.db.dropDeviceFolder(device[:], []byte(s.folder), s.meta); backend.IsClosed(err) {
return
} else if err != nil {
panic(err)
}
if device == protocol.LocalDeviceID {
s.meta.resetCounts(device)
@@ -131,7 +146,11 @@ func (s *FileSet) Drop(device protocol.DeviceID) {
s.meta.resetAll(device)
}
s.meta.toDB(s.db, []byte(s.folder))
if err := s.meta.toDB(s.db, []byte(s.folder)); backend.IsClosed(err) {
return
} else if err != nil {
panic(err)
}
}
func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) {
@@ -145,73 +164,110 @@ func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) {
s.updateMutex.Lock()
defer s.updateMutex.Unlock()
defer s.meta.toDB(s.db, []byte(s.folder))
defer func() {
if err := s.meta.toDB(s.db, []byte(s.folder)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}()
if device == protocol.LocalDeviceID {
// For the local device we have a bunch of metadata to track.
s.db.updateLocalFiles([]byte(s.folder), fs, s.meta)
if err := s.db.updateLocalFiles([]byte(s.folder), fs, s.meta); err != nil && !backend.IsClosed(err) {
panic(err)
}
return
}
// Easy case, just update the files and we're done.
s.db.updateRemoteFiles([]byte(s.folder), device[:], fs, s.meta)
if err := s.db.updateRemoteFiles([]byte(s.folder), device[:], fs, s.meta); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithNeed(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithNeed(%v)", s.folder, device)
s.db.withNeed([]byte(s.folder), device[:], false, nativeFileIterator(fn))
if err := s.db.withNeed([]byte(s.folder), device[:], false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithNeedTruncated(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithNeedTruncated(%v)", s.folder, device)
s.db.withNeed([]byte(s.folder), device[:], true, nativeFileIterator(fn))
if err := s.db.withNeed([]byte(s.folder), device[:], true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithHave(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHave(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn))
if err := s.db.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithHaveTruncated(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHaveTruncated(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn))
if err := s.db.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithHaveSequence(startSeq int64, fn Iterator) {
l.Debugf("%s WithHaveSequence(%v)", s.folder, startSeq)
s.db.withHaveSequence([]byte(s.folder), startSeq, nativeFileIterator(fn))
if err := s.db.withHaveSequence([]byte(s.folder), startSeq, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
// Except for an item with a path equal to prefix, only children of prefix are iterated.
// E.g. for prefix "dir", "dir/file" is iterated, but "dir.file" is not.
func (s *FileSet) WithPrefixedHaveTruncated(device protocol.DeviceID, prefix string, fn Iterator) {
l.Debugf(`%s WithPrefixedHaveTruncated(%v, "%v")`, s.folder, device, prefix)
s.db.withHave([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn))
if err := s.db.withHave([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithGlobal(fn Iterator) {
l.Debugf("%s WithGlobal()", s.folder)
s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn))
if err := s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) WithGlobalTruncated(fn Iterator) {
l.Debugf("%s WithGlobalTruncated()", s.folder)
s.db.withGlobal([]byte(s.folder), nil, true, nativeFileIterator(fn))
if err := s.db.withGlobal([]byte(s.folder), nil, true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
// Except for an item with a path equal to prefix, only children of prefix are iterated.
// E.g. for prefix "dir", "dir/file" is iterated, but "dir.file" is not.
func (s *FileSet) WithPrefixedGlobalTruncated(prefix string, fn Iterator) {
l.Debugf(`%s WithPrefixedGlobalTruncated("%v")`, s.folder, prefix)
s.db.withGlobal([]byte(s.folder), []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn))
if err := s.db.withGlobal([]byte(s.folder), []byte(osutil.NormalizedFilename(prefix)), true, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) Get(device protocol.DeviceID, file string) (protocol.FileInfo, bool) {
f, ok := s.db.getFileDirty([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(file)))
f, ok, err := s.db.getFileDirty([]byte(s.folder), device[:], []byte(osutil.NormalizedFilename(file)))
if backend.IsClosed(err) {
return protocol.FileInfo{}, false
} else if err != nil {
panic(err)
}
f.Name = osutil.NativeFilename(f.Name)
return f, ok
}
func (s *FileSet) GetGlobal(file string) (protocol.FileInfo, bool) {
fi, ok := s.db.getGlobalDirty([]byte(s.folder), []byte(osutil.NormalizedFilename(file)), false)
fi, ok, err := s.db.getGlobalDirty([]byte(s.folder), []byte(osutil.NormalizedFilename(file)), false)
if backend.IsClosed(err) {
return protocol.FileInfo{}, false
} else if err != nil {
panic(err)
}
if !ok {
return protocol.FileInfo{}, false
}
@@ -221,7 +277,12 @@ func (s *FileSet) GetGlobal(file string) (protocol.FileInfo, bool) {
}
func (s *FileSet) GetGlobalTruncated(file string) (FileInfoTruncated, bool) {
fi, ok := s.db.getGlobalDirty([]byte(s.folder), []byte(osutil.NormalizedFilename(file)), true)
fi, ok, err := s.db.getGlobalDirty([]byte(s.folder), []byte(osutil.NormalizedFilename(file)), true)
if backend.IsClosed(err) {
return FileInfoTruncated{}, false
} else if err != nil {
panic(err)
}
if !ok {
return FileInfoTruncated{}, false
}
@@ -231,7 +292,13 @@ func (s *FileSet) GetGlobalTruncated(file string) (FileInfoTruncated, bool) {
}
func (s *FileSet) Availability(file string) []protocol.DeviceID {
return s.db.availability([]byte(s.folder), []byte(osutil.NormalizedFilename(file)))
av, err := s.db.availability([]byte(s.folder), []byte(osutil.NormalizedFilename(file)))
if backend.IsClosed(err) {
return nil
} else if err != nil {
panic(err)
}
return av
}
func (s *FileSet) Sequence(device protocol.DeviceID) int64 {
@@ -255,11 +322,21 @@ func (s *FileSet) GlobalSize() Counts {
}
func (s *FileSet) IndexID(device protocol.DeviceID) protocol.IndexID {
id := s.db.getIndexID(device[:], []byte(s.folder))
id, err := s.db.getIndexID(device[:], []byte(s.folder))
if backend.IsClosed(err) {
return 0
} else if err != nil {
panic(err)
}
if id == 0 && device == protocol.LocalDeviceID {
// No index ID set yet. We create one now.
id = protocol.NewIndexID()
s.db.setIndexID(device[:], []byte(s.folder), id)
err := s.db.setIndexID(device[:], []byte(s.folder), id)
if backend.IsClosed(err) {
return 0
} else if err != nil {
panic(err)
}
}
return id
}
@@ -268,12 +345,19 @@ func (s *FileSet) SetIndexID(device protocol.DeviceID, id protocol.IndexID) {
if device == protocol.LocalDeviceID {
panic("do not explicitly set index ID for local device")
}
s.db.setIndexID(device[:], []byte(s.folder), id)
if err := s.db.setIndexID(device[:], []byte(s.folder), id); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
func (s *FileSet) MtimeFS() *fs.MtimeFS {
prefix := s.db.keyer.GenerateMtimesKey(nil, []byte(s.folder))
kv := NewNamespacedKV(s.db.Lowlevel, string(prefix))
prefix, err := s.db.keyer.GenerateMtimesKey(nil, []byte(s.folder))
if backend.IsClosed(err) {
return nil
} else if err != nil {
panic(err)
}
kv := NewNamespacedKV(s.db, string(prefix))
return fs.NewMtimeFS(s.fs, kv)
}
@@ -283,23 +367,39 @@ func (s *FileSet) ListDevices() []protocol.DeviceID {
// DropFolder clears out all information related to the given folder from the
// database.
func DropFolder(ll *Lowlevel, folder string) {
db := newInstance(ll)
db.dropFolder([]byte(folder))
db.dropMtimes([]byte(folder))
db.dropFolderMeta([]byte(folder))
// Also clean out the folder ID mapping.
db.folderIdx.Delete([]byte(folder))
func DropFolder(db *Lowlevel, folder string) {
droppers := []func([]byte) error{
db.dropFolder,
db.dropMtimes,
db.dropFolderMeta,
db.folderIdx.Delete,
}
for _, drop := range droppers {
if err := drop([]byte(folder)); backend.IsClosed(err) {
return
} else if err != nil {
panic(err)
}
}
}
// DropDeltaIndexIDs removes all delta index IDs from the database.
// This will cause a full index transmission on the next connection.
func DropDeltaIndexIDs(db *Lowlevel) {
dbi := db.NewIterator(util.BytesPrefix([]byte{KeyTypeIndexID}), nil)
dbi, err := db.NewPrefixIterator([]byte{KeyTypeIndexID})
if backend.IsClosed(err) {
return
} else if err != nil {
panic(err)
}
defer dbi.Release()
for dbi.Next() {
db.Delete(dbi.Key(), nil)
if err := db.Delete(dbi.Key()); err != nil && !backend.IsClosed(err) {
panic(err)
}
}
if err := dbi.Error(); err != nil && !backend.IsClosed(err) {
panic(err)
}
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/d4l3k/messagediff"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -117,7 +118,7 @@ func (l fileList) String() string {
}
func TestGlobalSet(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -332,7 +333,7 @@ func TestGlobalSet(t *testing.T) {
}
func TestNeedWithInvalid(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -369,7 +370,7 @@ func TestNeedWithInvalid(t *testing.T) {
}
func TestUpdateToInvalid(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -425,7 +426,7 @@ func TestUpdateToInvalid(t *testing.T) {
}
func TestInvalidAvailability(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -463,7 +464,7 @@ func TestInvalidAvailability(t *testing.T) {
}
func TestGlobalReset(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -501,7 +502,7 @@ func TestGlobalReset(t *testing.T) {
}
func TestNeed(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -539,7 +540,7 @@ func TestNeed(t *testing.T) {
}
func TestSequence(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -569,7 +570,7 @@ func TestSequence(t *testing.T) {
}
func TestListDropFolder(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s0 := db.NewFileSet("test0", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
local1 := []protocol.FileInfo{
@@ -619,7 +620,7 @@ func TestListDropFolder(t *testing.T) {
}
func TestGlobalNeedWithInvalid(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test1", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -660,7 +661,7 @@ func TestGlobalNeedWithInvalid(t *testing.T) {
}
func TestLongPath(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -671,7 +672,7 @@ func TestLongPath(t *testing.T) {
name := b.String() // 5000 characters
local := []protocol.FileInfo{
{Name: string(name), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
{Name: name, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
}
replace(s, protocol.LocalDeviceID, local)
@@ -686,39 +687,6 @@ func TestLongPath(t *testing.T) {
}
}
func TestCommitted(t *testing.T) {
// Verify that the Committed counter increases when we change things and
// doesn't increase when we don't.
ldb := db.OpenMemory()
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
local := []protocol.FileInfo{
{Name: string("file"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
}
// Adding a file should increase the counter
c0 := ldb.Committed()
replace(s, protocol.LocalDeviceID, local)
c1 := ldb.Committed()
if c1 <= c0 {
t.Errorf("committed data didn't increase; %d <= %d", c1, c0)
}
// Updating with something identical should not do anything
s.Update(protocol.LocalDeviceID, local)
c2 := ldb.Committed()
if c2 > c1 {
t.Errorf("replace with same contents should do nothing but %d > %d", c2, c1)
}
}
func BenchmarkUpdateOneFile(b *testing.B) {
local0 := fileList{
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
@@ -729,10 +697,11 @@ func BenchmarkUpdateOneFile(b *testing.B) {
protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)},
}
ldb, err := db.Open("testdata/benchmarkupdate.db", db.TuningAuto)
be, err := backend.Open("testdata/benchmarkupdate.db", backend.TuningAuto)
if err != nil {
b.Fatal(err)
}
ldb := db.NewLowlevel(be)
defer func() {
ldb.Close()
os.RemoveAll("testdata/benchmarkupdate.db")
@@ -751,7 +720,7 @@ func BenchmarkUpdateOneFile(b *testing.B) {
}
func TestIndexID(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -783,7 +752,7 @@ func TestIndexID(t *testing.T) {
}
func TestDropFiles(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -846,7 +815,7 @@ func TestDropFiles(t *testing.T) {
}
func TestIssue4701(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -887,7 +856,7 @@ func TestIssue4701(t *testing.T) {
}
func TestWithHaveSequence(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -915,14 +884,14 @@ func TestWithHaveSequence(t *testing.T) {
func TestStressWithHaveSequence(t *testing.T) {
// This races two loops against each other: one that contiously does
// updates, and one that continously does sequence walks. The test fails
// updates, and one that continuously does sequence walks. The test fails
// if the sequence walker sees a discontinuity.
if testing.Short() {
t.Skip("Takes a long time")
}
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -945,7 +914,7 @@ func TestStressWithHaveSequence(t *testing.T) {
close(done)
}()
var prevSeq int64 = 0
var prevSeq int64
loop:
for {
select {
@@ -964,7 +933,7 @@ loop:
}
func TestIssue4925(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -990,7 +959,7 @@ func TestIssue4925(t *testing.T) {
}
func TestMoveGlobalBack(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
file := "foo"
@@ -1054,7 +1023,7 @@ func TestMoveGlobalBack(t *testing.T) {
// needed files.
// https://github.com/syncthing/syncthing/issues/5007
func TestIssue5007(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
file := "foo"
@@ -1081,7 +1050,7 @@ func TestIssue5007(t *testing.T) {
// TestNeedDeleted checks that a file that doesn't exist locally isn't needed
// when the global file is deleted.
func TestNeedDeleted(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
file := "foo"
@@ -1115,7 +1084,7 @@ func TestNeedDeleted(t *testing.T) {
}
func TestReceiveOnlyAccounting(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -1219,7 +1188,7 @@ func TestReceiveOnlyAccounting(t *testing.T) {
}
func TestNeedAfterUnignore(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
folder := "test"
file := "foo"
@@ -1251,7 +1220,7 @@ func TestNeedAfterUnignore(t *testing.T) {
func TestRemoteInvalidNotAccounted(t *testing.T) {
// Remote files with the invalid bit should not count.
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
files := []protocol.FileInfo{
@@ -1270,7 +1239,7 @@ func TestRemoteInvalidNotAccounted(t *testing.T) {
}
func TestNeedWithNewerInvalid(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("default", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -1308,7 +1277,7 @@ func TestNeedWithNewerInvalid(t *testing.T) {
}
func TestNeedAfterDeviceRemove(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
file := "foo"
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
@@ -1335,7 +1304,7 @@ func TestNeedAfterDeviceRemove(t *testing.T) {
func TestCaseSensitive(t *testing.T) {
// Normal case sensitive lookup should work
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
local := []protocol.FileInfo{
@@ -1372,7 +1341,7 @@ func TestSequenceIndex(t *testing.T) {
// Set up a db and a few files that we will manipulate.
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
local := []protocol.FileInfo{
@@ -1463,7 +1432,7 @@ func TestSequenceIndex(t *testing.T) {
}
func TestIgnoreAfterReceiveOnly(t *testing.T) {
ldb := db.OpenMemory()
ldb := db.NewLowlevel(backend.OpenMemory())
file := "foo"
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)

View File

@@ -10,16 +10,15 @@ import (
"encoding/binary"
"sort"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
)
// A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
// fast lookups in both directions and persists to the database. Don't use for
// storing more items than fit comfortably in RAM.
type smallIndex struct {
db *leveldb.DB
db backend.Backend
prefix []byte
id2val map[uint32]string
val2id map[string]uint32
@@ -27,7 +26,7 @@ type smallIndex struct {
mut sync.Mutex
}
func newSmallIndex(db *leveldb.DB, prefix []byte) *smallIndex {
func newSmallIndex(db backend.Backend, prefix []byte) *smallIndex {
idx := &smallIndex{
db: db,
prefix: prefix,
@@ -42,7 +41,10 @@ func newSmallIndex(db *leveldb.DB, prefix []byte) *smallIndex {
// load iterates over the prefix space in the database and populates the in
// memory maps.
func (i *smallIndex) load() {
it := i.db.NewIterator(util.BytesPrefix(i.prefix), nil)
it, err := i.db.NewPrefixIterator(i.prefix)
if err != nil {
panic("loading small index: " + err.Error())
}
defer it.Release()
for it.Next() {
val := string(it.Value())
@@ -60,7 +62,7 @@ func (i *smallIndex) load() {
// ID returns the index number for the given byte slice, allocating a new one
// and persisting this to the database if necessary.
func (i *smallIndex) ID(val []byte) uint32 {
func (i *smallIndex) ID(val []byte) (uint32, error) {
i.mut.Lock()
// intentionally avoiding defer here as we want this call to be as fast as
// possible in the general case (folder ID already exists). The map lookup
@@ -69,7 +71,7 @@ func (i *smallIndex) ID(val []byte) uint32 {
// here.
if id, ok := i.val2id[string(val)]; ok {
i.mut.Unlock()
return id
return id, nil
}
id := i.nextID
@@ -82,10 +84,13 @@ func (i *smallIndex) ID(val []byte) uint32 {
key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id
copy(key, i.prefix)
binary.BigEndian.PutUint32(key[len(i.prefix):], id)
i.db.Put(key, val, nil)
if err := i.db.Put(key, val); err != nil {
i.mut.Unlock()
return 0, err
}
i.mut.Unlock()
return id
return id, nil
}
// Val returns the value for the given index number, or (nil, false) if there
@@ -101,7 +106,7 @@ func (i *smallIndex) Val(id uint32) ([]byte, bool) {
return []byte(val), true
}
func (i *smallIndex) Delete(val []byte) {
func (i *smallIndex) Delete(val []byte) error {
i.mut.Lock()
defer i.mut.Unlock()
@@ -115,7 +120,9 @@ func (i *smallIndex) Delete(val []byte) {
// Put an empty value into the database. This indicates that the
// entry does not exist any more and prevents the ID from being
// reused in the future.
i.db.Put(key, []byte{}, nil)
if err := i.db.Put(key, []byte{}); err != nil {
return err
}
// Delete reverse mapping.
delete(i.id2val, id)
@@ -123,6 +130,7 @@ func (i *smallIndex) Delete(val []byte) {
// Delete forward mapping.
delete(i.val2id, string(val))
return nil
}
// Values returns the set of values in the index

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